{ "name": "DAOHaus Alerts", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "530c914a-c732-4cb6-aaf1-2b722f9e7398", "options": {} }, "id": "def14e6f-a3fc-4844-8a94-b2d3a0a9bdb5", "name": "Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 1.1, "position": [ 660, 540 ], "webhookId": "530c914a-c732-4cb6-aaf1-2b722f9e7398" }, { "parameters": { "method": "POST", "url": "https://opt-mainnet.g.alchemy.com/v2/{alchemy_api_key}", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"id\": 1,\n \"jsonrpc\": \"2.0\",\n \"method\": \"eth_getTransactionReceipt\",\n \"params\": [\n\"{{ $json[\"body\"][\"event\"][\"activity\"][0][\"hash\"] }}\"\n ]\n}", "options": {} }, "id": "b97eaff4-26f2-4230-b3e2-3eb14b5cbbba", "name": "HTTP Request", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ 1160, 540 ] }, { "parameters": { "authentication": "webhook", "options": {}, "embeds": { "values": [ { "description": "={{ $json[\"message\"] }}\n\n{{ $json[\"proposalURL\"] }}", "author": "Australian Capital DAO", "color": "#FF00D9", "timestamp": "={{ ($now.minus({hours:10})).toFormat('yyyy-LL-dd HH:mm:ss') }}\n", "title": "New DAO TX", "url": "={{ $json[\"explorer\"] }}" } ] } }, "id": "8e49fb62-06a1-4e51-898c-c17e1eb5dae0", "name": "Discord2", "type": "n8n-nodes-base.discord", "typeVersion": 2, "position": [ 1960, 420 ], "credentials": { "discordWebhookApi": { "id": "XzOnTbxymIRJMNFK", "name": "N8N Channel" } } }, { "parameters": { "chatId": "-4260162868", "text": "={{ $json[\"message\"] }}", "replyMarkup": "inlineKeyboard", "inlineKeyboard": { "rows": [ { "row": { "buttons": [ { "text": "View tx on Etherscan", "additionalFields": { "url": "={{ $json[\"explorer\"] }}" } }, { "text": "View Proposal", "additionalFields": { "url": "={{ $json[\"proposalURL\"] }}" } } ] } } ] }, "additionalFields": { "appendAttribution": false } }, "id": "2577c299-f86e-46d2-8799-e4e10875e3c7", "name": "Telegram", "type": "n8n-nodes-base.telegram", "typeVersion": 1.1, "position": [ 1960, 640 ], "credentials": { "telegramApi": { "id": "VWQskb1y7DoFEnMh", "name": "Telegram account" } } }, { "parameters": { "updates": [ "message" ], "additionalFields": {} }, "id": "245d297b-8838-441e-b206-1f9cfdff8591", "name": "Telegram Trigger", "type": "n8n-nodes-base.telegramTrigger", "typeVersion": 1.1, "position": [ 480, 940 ], "webhookId": "47a00ca7-1eb5-408d-8846-4af7f8b1e36b", "credentials": { "telegramApi": { "id": "VWQskb1y7DoFEnMh", "name": "Telegram account" } } }, { "parameters": { "authentication": "webhook", "content": "=New message from: {{ $json[\"message\"][\"from\"][\"first_name\"] }} (username: {{ $json[\"message\"][\"from\"][\"username\"] }}) (ID: {{ $json[\"message\"][\"from\"][\"id\"] }})\nChat Title: {{ $json[\"message\"][\"chat\"][\"title\"] }}\nChat ID: {{ $json[\"message\"][\"chat\"][\"id\"] }}\nChat Type: {{ $json[\"message\"][\"chat\"][\"type\"] }}\nMessage text: {{ $json[\"message\"][\"text\"] }}", "options": {} }, "id": "ce165915-be3e-49f2-b9e8-308f98e37ffd", "name": "Discord3", "type": "n8n-nodes-base.discord", "typeVersion": 2, "position": [ 1560, 900 ], "credentials": { "discordWebhookApi": { "id": "XzOnTbxymIRJMNFK", "name": "N8N Channel" } } }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "ca511fdf-180b-4236-b474-3bfe439ded0c", "leftValue": "={{ $json[\"body\"][\"event\"][\"activity\"].length }}", "rightValue": 1, "operator": { "type": "number", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "9536c45d-ae2a-472d-87f5-2b4535150128", "name": "If1", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [ 880, 540 ] }, { "parameters": { "jsCode": "function hexToDecimal(hex) {\n return parseInt(hex, 16);\n}\nfunction stripExtraZeros(hexString) {\n // Remove leading zeros while keeping at least \"0x\" if present\n hexString = hexString.replace(/^0x0+/, '');\n return \"0x\"+hexString;\n}\n\n\n\nconst topics = {\n \"0xb9956173924f9c1204bae41dd3737d7ed1161846d13183879cdc03c4b9f8d019\":\"Submit Proposal\",\n \"0x786755545a7e27c12c90cc7f0934514d03fdacfe3684a340b8c4100531e7ecd5\":\"Submit Vote\",\n \"0xb4571f7e4e2c2b6e6185e47ab5caa5fe34087299bd49fbae945a4583101ee3f0\":\"Process Proposal\",\n \"0xd45ad122361f16d6f50d7c4a73638f20ee48eff6186af15224e2a4a1f6d50171\":\"Sponsor Proposal\"\n};\n\nvar Logs = $('HTTP Request').first().json.result.logs;\nvar topicsFromLogs = [];\n\nfor (var i = 0; i < Logs.length; i++) {\n var log = Logs[i];\n topicsFromLogs.push(...log.topics); // Using spread operator to append topics array\n}\n\nvar data = {\n \"method\":\"Unknown method\"\n};\n\nconst opts = {\n \"Submit Vote\":{\n 1:\"member\",\n 2:\"proposal\",\n 3:\"vote\"\n },\n \"Submit Proposal\":{\n 1:\"proposal\",\n 2:\"dataHash\"\n },\n \"Process Proposal\":{\n 1:\"member\",\n 2:\"proposal\",\n 3:\"vote\"\n },\n \"Sponsor Proposal\":{\n 1:\"member\",\n 2:\"proposal\",\n 3:\"votingStarts\"\n }\n}\n\n\n// Check each topic in the logs against the topics object\nfor (let i = 0; i < topicsFromLogs.length; i++) {\n const topic = topicsFromLogs[i];\n if (topics.hasOwnProperty(topic)) {\n data[\"method\"] = topics[topic];\n console.log('Matched Option:', data[\"method\"]);\n } else {\n if (opts.hasOwnProperty(data[\"method\"])){\n var keys = opts[data[\"method\"]];\n if (keys[i] != \"member\"){\n data[keys[i]] = hexToDecimal(topic); \n } else {\n data[keys[i]] = stripExtraZeros(topic);\n }\n }\n }\n}\nfunction hexToUtf8(hexStr) {\n return Buffer.from(hexStr, 'hex').toString('utf8');\n}\n\nif (data[\"method\"]== \"Submit Proposal\"){\n const hexData = $('HTTP Request').first().json.result.logs[0].data;\n // Extracting segments from hexData (assuming it's already defined)\n const votingPeriodHex = hexData.slice(2, 66); // 64 characters (32 bytes) for votingPeriod\n const proposalDataHex = hexData.slice(66, 130); // 64 characters (32 bytes) for proposalData\n const expirationHex = hexData.slice(130, 194); // 64 characters (32 bytes) for expiration\n const baalGasHex = hexData.slice(194, 258); // 64 characters (32 bytes) for baalGas\n const selfSponsorHex = hexData.slice(258, 322); // 64 characters (32 bytes) for selfSponsor\n const timestampHex = hexData.slice(322, 386); // 64 characters (32 bytes) for timestamp\n const detailsHex = hexData.slice(386); // Remaining part for details\n \n // Decode hex segments into decimal or string format\n const votingPeriod = hexToDecimal(votingPeriodHex);\n const proposalData = proposalDataHex;\n const expiration = hexToDecimal(expirationHex);\n const baalGas = hexToDecimal(baalGasHex);\n const selfSponsor = hexToDecimal(selfSponsorHex);\n const timestamp = hexToDecimal(timestampHex);\n \n let detailsObj;\n detailsObj = hexToUtf8(detailsHex);\n const jsonStringStart = detailsObj.indexOf('{\"');\n if (jsonStringStart !== -1) {\n detailsObj = detailsObj.slice(jsonStringStart);\n }\n\n \n // Construct the JSON object\n const jsonObject = {\n votingPeriod: votingPeriod,\n proposalData: proposalData,\n expiration: expiration,\n baalGas: baalGas,\n selfSponsor: selfSponsor,\n timestamp: timestamp,\n details: detailsObj\n };\n\n data['data'] = jsonObject;\n} else if (data[\"method\"]== \"Submit Vote\"){\n const hexData = $('HTTP Request').first().json.result.logs[0].data;\n data[\"votes\"]= hexToDecimal(hexData)/1000000000000000000;\n} else if (data[\"method\"]== \"Process Proposal\"){\n var logData = []\n for (var i = 0; i < Logs.length; i++) {\n if (Logs[i].topics[0]==\"0xb4571f7e4e2c2b6e6185e47ab5caa5fe34087299bd49fbae945a4583101ee3f0\"){\n data[\"proposal\"] = hexToDecimal(Logs[i].topics[1]);\n }\n}\n\n\n}\n\nconsole.log(data);\nreturn data;\n" }, "id": "eb7edb1c-a29d-4a55-9e69-3526edb2194a", "name": "Parser", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1360, 540 ] }, { "parameters": { "jsCode": "function cleanJsonString(jsonString) {\n // Remove invisible and non-printable characters (excluding newlines, carriage returns, and tabs)\n const cleanedString = jsonString.replace(/[^\\x20-\\x7E\\r\\n\\t]+/g, '');\n \n // Remove leading or trailing whitespace\n const trimmedString = cleanedString.trim();\n \n // Ensure the string is enclosed within curly braces if it’s a JSON object\n if (!trimmedString.startsWith('{') || !trimmedString.endsWith('}')) {\n throw new Error('Invalid JSON format: Missing opening or closing brace.');\n }\n\n return trimmedString;\n}\n\nconst addressNames = {\n \"0x6cb4b39bec23a921c9a20d061bf17d4640b0d39e\":\"woodburn.au\" \n};\nfunction addressName(address) {\n if (addressNames.hasOwnProperty(address)){\n return addressNames[address];\n }\n return address;\n}\n\n\n\nvar message = \"New DAO transaction: \";\nconst parsed = $('Parser').first().json;\nmessage += parsed.method;\n\nif (parsed.method == \"Submit Vote\"){\n message += \"\\n\"+ addressName(parsed.member) + \" voted \";\n if (parsed.vote == 1){\n message += \"for\";\n } else {\n message += \"against\";\n }\n message += \" proposal #\"+parsed.proposal;\n message += \" using \" + parsed.votes + \" votes\";\n}\nelse if (parsed.method == \"Sponsor Proposal\"){\n message += \"\\n\"+ addressName(parsed.member) + \" sponsored \";\n message += \"proposal #\"+parsed.proposal;\n}\nelse if (parsed.method == \"Submit Proposal\"){\n message += \"\\n\"+ addressName($('HTTP Request').first().json.result.from);\n message += \" created proposal #\" + parsed.proposal;\n try {\n const cleanedJsonString = cleanJsonString(parsed.data.details);\n const details = JSON.parse(cleanedJsonString);\n message += \"\\nProposal: \" + details.title;\n message += \"\\nDescription: \" + details.description;\n\n if (details.contentURI != \"\"){\n message += \"\\nURL: \" + details.contentURI;\n }\n }\n catch (error){\n message += \"\\nProposal details failed to parse\";\n message += error;\n }\n}\nelse if (parsed.method == \"Process Proposal\"){\n message += \"\\n\"+ addressName($('HTTP Request').first().json.result.from);\n message += \" executed proposal #\" + parsed.proposal;\n}\n\n\n// message += \"\\n\\nhttps://optimistic.etherscan.io/tx/\";\n// message += $('HTTP Request').first().json.result.transactionHash;\n// message += \"\\nhttps://admin.daohaus.club/#/molochV3/0xa/0xf4604948ad5365840803297bf81cd9a46c36fce7/proposal/\"\n// message += parsed.proposal;\n\nreturn {\n \"message\":message,\n \"explorer\":\"https://optimistic.etherscan.io/tx/\"+$('HTTP Request').first().json.result.transactionHash,\n\"proposalURL\":\"https://admin.daohaus.club/#/molochV3/0xa/0xf4604948ad5365840803297bf81cd9a46c36fce7/proposal/\"+parsed.proposal\n};\n\n" }, "id": "22c0ce02-9388-4dca-ac32-3fda22a4f280", "name": "Create message", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1580, 500 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "21456147-b1d5-4cb6-877d-ffe7c567eec6", "leftValue": "={{ $json[\"message\"][\"entities\"].length }}", "rightValue": 0, "operator": { "type": "number", "operation": "notEquals" } } ], "combinator": "and" }, "options": {} }, "id": "cc10be80-8d35-43d0-84a7-78440d3b7801", "name": "If", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [ 660, 1100 ] }, { "parameters": { "chatId": "={{ $json[\"message\"][\"chat\"][\"id\"] }}", "text": "Pong", "additionalFields": { "appendAttribution": false, "reply_to_message_id": "={{ $json[\"message\"][\"message_id\"] }}" } }, "id": "d6d67b9f-279f-4618-a2d4-14737449131f", "name": "Ping Pong", "type": "n8n-nodes-base.telegram", "typeVersion": 1.1, "position": [ 1280, 1000 ], "credentials": { "telegramApi": { "id": "VWQskb1y7DoFEnMh", "name": "Telegram account" } } }, { "parameters": { "chatId": "={{ $json[\"message\"][\"chat\"][\"id\"] }}", "text": "Sure here is the link to the Australian Capital DAO", "replyMarkup": "inlineKeyboard", "inlineKeyboard": { "rows": [ { "row": { "buttons": [ { "text": "Go to DAO Page", "additionalFields": { "url": "https://admin.daohaus.club/#/molochv3/0xa/0xf4604948ad5365840803297bf81cd9a46c36fce7" } } ] } } ] }, "additionalFields": { "appendAttribution": false, "reply_to_message_id": "={{ $json[\"message\"][\"message_id\"] }}" } }, "id": "402477d9-701c-4d08-80d9-608c4b702881", "name": "Dao Link", "type": "n8n-nodes-base.telegram", "typeVersion": 1.1, "position": [ 1280, 1160 ], "credentials": { "telegramApi": { "id": "VWQskb1y7DoFEnMh", "name": "Telegram account" } } }, { "parameters": { "rules": { "values": [ { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "leftValue": "={{ $json[\"message\"][\"text\"] }}", "rightValue": "/ping", "operator": { "type": "string", "operation": "startsWith" } } ], "combinator": "and" } }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "4c2f8464-cf52-467d-a219-e75793559353", "leftValue": "={{ $json[\"message\"][\"text\"] }}", "rightValue": "/dao", "operator": { "type": "string", "operation": "startsWith" } } ], "combinator": "and" } } ] }, "options": {} }, "id": "eb1090bf-5f39-4c8e-ac85-ed2e7b59e76e", "name": "Switch", "type": "n8n-nodes-base.switch", "typeVersion": 3, "position": [ 960, 1080 ] } ], "pinData": { "Webhook": [ { "json": { "headers": { "host": "n8n.woodburn.au", "x-forwarded-scheme": "https", "x-forwarded-proto": "https", "x-forwarded-for": "118.208.168.176", "x-real-ip": "118.208.168.176", "content-length": "845", "content-type": "application/json", "user-agent": "insomnia/9.3.1", "accept": "*/*" }, "params": {}, "query": {}, "body": { "webhookId": "wh_mvrhnhyqysvf70qj", "id": "whevt_rc0mq97pww9a7leo", "createdAt": "2024-07-18T05:46:57.824Z", "type": "ADDRESS_ACTIVITY", "event": { "network": "OPT_MAINNET", "activity": [ { "fromAddress": "0xf4604948ad5365840803297bf81cd9a46c36fce7", "toAddress": "0x35d3efffa0e6c63ee5c7e8c7dd7e7d6e961a6689", "blockNum": "0x752694c", "hash": "0x02d3f83b6663df1edc04db532190a17ef7a3267ca3104d7c36435eb2aad12b3f", "value": 0, "typeTraceAddress": "STATICCALL_0_0", "asset": "ETH", "category": "internal", "rawContract": { "rawValue": "0x0", "decimals": 18 } } ] } } } } ], "Telegram Trigger": [ { "json": { "update_id": 328606253, "message": { "message_id": 34, "from": { "id": 5166524939, "is_bot": false, "first_name": "Nathan.Woodburn/", "username": "nathanwoodburn", "language_code": "en" }, "chat": { "id": -4260162868, "title": "Test Bot Group", "type": "group", "all_members_are_administrators": true }, "date": 1721355038, "text": "/dao@australian_capital_dao_bot", "entities": [ { "offset": 0, "length": 31, "type": "bot_command" } ] } } } ] }, "connections": { "Webhook": { "main": [ [ { "node": "If1", "type": "main", "index": 0 } ] ] }, "HTTP Request": { "main": [ [ { "node": "Parser", "type": "main", "index": 0 } ] ] }, "Telegram Trigger": { "main": [ [ { "node": "Discord3", "type": "main", "index": 0 }, { "node": "If", "type": "main", "index": 0 } ] ] }, "If1": { "main": [ [ { "node": "HTTP Request", "type": "main", "index": 0 } ] ] }, "Parser": { "main": [ [ { "node": "Create message", "type": "main", "index": 0 } ] ] }, "Create message": { "main": [ [ { "node": "Discord2", "type": "main", "index": 0 }, { "node": "Telegram", "type": "main", "index": 0 } ] ] }, "If": { "main": [ [ { "node": "Switch", "type": "main", "index": 0 } ] ] }, "Switch": { "main": [ [ { "node": "Ping Pong", "type": "main", "index": 0 } ], [ { "node": "Dao Link", "type": "main", "index": 0 } ] ] } }, "active": true, "settings": { "executionOrder": "v1" }, "versionId": "4d31c171-d9e4-47a8-8a76-4a56b6fd3101", "meta": { "templateCredsSetupCompleted": true, "instanceId": "716fbd4310435337bb8df7c59b7984bfb867d59d9129682cbc4d8523a713d40e" }, "id": "POAYZQJI8PYGlKWt", "tags": [] }