diff --git a/lido-actions/actions/.gitignore b/lido-actions/actions/.gitignore new file mode 100644 index 0000000..2f5b2a0 --- /dev/null +++ b/lido-actions/actions/.gitignore @@ -0,0 +1,5 @@ +# Dependency directories +node_modules/ + +# Ignore tsc output +out/**/* diff --git a/lido-actions/actions/lidoEvents.ts b/lido-actions/actions/lidoEvents.ts new file mode 100644 index 0000000..f896f27 --- /dev/null +++ b/lido-actions/actions/lidoEvents.ts @@ -0,0 +1,79 @@ +import { + Context, + TransactionEvent +} from '@tenderly/actions'; + +import axios from 'axios'; + +const subscribeLidoImportantEventsFn = async (context: Context, transactionEvent: TransactionEvent) => { + + const txHash = transactionEvent.hash; + const network = transactionEvent.network; + + const bearerToken = await context.secrets.get('BEARER'); + const botToken = await context.secrets.get('BOT-TOKEN'); + const channelId = await context.secrets.get('CHANNEL-ID'); + + const url = `https://api.tenderly.co/api/v1/public-contract/${network}/trace/${txHash}`; + + const tdlyTx = `https://www.tdly.co/tx/${network}/${txHash}`; + + try { + const response = await axios.get(url, { + headers: { + 'authorization': `${bearerToken}` + } + }); + + const result = response.data; + + const validatorExitRequestLog = result.logs.find((log: any) => log.name === 'ValidatorExitRequest'); + + if (validatorExitRequestLog) { + const stakingModuleId = validatorExitRequestLog.inputs.find((input: any) => input.soltype.name === 'stakingModuleId')?.value; + const nodeOperatorId = validatorExitRequestLog.inputs.find((input: any) => input.soltype.name === 'nodeOperatorId')?.value; + const timestamp = validatorExitRequestLog.inputs.find((input: any) => input.soltype.name === 'timestamp')?.value; + const validatorPubkey = validatorExitRequestLog.inputs.find((input: any) => input.soltype.name === 'validatorPubkey')?.value; + + const rawTimestamp = Number(timestamp); + const convertedTime = new Date(rawTimestamp * 1000); // Convert to milliseconds + + if (stakingModuleId == 1 && nodeOperatorId == 14) { + console.log(`Condition met: stakingModuleId: ${stakingModuleId}, nodeOperatorId: ${nodeOperatorId}, validatorPubkey: ${validatorPubkey}, timestamp: ${convertedTime.toUTCString()}`); + + const message = `*Condition met* + +*stakingModuleId*: ${stakingModuleId} +*nodeOperatorId*: ${nodeOperatorId} +*validatorPubkey*: ${validatorPubkey} +*timestamp*: ${convertedTime.toUTCString()} +*transaction*: [txHash](${tdlyTx}) +`; + + const telegramApiUrl = `https://api.telegram.org/bot${botToken}/sendMessage`; + const payload = { + chat_id: channelId, + text: message, + parse_mode: "markdown", + }; + + try { + await axios.post(telegramApiUrl, payload); + console.log('Message sent successfully.'); + } catch (error) { + console.error('Error sending message:', error); + } + + } else { + console.log(`Condition not met: stakingModuleId: ${stakingModuleId}, nodeOperatorId: ${nodeOperatorId}, validatorPubkey: ${validatorPubkey}`); + } + } else { + console.log('No ValidatorExitRequest log found'); + } + + } catch (error) { + console.error('Error:', error); + } +} + +module.exports = {subscribeLidoImportantEventsFn}; diff --git a/lido-actions/actions/package-lock.json b/lido-actions/actions/package-lock.json new file mode 100644 index 0000000..25cb33a --- /dev/null +++ b/lido-actions/actions/package-lock.json @@ -0,0 +1,151 @@ +{ + "name": "actions", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "actions", + "dependencies": { + "@tenderly/actions": "^0.2.16", + "@types/node": "^20.8.10", + "axios": "^1.6.0" + }, + "devDependencies": { + "typescript": "^4.9.5" + } + }, + "node_modules/@tenderly/actions": { + "version": "0.2.65", + "resolved": "https://registry.npmjs.org/@tenderly/actions/-/actions-0.2.65.tgz", + "integrity": "sha512-ARkCvY1BwLSwnrgKcAFPjEJn4JktvSuRzn0NwZLPkkMfNqTCTLH0MML5xjgR7m0ADAVJpWdwjnSZ4LG7h0mvxg==" + }, + "node_modules/@types/node": { + "version": "20.16.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz", + "integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + } + } +} diff --git a/lido-actions/actions/package.json b/lido-actions/actions/package.json new file mode 100644 index 0000000..6576386 --- /dev/null +++ b/lido-actions/actions/package.json @@ -0,0 +1,14 @@ +{ + "name": "actions", + "scripts": { + "build": "node_modules/.bin/tsc" + }, + "devDependencies": { + "typescript": "^4.9.5" + }, + "dependencies": { + "@tenderly/actions": "^0.2.16", + "@types/node": "^20.8.10", + "axios": "^1.6.0" + } +} diff --git a/lido-actions/actions/tsconfig.json b/lido-actions/actions/tsconfig.json new file mode 100644 index 0000000..3dd096e --- /dev/null +++ b/lido-actions/actions/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "noUnusedLocals": true, + "outDir": "out", + "rootDir": "", + "sourceMap": true, + "strict": true, + "target": "es2020" + }, + "exclude": [ + "**/*.spec.ts" + ], + "include": [ + "**/*" + ] +} \ No newline at end of file diff --git a/lido-actions/tenderly.yaml b/lido-actions/tenderly.yaml new file mode 100644 index 0000000..8bbb807 --- /dev/null +++ b/lido-actions/tenderly.yaml @@ -0,0 +1,22 @@ +account_id: "" +actions: + account_slug/project_slug: + runtime: v2 + sources: actions + specs: + action_name: + description: Get a notification when condition matches on Lido tx + function: lidoEvents:subscribeLidoImportantEventsFn + execution_type: parallel + trigger: + type: transaction + transaction: + status: + - mined + filters: + - network: 1 + eventEmitted: + contract: + address: 0x0de4ea0184c2ad0baca7183356aea5b8d5bf5c6e + name: ValidatorExitRequest +project_slug: ""