diff --git a/.env.example b/.env.example index 6604724..6d7ca3a 100644 --- a/.env.example +++ b/.env.example @@ -26,4 +26,7 @@ BLOCK_NUMBER= # Slack SLACK_WEBHOOK_URL= -NOTIFICATIONS_ENABLED=false \ No newline at end of file +NOTIFICATIONS_ENABLED=false + +# Sentry +SENTRY_DSN= \ No newline at end of file diff --git a/README.md b/README.md index 2277570..aebf49c 100644 --- a/README.md +++ b/README.md @@ -208,11 +208,12 @@ tenderly actions deploy Make sure you configure the secrets in Tenderly: -- `SLACK_WEBHOOK_URL`: Slack Webhook -- `NOTIFICATIONS_ENABLED`: (default `true`) Set to `false` to disable Slack notifications - `NODE_URL_${network}`: RPC Node URL - `NODE_USER_${network}`: (optional) RPC Node user name for basic auth. - `NODE_PASSWORD_${network}`: (optional) RPC Node password name for basic auth. +- `NOTIFICATIONS_ENABLED`: (default `true`) Set to `false` to disable Slack notifications +- `SLACK_WEBHOOK_URL`: Slack Webhook (required only if notifications are enabled) +- `SENTRY_DSN`: (optional) Sentry DSN code. If present, it will enable Sentry notifications ## Developers diff --git a/actions/package-lock.json b/actions/package-lock.json index d054f4a..22410c9 100644 --- a/actions/package-lock.json +++ b/actions/package-lock.json @@ -8,6 +8,8 @@ "license": "MIT", "dependencies": { "@cowprotocol/contracts": "^1.4.0", + "@sentry/integrations": "^7.64.0", + "@sentry/node": "^7.64.0", "@tenderly/actions": "^0.0.13", "axios": "^1.4.0", "ethers": "^5.7.2", @@ -768,6 +770,85 @@ } } }, + "node_modules/@sentry-internal/tracing": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.64.0.tgz", + "integrity": "sha512-1XE8W6ki7hHyBvX9hfirnGkKDBKNq3bDJyXS86E0bYVDl94nvbRM9BD9DHsCFetqYkVm1yDGEK+6aUVs4CztoQ==", + "dependencies": { + "@sentry/core": "7.64.0", + "@sentry/types": "7.64.0", + "@sentry/utils": "7.64.0", + "tslib": "^2.4.1 || ^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/core": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.64.0.tgz", + "integrity": "sha512-IzmEyl5sNG7NyEFiyFHEHC+sizsZp9MEw1+RJRLX6U5RITvcsEgcajSkHQFafaBPzRrcxZMdm47Cwhl212LXcw==", + "dependencies": { + "@sentry/types": "7.64.0", + "@sentry/utils": "7.64.0", + "tslib": "^2.4.1 || ^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/integrations": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.64.0.tgz", + "integrity": "sha512-6gbSGiruOifAmLtXw//Za19GWiL5qugDMEFxSvc5WrBWb+A8UK+foPn3K495OcivLS68AmqAQCUGb+6nlVowwA==", + "dependencies": { + "@sentry/types": "7.64.0", + "@sentry/utils": "7.64.0", + "localforage": "^1.8.1", + "tslib": "^2.4.1 || ^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/node": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.64.0.tgz", + "integrity": "sha512-wRi0uTnp1WSa83X2yLD49tV9QPzGh5e42IKdIDBiQ7lV9JhLILlyb34BZY1pq6p4dp35yDasDrP3C7ubn7wo6A==", + "dependencies": { + "@sentry-internal/tracing": "7.64.0", + "@sentry/core": "7.64.0", + "@sentry/types": "7.64.0", + "@sentry/utils": "7.64.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^2.4.1 || ^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/types": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.64.0.tgz", + "integrity": "sha512-LqjQprWXjUFRmzIlUjyA+KL+38elgIYmAeoDrdyNVh8MK5IC1W2Lh1Q87b4yOiZeMiIhIVNBd7Ecoh2rodGrGA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.64.0.tgz", + "integrity": "sha512-HRlM1INzK66Gt+F4vCItiwGKAng4gqzCR4C5marsL3qv6SrKH98dQnCGYgXluSWaaa56h97FRQu7TxCk6jkSvQ==", + "dependencies": { + "@sentry/types": "7.64.0", + "tslib": "^2.4.1 || ^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@tenderly/actions": { "version": "0.0.13", "resolved": "https://registry.npmjs.org/@tenderly/actions/-/actions-0.0.13.tgz", @@ -915,6 +996,17 @@ "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1231,6 +1323,14 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1266,7 +1366,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -1714,6 +1813,23 @@ "npm": ">=1.3.7" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1791,6 +1907,22 @@ "node": ">=0.6.0" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -1805,6 +1937,11 @@ "dev": true, "license": "MIT" }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1873,7 +2010,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, "license": "MIT" }, "node_modules/next-tick": { @@ -2233,6 +2369,11 @@ } } }, + "node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2827,6 +2968,67 @@ "uuid": "^7.0.3" } }, + "@sentry-internal/tracing": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.64.0.tgz", + "integrity": "sha512-1XE8W6ki7hHyBvX9hfirnGkKDBKNq3bDJyXS86E0bYVDl94nvbRM9BD9DHsCFetqYkVm1yDGEK+6aUVs4CztoQ==", + "requires": { + "@sentry/core": "7.64.0", + "@sentry/types": "7.64.0", + "@sentry/utils": "7.64.0", + "tslib": "^2.4.1 || ^1.9.3" + } + }, + "@sentry/core": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.64.0.tgz", + "integrity": "sha512-IzmEyl5sNG7NyEFiyFHEHC+sizsZp9MEw1+RJRLX6U5RITvcsEgcajSkHQFafaBPzRrcxZMdm47Cwhl212LXcw==", + "requires": { + "@sentry/types": "7.64.0", + "@sentry/utils": "7.64.0", + "tslib": "^2.4.1 || ^1.9.3" + } + }, + "@sentry/integrations": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.64.0.tgz", + "integrity": "sha512-6gbSGiruOifAmLtXw//Za19GWiL5qugDMEFxSvc5WrBWb+A8UK+foPn3K495OcivLS68AmqAQCUGb+6nlVowwA==", + "requires": { + "@sentry/types": "7.64.0", + "@sentry/utils": "7.64.0", + "localforage": "^1.8.1", + "tslib": "^2.4.1 || ^1.9.3" + } + }, + "@sentry/node": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.64.0.tgz", + "integrity": "sha512-wRi0uTnp1WSa83X2yLD49tV9QPzGh5e42IKdIDBiQ7lV9JhLILlyb34BZY1pq6p4dp35yDasDrP3C7ubn7wo6A==", + "requires": { + "@sentry-internal/tracing": "7.64.0", + "@sentry/core": "7.64.0", + "@sentry/types": "7.64.0", + "@sentry/utils": "7.64.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^2.4.1 || ^1.9.3" + } + }, + "@sentry/types": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.64.0.tgz", + "integrity": "sha512-LqjQprWXjUFRmzIlUjyA+KL+38elgIYmAeoDrdyNVh8MK5IC1W2Lh1Q87b4yOiZeMiIhIVNBd7Ecoh2rodGrGA==" + }, + "@sentry/utils": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.64.0.tgz", + "integrity": "sha512-HRlM1INzK66Gt+F4vCItiwGKAng4gqzCR4C5marsL3qv6SrKH98dQnCGYgXluSWaaa56h97FRQu7TxCk6jkSvQ==", + "requires": { + "@sentry/types": "7.64.0", + "tslib": "^2.4.1 || ^1.9.3" + } + }, "@tenderly/actions": { "version": "0.0.13", "resolved": "https://registry.npmjs.org/@tenderly/actions/-/actions-0.0.13.tgz", @@ -2950,6 +3152,14 @@ "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3193,6 +3403,11 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -3224,7 +3439,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -3580,6 +3794,20 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3650,6 +3878,22 @@ "verror": "1.10.0" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "requires": { + "immediate": "~3.0.5" + } + }, + "localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "requires": { + "lie": "3.1.1" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -3662,6 +3906,11 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, + "lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -3709,8 +3958,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "next-tick": { "version": "0.2.2", @@ -3954,6 +4202,11 @@ "yn": "3.1.1" } }, + "tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/actions/package.json b/actions/package.json index fc9feea..102c8eb 100644 --- a/actions/package.json +++ b/actions/package.json @@ -18,6 +18,8 @@ }, "dependencies": { "@cowprotocol/contracts": "^1.4.0", + "@sentry/integrations": "^7.64.0", + "@sentry/node": "^7.64.0", "@tenderly/actions": "^0.0.13", "axios": "^1.4.0", "ethers": "^5.7.2", diff --git a/actions/register.ts b/actions/register.ts index 43998a1..9e8d89a 100644 --- a/actions/register.ts +++ b/actions/register.ts @@ -38,7 +38,11 @@ const _addContract: ActionFn = async (context: Context, event: Event) => { const transactionEvent = event as TransactionEvent; const tx = transactionEvent.hash; const composableCow = ComposableCoW__factory.createInterface(); - const { registry } = await init(transactionEvent.network, context); + const { registry } = await init( + "addContract", + transactionEvent.network, + context + ); // Process the logs let hasErrors = false; @@ -141,10 +145,12 @@ export const add = async ( composableCow: string, registry: Registry ) => { + const { handler, salt, staticInput } = params; if (registry.ownerOrders.has(owner)) { const conditionalOrders = registry.ownerOrders.get(owner); console.log( - `[register:add] Adding conditional order ${params} to already existing contract ${owner}. Tx: ${tx}` + `[register:add] Adding conditional order to already existing owner contract ${owner}`, + { tx, handler, salt, staticInput } ); let exists: boolean = false; // Iterate over the conditionalOrders to make sure that the params are not already in the registry @@ -168,7 +174,8 @@ export const add = async ( } } else { console.log( - `[register:add] Adding conditional order ${params} to new contract ${owner} . Tx: ${tx}` + `[register:add] Adding conditional order to new owner contract ${owner}:`, + { tx, handler, salt, staticInput } ); registry.ownerOrders.set( owner, diff --git a/actions/test/run_local.ts b/actions/test/run_local.ts index 2f8adfd..9ff3fbe 100644 --- a/actions/test/run_local.ts +++ b/actions/test/run_local.ts @@ -25,6 +25,7 @@ const main = async () => { `NODE_PASSWORD_${network}`, "SLACK_WEBHOOK_URL", "NOTIFICATIONS_ENABLED", + "SENTRY_DSN", ]; for (const name of envNames) { const envValue = process.env[name]; diff --git a/actions/utils.ts b/actions/utils.ts index ade5095..d71850a 100644 --- a/actions/utils.ts +++ b/actions/utils.ts @@ -4,47 +4,54 @@ import assert = require("assert"); import { ethers } from "ethers"; import { ConnectionInfo, Logger } from "ethers/lib/utils"; import { OrderStatus, Registry } from "./register"; + import Slack = require("node-slack"); +import { + init as sentryInit, + startTransaction as sentryStartTransaction, + Transaction as SentryTransaction, +} from "@sentry/node"; +import { CaptureConsole as CaptureConsoleIntegration } from "@sentry/integrations"; + +// const Sentry = require("@sentry/node"); const TENDERLY_LOG_LIMIT = 3800; // 4000 is the limit, we just leave some margin for printing the chunk index const NOTIFICATION_WAIT_PERIOD = 1000 * 60 * 60 * 2; // 2h - Don't send more than one notification every 2h + interface ExecutionContext { registry: Registry; - slack: Slack; notificationsEnabled: boolean; + slack?: Slack; + sentryTransaction?: SentryTransaction; context: Context; } let executionContext: ExecutionContext | undefined; export async function init( + transactionName: string, network: string, context: Context ): Promise { - // Get slack config - const webhookUrl = await context.secrets - .get("SLACK_WEBHOOK_URL") - .catch(() => ""); - // Init registry const registry = await Registry.load(context, network); // Get notifications config (enabled by default) - const notificationsEnabled = await context.secrets - .get("NOTIFICATIONS_ENABLED") - .then((value) => (value ? value !== "false" : true)) - .catch(() => true); + const notificationsEnabled = await _getNotificationsEnabled(context); - if (notificationsEnabled && !webhookUrl) { - throw new Error( - "SLACK_WEBHOOK_URL secret is required when NOTIFICATIONS_ENABLED is true" - ); + // Init slack + const slack = await _getSlack(notificationsEnabled, context); + + // Init Sentry + const sentryTransaction = await _getSentry(transactionName, network, context); + if (!sentryTransaction) { + console.warn("SENTRY_DSN secret is not set. Sentry will be disabled"); } - const slack = new Slack(webhookUrl); executionContext = { registry, slack, + sentryTransaction, notificationsEnabled, context, }; @@ -52,8 +59,70 @@ export async function init( return executionContext; } -export function getExecutionContext(): ExecutionContext | undefined { - return executionContext; +async function _getNotificationsEnabled(context: Context): Promise { + // Get notifications config (enabled by default) + return context.secrets + .get("NOTIFICATIONS_ENABLED") + .then((value) => (value ? value !== "false" : true)) + .catch(() => true); +} + +async function _getSlack( + notificationsEnabled: boolean, + context: Context +): Promise { + if (executionContext) { + return executionContext?.slack; + } + + // Init slack + let slack; + const webhookUrl = await context.secrets + .get("SLACK_WEBHOOK_URL") + .catch(() => ""); + if (!notificationsEnabled) { + return undefined; + } + + if (!webhookUrl) { + throw new Error( + "SLACK_WEBHOOK_URL secret is required when NOTIFICATIONS_ENABLED is true" + ); + } + + return new Slack(webhookUrl); +} + +async function _getSentry( + transactionName: string, + network: string, + context: Context +): Promise { + // Init Sentry + if (!executionContext) { + const sentryDsn = await context.secrets.get("SENTRY_DSN").catch(() => ""); + sentryInit({ + dsn: sentryDsn, + debug: false, + tracesSampleRate: 1.0, // Capture 100% of the transactions. Consider reducing in production. + integrations: [ + new CaptureConsoleIntegration({ + levels: ["error", "warn", "log", "info"], + }), + ], + initialScope: { + tags: { + network, + }, + }, + }); + } + + // Return transaction + return sentryStartTransaction({ + name: transactionName, + op: "action", + }); } async function getSecret(key: string, context: Context): Promise { @@ -243,7 +312,7 @@ export function sendSlack(message: string): boolean { const { slack, registry, notificationsEnabled } = executionContext; // Do not notify IF notifications are disabled - if (!notificationsEnabled) { + if (!notificationsEnabled || !slack) { return false; } diff --git a/actions/watch.ts b/actions/watch.ts index ba04ed9..2b86f07 100644 --- a/actions/watch.ts +++ b/actions/watch.ts @@ -57,7 +57,11 @@ const _checkForSettlement: ActionFn = async ( const transactionEvent = event as TransactionEvent; const settlement = GPv2Settlement__factory.createInterface(); - const { registry } = await init(transactionEvent.network, context); + const { registry } = await init( + "checkForSettlement", + transactionEvent.network, + context + ); let hasErrors = false; transactionEvent.logs.forEach(async (log) => { @@ -127,15 +131,49 @@ async function getTradeableOrderWithSignature( conditionalOrder: ConditionalOrder, contract: ComposableCoW ) { + const proof = conditionalOrder.proof ? conditionalOrder.proof.path : []; + const offchainInput = "0x"; + contract.getTradeableOrderWithSignature; + const { to, data } = + await contract.populateTransaction.getTradeableOrderWithSignature( + owner, + conditionalOrder.params, + offchainInput, + proof + ); + + // await contract.provider + // .estimateGas({ + // to: contract.address, + // data, + // }) + // .then((r) => { + // console.log("[getTradeableOrderWithSignature:estimateGas] Success", r); + // }) + // .catch((e) => { + // console.error( + // '[getTradeableOrderWithSignature:estimateGas] Error during "getTradeableOrderWithSignature" call: ', + // e + // ); + // }); + + console.log("[getTradeableOrderWithSignature] Simulate", { + to, + data, + }); + return contract.callStatic .getTradeableOrderWithSignature( owner, conditionalOrder.params, - "0x", - conditionalOrder.proof ? conditionalOrder.proof.path : [] + offchainInput, + proof ) .catch((e) => { - console.error('Error during "getTradeableOrderWithSignature" call: ', e); + console.error( + '[getTradeableOrderWithSignature] Error during "getTradeableOrderWithSignature" call: ', + e + ); throw e; }); } @@ -163,7 +201,11 @@ const _checkForAndPlaceOrder: ActionFn = async ( const blockEvent = event as BlockEvent; const { network } = blockEvent; const chainContext = await ChainContext.create(context, network); - const { registry } = await init(blockEvent.network, context); + const { registry } = await init( + "checkForAndPlaceOrder", + blockEvent.network, + context + ); const { ownerOrders } = registry; // enumerate all the owners @@ -174,7 +216,7 @@ const _checkForAndPlaceOrder: ActionFn = async ( // enumerate all the `ConditionalOrder`s for a given owner for (const conditionalOrder of conditionalOrders) { console.log( - `[checkForAndPlaceOrder] Check conditional order created in ${conditionalOrder.tx} with params:`, + `[checkForAndPlaceOrder] Check conditional order created in TX ${conditionalOrder.tx} with params:`, conditionalOrder.params ); const contract = ComposableCoW__factory.connect( @@ -468,7 +510,10 @@ class ChainContext { return new ChainContext(provider, apiUrl(network)); } } -function handleOrderBookError(status: any, data: any): { shouldThrow: boolean } { +function handleOrderBookError( + status: any, + data: any +): { shouldThrow: boolean } { if (status === 400 && data?.errorType === "DuplicatedOrder") { // The order is in the OrderBook, all good :) return { shouldThrow: false };