Skip to content

Commit

Permalink
Merge pull request #153 from gnosis/manboy-eth/issue149
Browse files Browse the repository at this point in the history
[Reality Module] Add monitoring for answers submitted for a proposal question at Reality.eth
  • Loading branch information
auryn-macmillan authored Oct 28, 2022
2 parents 5e2174d + 8d1a5db commit 7155ec0
Show file tree
Hide file tree
Showing 9 changed files with 433 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ interface NotificationChannels {
interface RequestType extends MonitoringCredentials {
network: string
realityModuleAddress: string
oracleAddress: string
notificationChannels: NotificationChannels[]
}

export const setUpMonitoring = async (
network: string,
realityModuleAddress: string,
oracleAddress: string,
data: MonitoringSectionData,
) => {
console.log()
Expand Down Expand Up @@ -84,6 +86,7 @@ export const setUpMonitoring = async (
apiKey: data.apiKey,
apiSecret: data.secretKey,
network,
oracleAddress,
realityModuleAddress,
notificationChannels,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export const setup = async (
await setUpMonitoring(
NETWORKS[safeInfo.chainId as NETWORK].name,
realityModuleAddress,
setupData.oracle.instanceData.instanceAddress,
setupData.monitoring,
).catch((e) => {
statusCallback("Error when setting up monitoring.", e)
Expand Down
108 changes: 48 additions & 60 deletions packages/backend/api/monitoring/notification.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import type { VercelRequest, VercelResponse } from "@vercel/node"
import {
CreateSentinelRequest,
setupSentinelClient,
NotificationType,
SentinelClient,
} from "defender-sentinel-client"
import { Network, Body } from "../../lib/types"
setupNewNotificationChannel,
createSentinel,
createAutotask,
setupAutotaskClient,
} from "../../lib/defender"
import { Network } from "../../lib/types"

export interface Body {
apiKey: string
apiSecret: string
network: Network
realityModuleAddress: string
oracleAddress: string
notificationChannels: [
{
channel: NotificationType
config: any
},
]
}

export default async (request: VercelRequest, response: VercelResponse) => {
response.setHeader("Access-Control-Allow-Origin", "*")
Expand All @@ -20,31 +37,41 @@ export default async (request: VercelRequest, response: VercelResponse) => {
console.log("Incoming request at ", request.url)
console.log("Request body", request.body)
const body = request.body as Body
const { apiKey, apiSecret, notificationChannels, realityModuleAddress, network } =
body
const client = new SentinelClient({ apiKey, apiSecret })
const {
apiKey,
apiSecret,
notificationChannels,
oracleAddress,
realityModuleAddress,
network,
} = body
const sentinelClient = setupSentinelClient({ apiKey, apiSecret })
console.log("Client is ready")

const notificationChannelIds = await Promise.all(
notificationChannels.map(async ({ channel, config }) => {
const notificationChannelSetupResponds = await setupNewNotificationChannel(
client,
channel,
config,
)
console.log(
"Notification channel set up responds",
notificationChannelSetupResponds,
)
const { notificationId } = notificationChannelSetupResponds
return notificationId
}),
notificationChannels.map(
async ({ channel, config }) =>
await setupNewNotificationChannel(sentinelClient, channel, config),
),
)

const autotaskClient = setupAutotaskClient({ apiKey, apiSecret })

const autotaskId = await createAutotask(
autotaskClient,
oracleAddress,
notificationChannelIds,
network,
apiKey,
apiSecret,
)

const sentinelCreationResponds = await createSentinel(
client,
sentinelClient,
notificationChannelIds,
network,
realityModuleAddress,
autotaskId,
)
console.log("Sentinel creation responds", sentinelCreationResponds)
return response
Expand All @@ -66,42 +93,3 @@ export default async (request: VercelRequest, response: VercelResponse) => {
})
}
}

const setupNewNotificationChannel = (
client: SentinelClient,
channel: NotificationType,
config: any,
) =>
client.createNotificationChannel({
type: channel,
name: `ZodiacRealityModuleNotification-${channel}`,
config,
paused: false,
})

const createSentinel = (
client: SentinelClient,
notificationChannels: string[],
network: Network,
realityModuleAddress: string,
) => {
const requestParameters: CreateSentinelRequest = {
type: "BLOCK",
network: network,
// optional
name: "New proposal added to the Reality Module",
addresses: [realityModuleAddress],
// optional
paused: false,
// optional
eventConditions: [
{
eventSignature: "ProposalQuestionCreated(bytes32, string)",
},
],
// optional
notificationChannels: notificationChannels,
}

return client.create(requestParameters)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const { SentinelClient } = require("defender-sentinel-client")

const PROPOSAL_QUESTION_CREATED_EVENT_TOPIC =
"0xa1f5047031a658827550a2c4be07648493f3ac88a09c857b3961d1336429a31f" // Keccak-256("ProposalQuestionCreated(bytes32,string)")

// @param event: AutotaskEvent
exports.handler = async function (event) {
// variable from the event
const evmEvent = event.request.body.transaction.logs.find(
(log) => log.topics[0] === PROPOSAL_QUESTION_CREATED_EVENT_TOPIC,
)
const questionId = evmEvent.topics[1]
console.log("QuestionId to monitor for:", questionId)
const txHash = event.request.body.transaction.transactionHash
const txFrom = event.request.body.transaction.from

// variables from autotask creation
const network = "{{network}}"
const oracleAddress = "{{oracleAddress}}"
const notificationChannels = "{{notificationChannels}}"
const apiKey = "{{apiKey}}"
const apiSecret = "{{apiSecret}}"

const client = new SentinelClient({
apiKey,
apiSecret,
})

const requestParameters = {
type: "BLOCK",
network,
name: `Answer submitted for a proposal question at Reality.eth for the Reality Module (on ${network})`,
addresses: [oracleAddress],
paused: false,
abi: `[{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes32",
"name": "answer",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "question_id",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "history_hash",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "user",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "bond",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ts",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "is_commitment",
"type": "bool"
}
],
"name": "LogNewAnswer",
"type": "event"
}]`,
eventConditions: [
{
eventSignature:
"LogNewAnswer(bytes32,bytes32,bytes32,address,uint256,uint256,bool)",
expression: `$1 == "${questionId}"`,
},
],
notificationChannels: notificationChannels,
}

return client.create(requestParameters)
}
117 changes: 117 additions & 0 deletions packages/backend/lib/defender/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import {
CreateSentinelRequest,
NotificationType,
SentinelClient,
} from "defender-sentinel-client"
import { AutotaskClient } from "defender-autotask-client"
import { CreateAutotaskRequest } from "defender-autotask-client"
import { Network } from "../../lib/types"
import { packageCode, readFileAndReplace } from "../util"

export { NotificationType } from "defender-sentinel-client"

export const setupSentinelClient = ({ apiKey, apiSecret }) =>
new SentinelClient({ apiKey, apiSecret })

export const setupAutotaskClient = ({ apiKey, apiSecret }) =>
new AutotaskClient({ apiKey, apiSecret })

export const setupNewNotificationChannel = async (
client: SentinelClient,
channel: NotificationType,
config: any,
) => {
const notificationChannel = await client.createNotificationChannel({
type: channel,
name: `ZodiacRealityModuleNotification-${channel}`,
config,
paused: false,
})
console.log(
"Created Notification Channel with ID: ",
notificationChannel.notificationId,
)

return notificationChannel.notificationId
}

export const createSentinel = async (
client: SentinelClient,
notificationChannels: string[],
network: Network,
realityModuleAddress: string,
autotaskId: string,
) => {
const requestParameters: CreateSentinelRequest = {
type: "BLOCK",
network,
name: `Proposal added to the Reality Module (${realityModuleAddress} on ${network})`,
addresses: [realityModuleAddress],
paused: false,
abi: `[{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "questionId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "string",
"name": "proposalId",
"type": "string"
}
],
"name": "ProposalQuestionCreated",
"type": "event"
}]`,
eventConditions: [
{
eventSignature: "ProposalQuestionCreated(bytes32,string)",
},
],
autotaskTrigger: autotaskId,
notificationChannels: notificationChannels,
}

const sentinel = await client.create(requestParameters)
console.log("Created Sentinel with subscriber ID: ", sentinel.subscriberId)

return sentinel.subscriberId
}

export const createAutotask = async (
client: AutotaskClient,
oracleAddress: string,
notificationChannels: string[],
network: string,
apiKey: string,
apiSecret: string,
) => {
const code = readFileAndReplace(
"lib/defender/autotasks/on_new_question_from_module.js",
{
"{{network}}": network,
"{{oracleAddress}}": oracleAddress,
'"{{notificationChannels}}"': JSON.stringify(notificationChannels),
"{{apiKey}}": apiKey,
"{{apiSecret}}": apiSecret,
},
)

const params: CreateAutotaskRequest = {
name: "Setup Sentinel for new Reality.eth question",
encodedZippedCode: await packageCode(code),
trigger: {
type: "webhook",
},
paused: false,
}

const createdAutotask = await client.create(params)
console.log("Created Autotask with ID: ", createdAutotask.autotaskId)

return createdAutotask.autotaskId
}
Loading

0 comments on commit 7155ec0

Please sign in to comment.