diff --git a/package-lock.json b/package-lock.json index 0f51cc4..379e582 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "2.5.5", "license": "ISC", "dependencies": { - "@nexusmutual/deployments": "^3.0.0", + "@nexusmutual/deployments": "^3.3.0", + "@nexusmutual/ethers-v5-aws-kms-signer": "^0.0.1", "@nexusmutual/utils": "^0.0.1", "convict": "^6.2.4", "dotenv": "^16.0.3", @@ -97,6 +98,658 @@ "openapi-types": ">=7" } }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-kms": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.971.0.tgz", + "integrity": "sha512-6ONABinm4PTTaMPZFIemKtepSHo0PnWALxUuWW9mhhposaosXvS8j4QzkkYb9L1SYiLAzfeCtfCgeXtmPGzSbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.970.0", + "@aws-sdk/credential-provider-node": "3.971.0", + "@aws-sdk/middleware-host-header": "3.969.0", + "@aws-sdk/middleware-logger": "3.969.0", + "@aws-sdk/middleware-recursion-detection": "3.969.0", + "@aws-sdk/middleware-user-agent": "3.970.0", + "@aws-sdk/region-config-resolver": "3.969.0", + "@aws-sdk/types": "3.969.0", + "@aws-sdk/util-endpoints": "3.970.0", + "@aws-sdk/util-user-agent-browser": "3.969.0", + "@aws-sdk/util-user-agent-node": "3.971.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.20.6", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.7", + "@smithy/middleware-retry": "^4.4.23", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.22", + "@smithy/util-defaults-mode-node": "^4.2.25", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.971.0.tgz", + "integrity": "sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.970.0", + "@aws-sdk/middleware-host-header": "3.969.0", + "@aws-sdk/middleware-logger": "3.969.0", + "@aws-sdk/middleware-recursion-detection": "3.969.0", + "@aws-sdk/middleware-user-agent": "3.970.0", + "@aws-sdk/region-config-resolver": "3.969.0", + "@aws-sdk/types": "3.969.0", + "@aws-sdk/util-endpoints": "3.970.0", + "@aws-sdk/util-user-agent-browser": "3.969.0", + "@aws-sdk/util-user-agent-node": "3.971.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.20.6", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.7", + "@smithy/middleware-retry": "^4.4.23", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.22", + "@smithy/util-defaults-mode-node": "^4.2.25", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.970.0.tgz", + "integrity": "sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.969.0", + "@aws-sdk/xml-builder": "3.969.0", + "@smithy/core": "^3.20.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.970.0.tgz", + "integrity": "sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.970.0.tgz", + "integrity": "sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.10", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.971.0.tgz", + "integrity": "sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/credential-provider-env": "3.970.0", + "@aws-sdk/credential-provider-http": "3.970.0", + "@aws-sdk/credential-provider-login": "3.971.0", + "@aws-sdk/credential-provider-process": "3.970.0", + "@aws-sdk/credential-provider-sso": "3.971.0", + "@aws-sdk/credential-provider-web-identity": "3.971.0", + "@aws-sdk/nested-clients": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.971.0.tgz", + "integrity": "sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/nested-clients": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.971.0.tgz", + "integrity": "sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.970.0", + "@aws-sdk/credential-provider-http": "3.970.0", + "@aws-sdk/credential-provider-ini": "3.971.0", + "@aws-sdk/credential-provider-process": "3.970.0", + "@aws-sdk/credential-provider-sso": "3.971.0", + "@aws-sdk/credential-provider-web-identity": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.970.0.tgz", + "integrity": "sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.971.0.tgz", + "integrity": "sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.971.0", + "@aws-sdk/core": "3.970.0", + "@aws-sdk/token-providers": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.971.0.tgz", + "integrity": "sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/nested-clients": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.969.0.tgz", + "integrity": "sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.969.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.969.0.tgz", + "integrity": "sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.969.0", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.969.0.tgz", + "integrity": "sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.969.0", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.970.0.tgz", + "integrity": "sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@aws-sdk/util-endpoints": "3.970.0", + "@smithy/core": "^3.20.6", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.971.0.tgz", + "integrity": "sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.970.0", + "@aws-sdk/middleware-host-header": "3.969.0", + "@aws-sdk/middleware-logger": "3.969.0", + "@aws-sdk/middleware-recursion-detection": "3.969.0", + "@aws-sdk/middleware-user-agent": "3.970.0", + "@aws-sdk/region-config-resolver": "3.969.0", + "@aws-sdk/types": "3.969.0", + "@aws-sdk/util-endpoints": "3.970.0", + "@aws-sdk/util-user-agent-browser": "3.969.0", + "@aws-sdk/util-user-agent-node": "3.971.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.20.6", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.7", + "@smithy/middleware-retry": "^4.4.23", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.22", + "@smithy/util-defaults-mode-node": "^4.2.25", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.969.0.tgz", + "integrity": "sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.969.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.971.0.tgz", + "integrity": "sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/nested-clients": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.969.0.tgz", + "integrity": "sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.970.0.tgz", + "integrity": "sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.969.0", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.2.tgz", + "integrity": "sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.969.0.tgz", + "integrity": "sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.969.0", + "@smithy/types": "^4.12.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.971.0.tgz", + "integrity": "sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.969.0.tgz", + "integrity": "sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -1892,11 +2545,26 @@ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, "node_modules/@nexusmutual/deployments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@nexusmutual/deployments/-/deployments-3.0.0.tgz", - "integrity": "sha512-Yf1ApR9Yzpoc5DhyGCi1lPCV2+31IqRNSaHj5K+/tIauGF0N+tAplErjW7sDZw/XuuPxNsM+hcQ+aLQehELt7g==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@nexusmutual/deployments/-/deployments-3.3.0.tgz", + "integrity": "sha512-+Ou9YRQEdbVZxpMP+jRoW0ChuvvPw6REqI5ehyuzsFhB1CmvUMzWUNsfOQod/Qi4ZbQij581GNVS82x/ImDc7w==", "license": "GPL-3.0" }, + "node_modules/@nexusmutual/ethers-v5-aws-kms-signer": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@nexusmutual/ethers-v5-aws-kms-signer/-/ethers-v5-aws-kms-signer-0.0.1.tgz", + "integrity": "sha512-6pAxrUiNPKWnyJfsKbuM5zSdODpoWyeIquFEC+mTxyutYldR6aJq9AdHN5ntI7gY8ehGr3gAEcasOsmGndsSFQ==", + "license": "MIT", + "dependencies": { + "@aws-sdk/client-kms": "^3.533.0", + "@ethersproject/providers": "^5.7.2", + "asn1.js": "^5.4.1", + "ethers": "^5.7.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@nexusmutual/utils": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/@nexusmutual/utils/-/utils-0.0.1.tgz", @@ -1968,32 +2636,612 @@ "type-detect": "4.0.8" } }, - "node_modules/@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", - "dev": true, + "node_modules/@sinonjs/samsam": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", + "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", + "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.20.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.7.tgz", + "integrity": "sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", + "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", + "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", + "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", + "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", + "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.8.tgz", + "integrity": "sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.20.7", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.24", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.24.tgz", + "integrity": "sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/smithy-client": "^4.10.9", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", + "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", + "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", + "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz", + "integrity": "sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", + "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", + "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", + "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", + "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", + "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", + "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", + "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.10.9", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.9.tgz", + "integrity": "sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.20.7", + "@smithy/middleware-endpoint": "^4.4.8", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.10", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", + "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", + "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.23", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.23.tgz", + "integrity": "sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.10.9", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.26", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.26.tgz", + "integrity": "sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.6", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.10.9", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", + "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", + "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", + "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.10", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.10.tgz", + "integrity": "sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^2.0.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true - }, "node_modules/@types/conventional-commits-parser": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", @@ -2551,6 +3799,24 @@ "dev": true, "license": "MIT" }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -2648,6 +3914,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -4446,6 +5718,24 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -7707,6 +8997,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/superagent": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", @@ -7958,6 +9260,12 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8519,6 +9827,526 @@ "z-schema": "^5.0.1" } }, + "@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "requires": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "requires": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "requires": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + } + } + } + }, + "@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "requires": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "requires": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "requires": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "requires": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + } + } + } + }, + "@aws-sdk/client-kms": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.971.0.tgz", + "integrity": "sha512-6ONABinm4PTTaMPZFIemKtepSHo0PnWALxUuWW9mhhposaosXvS8j4QzkkYb9L1SYiLAzfeCtfCgeXtmPGzSbw==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.970.0", + "@aws-sdk/credential-provider-node": "3.971.0", + "@aws-sdk/middleware-host-header": "3.969.0", + "@aws-sdk/middleware-logger": "3.969.0", + "@aws-sdk/middleware-recursion-detection": "3.969.0", + "@aws-sdk/middleware-user-agent": "3.970.0", + "@aws-sdk/region-config-resolver": "3.969.0", + "@aws-sdk/types": "3.969.0", + "@aws-sdk/util-endpoints": "3.970.0", + "@aws-sdk/util-user-agent-browser": "3.969.0", + "@aws-sdk/util-user-agent-node": "3.971.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.20.6", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.7", + "@smithy/middleware-retry": "^4.4.23", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.22", + "@smithy/util-defaults-mode-node": "^4.2.25", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/client-sso": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.971.0.tgz", + "integrity": "sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.970.0", + "@aws-sdk/middleware-host-header": "3.969.0", + "@aws-sdk/middleware-logger": "3.969.0", + "@aws-sdk/middleware-recursion-detection": "3.969.0", + "@aws-sdk/middleware-user-agent": "3.970.0", + "@aws-sdk/region-config-resolver": "3.969.0", + "@aws-sdk/types": "3.969.0", + "@aws-sdk/util-endpoints": "3.970.0", + "@aws-sdk/util-user-agent-browser": "3.969.0", + "@aws-sdk/util-user-agent-node": "3.971.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.20.6", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.7", + "@smithy/middleware-retry": "^4.4.23", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.22", + "@smithy/util-defaults-mode-node": "^4.2.25", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/core": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.970.0.tgz", + "integrity": "sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA==", + "requires": { + "@aws-sdk/types": "3.969.0", + "@aws-sdk/xml-builder": "3.969.0", + "@smithy/core": "^3.20.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.970.0.tgz", + "integrity": "sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ==", + "requires": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-http": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.970.0.tgz", + "integrity": "sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q==", + "requires": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.10", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.971.0.tgz", + "integrity": "sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==", + "requires": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/credential-provider-env": "3.970.0", + "@aws-sdk/credential-provider-http": "3.970.0", + "@aws-sdk/credential-provider-login": "3.971.0", + "@aws-sdk/credential-provider-process": "3.970.0", + "@aws-sdk/credential-provider-sso": "3.971.0", + "@aws-sdk/credential-provider-web-identity": "3.971.0", + "@aws-sdk/nested-clients": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-login": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.971.0.tgz", + "integrity": "sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==", + "requires": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/nested-clients": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.971.0.tgz", + "integrity": "sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==", + "requires": { + "@aws-sdk/credential-provider-env": "3.970.0", + "@aws-sdk/credential-provider-http": "3.970.0", + "@aws-sdk/credential-provider-ini": "3.971.0", + "@aws-sdk/credential-provider-process": "3.970.0", + "@aws-sdk/credential-provider-sso": "3.971.0", + "@aws-sdk/credential-provider-web-identity": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.970.0.tgz", + "integrity": "sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw==", + "requires": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.971.0.tgz", + "integrity": "sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==", + "requires": { + "@aws-sdk/client-sso": "3.971.0", + "@aws-sdk/core": "3.970.0", + "@aws-sdk/token-providers": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.971.0.tgz", + "integrity": "sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==", + "requires": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/nested-clients": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.969.0.tgz", + "integrity": "sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==", + "requires": { + "@aws-sdk/types": "3.969.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.969.0.tgz", + "integrity": "sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==", + "requires": { + "@aws-sdk/types": "3.969.0", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.969.0.tgz", + "integrity": "sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==", + "requires": { + "@aws-sdk/types": "3.969.0", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.970.0.tgz", + "integrity": "sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg==", + "requires": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@aws-sdk/util-endpoints": "3.970.0", + "@smithy/core": "^3.20.6", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/nested-clients": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.971.0.tgz", + "integrity": "sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.970.0", + "@aws-sdk/middleware-host-header": "3.969.0", + "@aws-sdk/middleware-logger": "3.969.0", + "@aws-sdk/middleware-recursion-detection": "3.969.0", + "@aws-sdk/middleware-user-agent": "3.970.0", + "@aws-sdk/region-config-resolver": "3.969.0", + "@aws-sdk/types": "3.969.0", + "@aws-sdk/util-endpoints": "3.970.0", + "@aws-sdk/util-user-agent-browser": "3.969.0", + "@aws-sdk/util-user-agent-node": "3.971.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.20.6", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.7", + "@smithy/middleware-retry": "^4.4.23", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.10.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.22", + "@smithy/util-defaults-mode-node": "^4.2.25", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/region-config-resolver": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.969.0.tgz", + "integrity": "sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==", + "requires": { + "@aws-sdk/types": "3.969.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/token-providers": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.971.0.tgz", + "integrity": "sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==", + "requires": { + "@aws-sdk/core": "3.970.0", + "@aws-sdk/nested-clients": "3.971.0", + "@aws-sdk/types": "3.969.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/types": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.969.0.tgz", + "integrity": "sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.970.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.970.0.tgz", + "integrity": "sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg==", + "requires": { + "@aws-sdk/types": "3.969.0", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-locate-window": { + "version": "3.965.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.2.tgz", + "integrity": "sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.969.0.tgz", + "integrity": "sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==", + "requires": { + "@aws-sdk/types": "3.969.0", + "@smithy/types": "^4.12.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.971.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.971.0.tgz", + "integrity": "sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==", + "requires": { + "@aws-sdk/middleware-user-agent": "3.970.0", + "@aws-sdk/types": "3.969.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/xml-builder": { + "version": "3.969.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.969.0.tgz", + "integrity": "sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==", + "requires": { + "@smithy/types": "^4.12.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + } + }, + "@aws/lambda-invoke-store": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==" + }, "@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -9676,9 +11504,20 @@ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, "@nexusmutual/deployments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@nexusmutual/deployments/-/deployments-3.0.0.tgz", - "integrity": "sha512-Yf1ApR9Yzpoc5DhyGCi1lPCV2+31IqRNSaHj5K+/tIauGF0N+tAplErjW7sDZw/XuuPxNsM+hcQ+aLQehELt7g==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@nexusmutual/deployments/-/deployments-3.3.0.tgz", + "integrity": "sha512-+Ou9YRQEdbVZxpMP+jRoW0ChuvvPw6REqI5ehyuzsFhB1CmvUMzWUNsfOQod/Qi4ZbQij581GNVS82x/ImDc7w==" + }, + "@nexusmutual/ethers-v5-aws-kms-signer": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@nexusmutual/ethers-v5-aws-kms-signer/-/ethers-v5-aws-kms-signer-0.0.1.tgz", + "integrity": "sha512-6pAxrUiNPKWnyJfsKbuM5zSdODpoWyeIquFEC+mTxyutYldR6aJq9AdHN5ntI7gY8ehGr3gAEcasOsmGndsSFQ==", + "requires": { + "@aws-sdk/client-kms": "^3.533.0", + "@ethersproject/providers": "^5.7.2", + "asn1.js": "^5.4.1", + "ethers": "^5.7.2" + } }, "@nexusmutual/utils": { "version": "0.0.1", @@ -9768,6 +11607,426 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "@smithy/abort-controller": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", + "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/config-resolver": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", + "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", + "requires": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + } + }, + "@smithy/core": { + "version": "3.20.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.7.tgz", + "integrity": "sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw==", + "requires": { + "@smithy/middleware-serde": "^4.2.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + } + }, + "@smithy/credential-provider-imds": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", + "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", + "requires": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "tslib": "^2.6.2" + } + }, + "@smithy/fetch-http-handler": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", + "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", + "requires": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/hash-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", + "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", + "requires": { + "@smithy/types": "^4.12.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/invalid-dependency": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", + "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-content-length": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", + "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", + "requires": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-endpoint": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.8.tgz", + "integrity": "sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ==", + "requires": { + "@smithy/core": "^3.20.7", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-retry": { + "version": "4.4.24", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.24.tgz", + "integrity": "sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q==", + "requires": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/smithy-client": "^4.10.9", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-serde": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", + "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", + "requires": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-stack": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", + "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-config-provider": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", + "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", + "requires": { + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-http-handler": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz", + "integrity": "sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==", + "requires": { + "@smithy/abort-controller": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", + "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/protocol-http": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", + "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-builder": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", + "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", + "requires": { + "@smithy/types": "^4.12.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", + "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/service-error-classification": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", + "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", + "requires": { + "@smithy/types": "^4.12.0" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", + "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/signature-v4": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", + "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", + "requires": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/smithy-client": { + "version": "4.10.9", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.9.tgz", + "integrity": "sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg==", + "requires": { + "@smithy/core": "^3.20.7", + "@smithy/middleware-endpoint": "^4.4.8", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.10", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", + "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/url-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", + "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", + "requires": { + "@smithy/querystring-parser": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "requires": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "requires": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-defaults-mode-browser": { + "version": "4.3.23", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.23.tgz", + "integrity": "sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA==", + "requires": { + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.10.9", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-defaults-mode-node": { + "version": "4.2.26", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.26.tgz", + "integrity": "sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w==", + "requires": { + "@smithy/config-resolver": "^4.4.6", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.10.9", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-endpoints": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", + "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", + "requires": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-middleware": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", + "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", + "requires": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-retry": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", + "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", + "requires": { + "@smithy/service-error-classification": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-stream": { + "version": "4.5.10", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.10.tgz", + "integrity": "sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==", + "requires": { + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "requires": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "requires": { + "tslib": "^2.6.2" + } + }, "@types/conventional-commits-parser": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", @@ -10141,6 +12400,24 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==" + } + } + }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -10218,6 +12495,11 @@ } } }, + "bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -11468,6 +13750,14 @@ "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", "dev": true }, + "fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "requires": { + "strnum": "^2.1.0" + } + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -13776,6 +16066,11 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==" + }, "superagent": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", @@ -13958,6 +16253,11 @@ "strip-bom": "^3.0.0" } }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 5a8c0a7..7aa5502 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "ts:check": "echo 'No TS type check step'" }, "dependencies": { - "@nexusmutual/deployments": "^3.0.0", + "@nexusmutual/deployments": "^3.3.0", + "@nexusmutual/ethers-v5-aws-kms-signer": "^0.0.1", "@nexusmutual/utils": "^0.0.1", "convict": "^6.2.4", "dotenv": "^16.0.3", diff --git a/src/config.js b/src/config.js index dffa072..48c136b 100644 --- a/src/config.js +++ b/src/config.js @@ -33,6 +33,26 @@ const config = convict({ format: Object, default: {}, }, + awsAccessKeyId: { + doc: 'The AWS credentials key ID', + default: '', + env: 'AWS_ACCESS_KEY_ID', + }, + awsSecretAccessKey: { + doc: 'The AWS credentials secret access key', + default: '', + env: 'AWS_SECRET_ACCESS_KEY', + }, + awsKmsKeyId: { + doc: 'The UUID of the AWS KMS key or AWS KMS key alias', + default: '', + env: 'AWS_KMS_KEY_ID', + }, + awsRegion: { + doc: 'The AWS region where the KMS key is located', + default: '', + env: 'AWS_REGION', + }, }); // Automatically detect and add PRIORITY_POOLS_ORDER environment variables diff --git a/src/index.js b/src/index.js index c64d4af..9fd336a 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,7 @@ const config = require('./config'); const createChainApi = require('./lib/chainApi'); const contractFactory = require('./lib/contracts'); const createEventsApi = require('./lib/eventsApi'); +const riContractFactory = require('./lib/riContracts'); const swaggerSpec = require('./lib/swagger'); const createSynchronizer = require('./lib/synchronizer'); const { capacityRouter, pricingRouter, quoteRouter, reindexRouter } = require('./routes'); @@ -26,11 +27,12 @@ const main = async () => { provider.pollingInterval = config.get('pollingInterval'); // contract factory - const contracts = await contractFactory(addresses, provider); + const contracts = contractFactory(addresses, provider); + const riContracts = riContractFactory(provider); // apis - const chainApi = await createChainApi(contracts); - const eventsApi = await createEventsApi(provider, contracts); + const chainApi = await createChainApi(contracts, riContracts); + const eventsApi = await createEventsApi(provider, contracts, riContracts); const app = express(); @@ -72,6 +74,7 @@ const main = async () => { if (!isFromCache) { console.warn('Missing initial state, delaying startup until the state is fully loaded'); await synchronizer.updateAssetRates(); + await synchronizer.updateRiData(); await synchronizer.updateAll(); } @@ -83,6 +86,7 @@ const main = async () => { if (isFromCache) { await synchronizer.updateAssetRates(); + await synchronizer.updateRiData(); await synchronizer.updateAll(); } }; diff --git a/src/lib/capacityEngine.js b/src/lib/capacityEngine.js index 73287b2..0b4b323 100644 --- a/src/lib/capacityEngine.js +++ b/src/lib/capacityEngine.js @@ -1,6 +1,6 @@ const { ethers, BigNumber } = require('ethers'); -const { MAX_COVER_PERIOD } = require('./constants'); +const { MAX_COVER_PERIOD, RI_EPOCH_DURATION } = require('./constants'); const { bnMax, calculateTrancheId, @@ -9,7 +9,14 @@ const { getCapacitiesInAssets, getLatestCover, } = require('./helpers'); -const { selectProduct, selectProductPools, selectProductsInPool } = require('../store/selectors'); +const { + selectProduct, + selectProductPools, + selectProductsInPool, + selectVaultProducts, + selectRiAssetRate, + selectVaultEpochExpiryTimestamp, +} = require('../store/selectors'); const { WeiPerEther, Zero } = ethers.constants; @@ -60,7 +67,7 @@ function calculatePoolUtilizationRate(products) { function calculateProductCapacity( store, productId, - { poolId = null, period, now, assets, assetRates, withPools = true, editedCover = null }, + { poolId = null, vaultId = null, period, now, assets, assetRates, withPools = true, editedCover = null }, ) { const product = selectProduct(store, productId); if (!product) { @@ -120,10 +127,42 @@ function calculateProductCapacity( } } + let totalRiCapacity = Zero; + if (!poolId) { + const expiries = selectVaultEpochExpiryTimestamp(store); + const coverExpiry = now.add(product.gracePeriod).add(period); + const epochDuration = RI_EPOCH_DURATION * 24 * 3600; + const riVaults = selectVaultProducts(store, productId); + + totalRiCapacity = riVaults + .filter(vault => vault && expiries[vault.vaultId] && expiries[vault.vaultId].add(epochDuration).gt(coverExpiry)) + .reduce((total, vault) => { + const assetRate = selectRiAssetRate(store, vault.asset); + if (!assetRate) { + return total; // Skip vaults without asset rate + } + const allocatedAmount = (vault.allocations || []).reduce((acc, allocation) => { + // cover edit allocation + if (allocation.expiryTimestamp > now && allocation.coverId !== editedCover?.coverId && allocation.active) { + acc = acc.add(allocation.amount); + } + return acc; + }, BigNumber.from(0)); + // All values are in vault asset units: activeStake, withdrawalAmount, and allocatedAmount + // Convert to NXM at the end + const availableCapacityInAsset = vault.activeStake.add(vault.withdrawalAmount).sub(allocatedAmount); + const availableCapacityInNXM = availableCapacityInAsset.mul(assetRate).div(WeiPerEther); + return total.add(availableCapacityInNXM); + }, Zero); + } + const { capacityAvailableNXM, capacityUsedNXM, minPrice } = aggregatedData; + // Add RI capacity to pool capacity + const totalCapacityAvailableNXM = capacityAvailableNXM.add(totalRiCapacity); + // The available (i.e. remaining) capacity of a product - const capacityInAssets = getCapacitiesInAssets(capacityAvailableNXM, assets, assetRates); + const capacityInAssets = getCapacitiesInAssets(totalCapacityAvailableNXM, assets, assetRates); const capacityData = { productId: Number(productId), diff --git a/src/lib/chainApi.js b/src/lib/chainApi.js index 732e392..f618f07 100644 --- a/src/lib/chainApi.js +++ b/src/lib/chainApi.js @@ -1,7 +1,11 @@ -const { ethers } = require('ethers'); +const { BigNumber, ethers } = require('ethers'); + +const constants = require('./constants'); + const { WeiPerEther } = ethers.constants; +const { defaultAbiCoder } = ethers.utils; -const createChainApi = async contracts => { +const createChainApi = async (contracts, riContracts) => { // contract instances const cover = contracts('Cover'); const coverProducts = contracts('CoverProducts'); @@ -108,6 +112,83 @@ const createChainApi = async contracts => { return packedTrancheAllocation; }; + // RiContracts + const fetchVaultStake = async (vaultId, subnetworks = [], productId = null, riSubnetworks = {}) => { + const operator = '0xf99aa6479eb153dca93fd243a06cacd11f3268f9'; + let maxWeightedStake = BigNumber.from(0); + + for (const subnetworkId of subnetworks) { + const subnetworkStake = await riContracts[`delegator_${vaultId}`].stake(subnetworkId, operator); + + // Determine the weight to use for this subnetwork + let weight = constants.RI_WEIGHT; // Default weight + + if (productId !== null && riSubnetworks && riSubnetworks[subnetworkId]) { + const subnetwork = riSubnetworks[subnetworkId]; + // Check if this subnetwork contains the product + if (subnetwork.products && subnetwork.products[String(productId)]) { + weight = subnetwork.products[String(productId)].weight; + } + } + + // Calculate weighted stake for this subnetwork: stake * weight / 100 + const weightedStake = subnetworkStake.mul(weight).div(constants.RI_WEIGHT_DENOMINATOR); + + // Keep track of the maximum weighted stake across all subnetworks + // This allows a subnetwork with lower stake but higher weight to win + maxWeightedStake = weightedStake.gt(maxWeightedStake) ? weightedStake : maxWeightedStake; + } + + return maxWeightedStake; + }; + + const fetchVaultWithdrawals = async vaultId => { + console.log(riContracts[`vault_${vaultId}`].address); + const currentEpoch = await riContracts[`vault_${vaultId}`].currentEpoch(); + const withdrawalAmount = await riContracts[`vault_${vaultId}`].withdrawals(currentEpoch + 1); + return withdrawalAmount.mul(constants.RI_WEIGHT).div(constants.RI_WEIGHT_DENOMINATOR); + }; + + const fetchVaultAllocations = async blockNumber => { + const events = await cover.queryFilter(cover.filters.CoverRiAllocated(), blockNumber); + + const allocations = {}; + for (const event of events) { + const { args } = event; + const { coverId, data, dataFormat } = args; + + const { start, period, productId, originalCoverId } = await fetchCover(coverId); + const coverAllocations = defaultAbiCoder.decode([constants.RI_DATA_FORMATS[dataFormat]], [data]); + + for (const coverAllocation of coverAllocations) { + const { amount, vaultId } = coverAllocation; + if (!allocations[`${productId}_${vaultId}`]) { + allocations[`${productId}_${vaultId}`] = []; + } + + allocations[`${productId}_${vaultId}`].push({ + amount, + coverId, + expiryTimestamp: BigNumber.from(start).add(period), + originalCoverId, + }); + } + } + + return allocations; + }; + + const fetchVaultNextEpochStart = async vaultId => { + return await riContracts[`vault_${vaultId}`].nextEpochStart(); + }; + + const fetchRiAssetRate = async assetId => { + return { + assetRate: await riContracts[`asset_${assetId}`].getRate(), + protocolAssetCorrelationId: riContracts[`asset_${assetId}`].protocolAssetCorrelationId, + }; + }; + return { fetchProducts, fetchProduct, @@ -122,6 +203,11 @@ const createChainApi = async contracts => { fetchCover, fetchCoverPoolTrancheAllocations, fetchCoverReference, + fetchVaultStake, + fetchVaultWithdrawals, + fetchVaultAllocations, + fetchVaultNextEpochStart, + fetchRiAssetRate, }; }; diff --git a/src/lib/constants.js b/src/lib/constants.js index 08b8124..eb75b1b 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -46,7 +46,21 @@ module.exports = { SECONDS_PER_DAY: BigNumber.from(24 * 60 * 60), // set to minimal cover id which is not expired - FETCH_COVER_DATA_FROM_ID: 992, + FETCH_COVER_DATA_FROM_ID: 3710, + // Ri constants + SYMBIOTIC_PROVIDER_ID: 1, + RI_FETCH_COVER_DATA_FROM_BLOCK: 24219565, + RI_WEIGHT: 25, + RI_WEIGHT_DENOMINATOR: 100, + RI_THRESHOLD: 20_000_000 * 1_000_000, // in USDC + RI_MIN_COVER_AMOUNT: 100_000 * 1_000_000, // in USDC + RI_COVER_AMOUNT_PERCENTAGE: 80, + RI_COVER_AMOUNT_DENOMINATOR: 100, + RI_EPOCH_DURATION: 70, // days + + RI_DATA_FORMATS: { + 1: 'tuple(uint256 amount,uint256 vaultId, uint256 subnetworkId,uint256 providerId)[]', + }, HTTP_STATUS, }; diff --git a/src/lib/eventsApi.js b/src/lib/eventsApi.js index 0fea3ad..440b7ed 100644 --- a/src/lib/eventsApi.js +++ b/src/lib/eventsApi.js @@ -6,7 +6,7 @@ const { calculateTrancheId, calculateBucketId } = require('./helpers'); const events = ['StakeBurned', 'DepositExtended', 'StakeDeposited', 'PoolFeeChanged', 'Deallocated']; -module.exports = async (provider, contracts) => { +module.exports = async (provider, contracts, riContracts) => { // event emitter const emitter = new EventEmitter(); @@ -24,12 +24,12 @@ module.exports = async (provider, contracts) => { // emit an event on every block provider.on('block', async blockNumber => { + const { timestamp: blockTimestamp } = await provider.getBlock(blockNumber); const now = Math.floor(Date.now() / 1000); const activeBucketId = calculateBucketId(now); const activeTrancheId = calculateTrancheId(now); if (activeBucketId !== currentBucketId) { - const { timestamp: blockTimestamp } = await provider.getBlock(blockNumber); const blockBucketId = calculateBucketId(blockTimestamp); if (blockBucketId === activeBucketId) { @@ -41,7 +41,6 @@ module.exports = async (provider, contracts) => { } if (activeTrancheId !== currentTrancheId) { - const { timestamp: blockTimestamp } = await provider.getBlock(blockNumber); const blockTrancheId = calculateTrancheId(blockTimestamp); if (blockTrancheId === activeTrancheId) { @@ -52,7 +51,7 @@ module.exports = async (provider, contracts) => { } } - emitter.emit('block', blockNumber); + emitter.emit('block', blockNumber, blockTimestamp); }); // listeners @@ -104,6 +103,48 @@ module.exports = async (provider, contracts) => { emitter.emit('cover:change', coverId); }); + // Cover Ri events + cover.on('CoverRiAllocated', (coverId, premium, paymentAsset, data, dataFormat) => { + console.info(`Event: Cover ${coverId} allocated with RI`); + emitter.emit('ri:bought', coverId, data, dataFormat); + }); + + for (const contractName of Object.keys(riContracts)) { + const vaultId = contractName.split('_')[1]; + if (contractName.startsWith('vault_')) { + riContracts[contractName].on('Withdraw', () => { + console.info(`Event: Withdraw for vault ${vaultId}`); + emitter.emit('ri:withdraw', vaultId); + }); + riContracts[`vault_${vaultId}`].on('Deposit', () => { + console.info(`Event: Deposit for vault ${vaultId}`); + emitter.emit('ri:deposit', vaultId); + }); + riContracts[`vault_${vaultId}`].on('OnSlash', () => { + console.info(`Event: Slash for vault ${vaultId}`); + emitter.emit('ri:slash', vaultId); + }); + } + if (contractName.startsWith('delegator_')) { + riContracts[`delegator_${vaultId}`].on('SetMaxNetworkLimit', () => { + console.info(`Event: SetMaxNetworkLimit for vault ${vaultId}`); + emitter.emit('ri:setMaxNetworkLimit', vaultId); + }); + riContracts[`delegator_${vaultId}`].on('SetNetworkLimit', () => { + console.info(`Event: SetNetworkLimit for vault ${vaultId}`); + emitter.emit('ri:setNetworkLimit', vaultId); + }); + riContracts[`delegator_${vaultId}`].on('SetOperatorNetworkShares', () => { + console.info(`Event: SetOperatorNetworkShares for vault ${vaultId}`); + emitter.emit('ri:setOperatorNetworkShares', vaultId); + }); + riContracts[`delegator_${vaultId}`].on('SetOperatorNetworkLimit', () => { + console.info(`Event: SetOperatorNetworkLimit for vault ${vaultId}`); + emitter.emit('ri:setOperatorNetworkLimit', vaultId); + }); + } + } + return { on: emitter.on.bind(emitter), off: emitter.off.bind(emitter), diff --git a/src/lib/helpers.js b/src/lib/helpers.js index 89ac97c..3178db6 100644 --- a/src/lib/helpers.js +++ b/src/lib/helpers.js @@ -13,12 +13,14 @@ const { CAPACITY_BUFFER_MINIMUM, MAX_ACTIVE_TRANCHES, HTTP_STATUS, + RI_DATA_FORMATS, } = require('./constants'); const { ApiError } = require('./error'); const { selectCover } = require('../store/selectors'); const { BigNumber } = ethers; const { WeiPerEther, Zero } = ethers.constants; +const { defaultAbiCoder } = ethers.utils; /* Bignumber Utils */ @@ -301,6 +303,10 @@ function getLatestCover(store, originalCoverId) { : selectCover(store, originalCover.latestCoverId); } +const decodeRiData = (data, dataFormat) => { + return defaultAbiCoder.decode([RI_DATA_FORMATS[dataFormat]], [data]); +}; + module.exports = { bnMax, bnMin, @@ -319,4 +325,5 @@ module.exports = { getCoverTrancheAllocations, calculateCoverRefundInNXM, getLatestCover, + decodeRiData, }; diff --git a/src/lib/quoteEngine.js b/src/lib/quoteEngine.js index a357193..6c2fda1 100644 --- a/src/lib/quoteEngine.js +++ b/src/lib/quoteEngine.js @@ -1,6 +1,17 @@ const { BigNumber, ethers } = require('ethers'); -const { NXM_PER_ALLOCATION_UNIT, ONE_YEAR, TARGET_PRICE_DENOMINATOR, HTTP_STATUS } = require('./constants'); +const { + NXM_PER_ALLOCATION_UNIT, + ONE_YEAR, + TARGET_PRICE_DENOMINATOR, + RI_THRESHOLD, + RI_MIN_COVER_AMOUNT, + RI_COVER_AMOUNT_PERCENTAGE, + RI_COVER_AMOUNT_DENOMINATOR, + RI_EPOCH_DURATION, + SYMBIOTIC_PROVIDER_ID, + HTTP_STATUS, +} = require('./constants'); const { ApiError } = require('./error'); const { calculateFirstUsableTrancheIndex, @@ -18,6 +29,11 @@ const { selectProductPools, selectProduct, selectProductPriorityPoolsFixedPrice, + selectVaultProducts, + selectRiAssetRate, + selectActiveCoverAmount, + selectVaultEpochExpiryTimestamp, + selectRiCoverAmountPercentage, } = require('../store/selectors'); const { WeiPerEther, Zero } = ethers.constants; @@ -88,6 +104,208 @@ function calculateAnualPrice(premiumInAsset, period, coverAmountInAsset) { .add(1); // add one as a round up to the second decimal for better precision } +function calculateVaultCapacity(store, vault, now, coverId = 0) { + // assetRate from staked token to NXM + const assetRate = selectRiAssetRate(store, vault.asset); + if (!assetRate) { + return BigNumber.from(0); + } + const allocatedAmount = (vault.allocations || []).reduce((acc, allocation) => { + // cover edit allocation + const expiryTimestamp = BigNumber.isBigNumber(allocation.expiryTimestamp) + ? allocation.expiryTimestamp + : BigNumber.from(allocation.expiryTimestamp || 0); + const allocationAmount = BigNumber.isBigNumber(allocation.amount) + ? allocation.amount + : BigNumber.from(allocation.amount || 0); + if (expiryTimestamp.gt(now) && allocation.coverId !== coverId) { + acc = acc.add(allocationAmount); + } + return acc; + }, BigNumber.from(0)); + + // All values are in vault asset units: activeStake, withdrawalAmount, and allocatedAmount + // Convert to NXM at the end + const activeStake = BigNumber.isBigNumber(vault.activeStake) + ? vault.activeStake + : BigNumber.from(vault.activeStake || 0); + const withdrawalAmount = BigNumber.isBigNumber(vault.withdrawalAmount) + ? vault.withdrawalAmount + : BigNumber.from(vault.withdrawalAmount || 0); + const availableCapacityInAsset = activeStake.add(withdrawalAmount).sub(allocatedAmount); + return availableCapacityInAsset.mul(assetRate).div(WeiPerEther); +} + +/** + * Calculates the refund premium for RI (Reinsurance) allocations when a cover is being edited. + * The refund is calculated based on the remaining period of the cover. + * + * @param {object} store - The application state store. + * @param {object} product - The product object. + * @param {object} cover - The cover being edited (must have coverId, start, and period). + * @param {BigNumber} now - The current timestamp in seconds. + * @param {number} paymentAsset - The assetId of the asset used for payment. + * @returns {BigNumber} - The total refund premium in payment asset, or Zero if no refund. + */ +function calculateRiRefundInPaymentAsset(store, product, cover, now, paymentAsset) { + if (!cover || !cover.coverId) { + return Zero; + } + + const paymentAssetRate = selectAssetRate(store, paymentAsset); + const coverStart = BigNumber.isBigNumber(cover.start) ? cover.start : BigNumber.from(cover.start); + const coverPeriod = BigNumber.isBigNumber(cover.period) ? cover.period : BigNumber.from(cover.period); + const remainingPeriod = coverStart.add(coverPeriod).sub(now); + + if (remainingPeriod.lte(0)) { + return Zero; + } + + const vaults = selectVaultProducts(store, product.productId); + let totalRefundInPaymentAsset = Zero; + const nowNumber = BigNumber.isBigNumber(now) ? now.toNumber() : now; + + for (const vault of vaults) { + if (!vault || !vault.allocations) { + continue; + } + + // Find allocations for this cover that are still active + const coverAllocations = vault.allocations.filter( + allocation => allocation.coverId === cover.coverId && allocation.expiryTimestamp > nowNumber, + ); + + for (const allocation of coverAllocations) { + // Convert allocation amount from vault asset to NXM + const riAssetRate = selectRiAssetRate(store, vault.asset); + const allocationAmountInNXM = allocation.amount.mul(riAssetRate).div(WeiPerEther); + + // Calculate the original premium that was paid for this existing allocation + const premiumInNXM = allocationAmountInNXM + .mul(vault.price) + .mul(coverPeriod) + .div(ONE_YEAR) + .div(TARGET_PRICE_DENOMINATOR); + + // Calculate refund based on remaining period + const refundInNXM = premiumInNXM.mul(remainingPeriod).div(coverPeriod); + + // Convert refund to payment asset + const refundForAllocation = refundInNXM.mul(paymentAssetRate).div(WeiPerEther); + totalRefundInPaymentAsset = totalRefundInPaymentAsset.add(refundForAllocation); + } + } + + return totalRefundInPaymentAsset; +} + +function calculateRiQuote(store, product, period, amountInNXM, now, paymentAsset, cover) { + if (amountInNXM.eq(0)) { + return null; + } + + const paymentAssetRate = selectAssetRate(store, paymentAsset); + const expiries = selectVaultEpochExpiryTimestamp(store); // timestamps of current epoch expiration + const coverExpiry = now.add(product.gracePeriod).add(period); + const epochDuration = RI_EPOCH_DURATION * 24 * 3600; + + let totalAvailableCapacity = BigNumber.from(0); + + const allVaults = selectVaultProducts(store, product.id); + if (!allVaults || !Array.isArray(allVaults) || allVaults.length === 0) { + return null; + } + // Filter out null/undefined vaults + const validVaults = allVaults.filter(v => v != null); + if (validVaults.length === 0) { + return null; + } + const epochDurationBN = BigNumber.from(epochDuration); + const vaults = validVaults + .filter(vault => { + if (!vault || !vault.vaultId) { + return false; + } + const expiry = expiries[vault.vaultId]; + if (!expiry) { + return false; + } + const expiryBigNumber = BigNumber.isBigNumber(expiry) ? expiry : BigNumber.from(expiry); + const expiryWithDuration = expiryBigNumber.add(epochDurationBN); + return expiryWithDuration.gt(coverExpiry); + }) + .map(vault => { + const availableCapacityInNXM = calculateVaultCapacity(store, vault, now, cover?.coverId || 0); + totalAvailableCapacity = totalAvailableCapacity.add(availableCapacityInNXM); + return { + ...vault, + availableCapacityInNXM, + }; + }) + .sort((a, b) => a.availableCapacityInNXM.sub(b.availableCapacityInNXM).toNumber()); + + if (totalAvailableCapacity.lt(amountInNXM)) { + return null; + } + + const riRequest = { + providerId: SYMBIOTIC_PROVIDER_ID, + amount: amountInNXM, // @TODO: change to coverAsset + premium: BigNumber.from(0), + data: [], // { amount: riAmount, riPoolId: 1, providerId: riProviderId } + dataFormat: 1, + deadline: now.add(2 * 24 * 3600).toNumber(), + }; + + let vaultsCount = vaults.length; + let totalAmountToAllocate = BigNumber.from(amountInNXM); + let amountPerVault = totalAmountToAllocate.div(vaultsCount); + let remainder = totalAmountToAllocate.mod(vaultsCount); + + // Calculate refund premium if cover is being edited + const totalRefundInPaymentAsset = calculateRiRefundInPaymentAsset(store, product, cover, now, paymentAsset); + + for (const vault of vaults) { + if (vault.availableCapacityInNXM.lte(0)) { + vaultsCount--; + amountPerVault = totalAmountToAllocate.div(vaultsCount); + remainder = totalAmountToAllocate.mod(vaultsCount); + continue; + } + + let allocationAmount = amountPerVault; + const riAssetRate = selectRiAssetRate(store, vault.asset); + + if (vault.availableCapacityInNXM.lt(amountPerVault)) { + allocationAmount = vault.availableCapacityInNXM; + vaultsCount--; + totalAmountToAllocate = totalAmountToAllocate.sub(allocationAmount); + amountPerVault = totalAmountToAllocate.div(vaultsCount); + remainder = totalAmountToAllocate.mod(vaultsCount); + } else if (remainder.gt(0)) { + // add 1 to the allocation amount to cover the remainder + allocationAmount = allocationAmount.add(1); + remainder = remainder.sub(1); + } + + const premiumInNXM = allocationAmount.mul(vault.price).mul(period).div(ONE_YEAR).div(TARGET_PRICE_DENOMINATOR); + const premiumInPaymentAsset = premiumInNXM.mul(paymentAssetRate).div(WeiPerEther); + + riRequest.premium = riRequest.premium.add(premiumInPaymentAsset); + riRequest.data.push({ + amount: allocationAmount.mul(riAssetRate).div(WeiPerEther), + riVaultId: vault.id, + providerId: vault.providerId, + }); + } + + // Subtract total refund from premium (ensure premium doesn't go negative) + const premiumAfterRefund = riRequest.premium.sub(totalRefundInPaymentAsset); + riRequest.premium = premiumAfterRefund.gt(0) ? premiumAfterRefund : BigNumber.from(0); + + return riRequest; +} + /** * Calculates the premium and allocations for a given insurance product based on the specified parameters. * @@ -97,9 +315,11 @@ function calculateAnualPrice(premiumInAsset, period, coverAmountInAsset) { * @param {number} period - The cover period in seconds. * @param {string} coverAsset - The assetId of the asset to be covered. * @param {number} editedCoverId - The ID of the cover which is edited. ID is 0 when getting quote for new cover. + * @param {number} paymentAsset - The assetId of the asset to be used for payment. + * @param {boolean} useRiVaults - Whether to use RI vaults for the quote. * @returns {Array} - An array of objects containing pool allocations and premiums. */ -const quoteEngine = (store, productId, amount, period, coverAsset, editedCoverId = 0) => { +const quoteEngine = (store, productId, amount, period, coverAsset, editedCoverId = 0, paymentAsset, useRiVaults) => { const product = selectProduct(store, productId); if (!product) { @@ -121,8 +341,46 @@ const quoteEngine = (store, productId, amount, period, coverAsset, editedCoverId const amountToAllocate = divCeil(coverAmountInNxm, NXM_PER_ALLOCATION_UNIT).mul(NXM_PER_ALLOCATION_UNIT); console.info(`Amount to allocate: ${formatEther(amountToAllocate)} nxm`); + let riAmountInNXM = BigNumber.from(0); + const cover = getLatestCover(store, editedCoverId); + if (cover && BigNumber.from(cover.start).add(cover.period).lt(now)) { + throw new ApiError('Cover already expired', HTTP_STATUS.BAD_REQUEST); + } + + // check if the allocated amount is greater than the RI threshold + if (useRiVaults) { + const totalActiveCoverInNXM = selectActiveCoverAmount(store, productId, now); + const usdcRate = selectAssetRate(store, 6); + + const activeCover = + cover && now.lt(BigNumber.from(cover.start).add(cover.period)) + ? cover.poolAllocations.reduce((acc, pool) => { + const poolAmount = BigNumber.isBigNumber(pool.coverAmountInNxm) + ? pool.coverAmountInNxm + : BigNumber.from(pool.coverAmountInNxm); + return acc.sub(poolAmount); + }, totalActiveCoverInNXM) + : totalActiveCoverInNXM; + + const riThresholdInNXM = BigNumber.from(RI_THRESHOLD).mul(WeiPerEther).div(usdcRate); + const riMinCoverAmountInNXM = BigNumber.from(RI_MIN_COVER_AMOUNT).mul(WeiPerEther).div(usdcRate); + + // Get product-specific RI cover amount percentage, fall back to default if not set + const riCoverAmountPercentage = selectRiCoverAmountPercentage(store, productId) ?? RI_COVER_AMOUNT_PERCENTAGE; + + if (activeCover.gt(riThresholdInNXM)) { + riAmountInNXM = amountToAllocate.mul(riCoverAmountPercentage).div(RI_COVER_AMOUNT_DENOMINATOR); + } else if (activeCover.lt(riThresholdInNXM) && activeCover.add(amountToAllocate).gt(riThresholdInNXM)) { + riAmountInNXM = amountToAllocate + .sub(riThresholdInNXM.sub(activeCover)) + .mul(riCoverAmountPercentage) + .div(RI_COVER_AMOUNT_DENOMINATOR); + } + riAmountInNXM = riAmountInNXM.gt(riMinCoverAmountInNXM) ? riAmountInNXM : BigNumber.from(0); + } + const poolsData = productPools.map(pool => { const { poolId, targetPrice, bumpedPrice, bumpedPriceUpdateTime, allocations, trancheCapacities } = pool; @@ -147,9 +405,49 @@ const quoteEngine = (store, productId, amount, period, coverAsset, editedCoverId }; }); + // Check capacity for both pools and RI before allocation + if (useRiVaults && riAmountInNXM.gt(0)) { + // Calculate total pool capacity + const totalPoolCapacity = poolsData.reduce((total, pool) => { + return total.add(pool.availableCapacityInNXM); + }, Zero); + + // Calculate total RI capacity + let totalRiCapacity = Zero; + const expiries = selectVaultEpochExpiryTimestamp(store); + const coverExpiry = now.add(product.gracePeriod).add(period); + const epochDuration = RI_EPOCH_DURATION * 24 * 3600; + const riVaults = selectVaultProducts(store, productId); + totalRiCapacity = riVaults + .filter(vault => { + return vault && expiries[vault.vaultId] && expiries[vault.vaultId].add(epochDuration).gt(coverExpiry); + }) + .reduce((total, vault) => { + const availableCapacityInNXM = calculateVaultCapacity(store, vault, now, cover?.coverId || 0); + return total.add(availableCapacityInNXM); + }, Zero); + + if (totalPoolCapacity.add(totalRiCapacity).lt(amountToAllocate)) { + throw new ApiError( + `Not enough capacity. Required: ${formatEther(amountToAllocate)} NXM, Available: ${formatEther( + totalPoolCapacity.add(totalRiCapacity), + )} NXM`, + HTTP_STATUS.BAD_REQUEST, + ); + } + + if (totalPoolCapacity.lt(amountToAllocate.sub(riAmountInNXM))) { + riAmountInNXM = amountToAllocate.sub(totalPoolCapacity); + } + + if (totalRiCapacity.lt(riAmountInNXM)) { + riAmountInNXM = totalRiCapacity; + } + } + const customPoolIdPriorityFixedPrice = selectProductPriorityPoolsFixedPrice(store, productId) || []; const poolsInPriorityOrder = sortPools(poolsData, customPoolIdPriorityFixedPrice); - const allocations = calculatePoolAllocations(amountToAllocate, poolsInPriorityOrder); + const allocations = calculatePoolAllocations(amountToAllocate.sub(riAmountInNXM), poolsInPriorityOrder); const poolsWithPremium = allocations.map(allocation => { const pool = poolsData.find(data => allocation.poolId === data.poolId); @@ -194,6 +492,8 @@ const quoteEngine = (store, productId, amount, period, coverAsset, editedCoverId const annualPrice = premiumInAsset.gt(0) ? calculateAnualPrice(premiumInAsset, period, coverAmountInAsset) : Zero; + const riQuote = calculateRiQuote(store, product, period, riAmountInNXM, now, paymentAsset, cover); + return { poolsWithPremium, premiumInNXM, @@ -204,6 +504,7 @@ const quoteEngine = (store, productId, amount, period, coverAsset, editedCoverId premiumInAssetWithRefund, annualPrice, coverAmountInAsset, + riQuote, }; }; diff --git a/src/lib/riContracts/Delegator.json b/src/lib/riContracts/Delegator.json new file mode 100644 index 0000000..a6d4222 --- /dev/null +++ b/src/lib/riContracts/Delegator.json @@ -0,0 +1,937 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "operatorRegistry", + "type": "address", + "internalType": "address" + }, + { + "name": "networkRegistry", + "type": "address", + "internalType": "address" + }, + { + "name": "vaultFactory", + "type": "address", + "internalType": "address" + }, + { + "name": "operatorVaultOptInService", + "type": "address", + "internalType": "address" + }, + { + "name": "operatorNetworkOptInService", + "type": "address", + "internalType": "address" + }, + { + "name": "delegatorFactory", + "type": "address", + "internalType": "address" + }, + { + "name": "entityType", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "DEFAULT_ADMIN_ROLE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FACTORY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "HOOK_GAS_LIMIT", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "HOOK_RESERVE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "HOOK_SET_ROLE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "NETWORK_LIMIT_SET_ROLE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "NETWORK_REGISTRY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "OPERATOR_NETWORK_OPT_IN_SERVICE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "OPERATOR_REGISTRY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "OPERATOR_VAULT_OPT_IN_SERVICE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "TYPE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "VAULT_FACTORY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "VERSION", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "getRoleAdmin", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "grantRole", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "hasRole", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "hook", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "maxNetworkLimit", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "value", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "networkLimit", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "networkLimitAt", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "timestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hint", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "onSlash", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "captureTimestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "operator", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceRole", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "callerConfirmation", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "revokeRole", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setHook", + "inputs": [ + { + "name": "hook_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setMaxNetworkLimit", + "inputs": [ + { + "name": "identifier", + "type": "uint96", + "internalType": "uint96" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setNetworkLimit", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "stake", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "stakeAt", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + }, + { + "name": "timestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hints", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "staticDelegateCall", + "inputs": [ + { + "name": "target", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "supportsInterface", + "inputs": [ + { + "name": "interfaceId", + "type": "bytes4", + "internalType": "bytes4" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "vault", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OnSlash", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "captureTimestamp", + "type": "uint48", + "indexed": false, + "internalType": "uint48" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleAdminChanged", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "previousAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "newAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleGranted", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleRevoked", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetHook", + "inputs": [ + { + "name": "hook", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetMaxNetworkLimit", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetNetworkLimit", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetOperatorNetworkShares", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "shares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetOperatorNetworkLimit", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AccessControlBadConfirmation", + "inputs": [] + }, + { + "type": "error", + "name": "AccessControlUnauthorizedAccount", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + }, + { + "name": "neededRole", + "type": "bytes32", + "internalType": "bytes32" + } + ] + }, + { + "type": "error", + "name": "AlreadySet", + "inputs": [] + }, + { + "type": "error", + "name": "CheckpointUnorderedInsertion", + "inputs": [] + }, + { + "type": "error", + "name": "DuplicateRoleHolder", + "inputs": [] + }, + { + "type": "error", + "name": "ExceedsMaxNetworkLimit", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientHookGas", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidInitialization", + "inputs": [] + }, + { + "type": "error", + "name": "MissingRoleHolders", + "inputs": [] + }, + { + "type": "error", + "name": "NotInitialized", + "inputs": [] + }, + { + "type": "error", + "name": "NotInitializing", + "inputs": [] + }, + { + "type": "error", + "name": "NotNetwork", + "inputs": [] + }, + { + "type": "error", + "name": "NotOperator", + "inputs": [] + }, + { + "type": "error", + "name": "NotSlasher", + "inputs": [] + }, + { + "type": "error", + "name": "NotVault", + "inputs": [] + }, + { + "type": "error", + "name": "ReentrancyGuardReentrantCall", + "inputs": [] + }, + { + "type": "error", + "name": "SafeCastOverflowedUintDowncast", + "inputs": [ + { + "name": "bits", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "value", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "ZeroAddressRoleHolder", + "inputs": [] + } +] diff --git a/src/lib/riContracts/Slasher.json b/src/lib/riContracts/Slasher.json new file mode 100644 index 0000000..8c266b3 --- /dev/null +++ b/src/lib/riContracts/Slasher.json @@ -0,0 +1,433 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "vaultFactory", + "type": "address", + "internalType": "address" + }, + { + "name": "networkMiddlewareService", + "type": "address", + "internalType": "address" + }, + { + "name": "slasherFactory", + "type": "address", + "internalType": "address" + }, + { + "name": "entityType", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "BURNER_GAS_LIMIT", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "BURNER_RESERVE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FACTORY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "NETWORK_MIDDLEWARE_SERVICE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "TYPE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "VAULT_FACTORY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "cumulativeSlash", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "cumulativeSlashAt", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + }, + { + "name": "timestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hint", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "isBurnerHook", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "latestSlashedCaptureTimestamp", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "value", + "type": "uint48", + "internalType": "uint48" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "slash", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "captureTimestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hints", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "slashedAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "slashableStake", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + }, + { + "name": "captureTimestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hints", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "staticDelegateCall", + "inputs": [ + { + "name": "target", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "vault", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Slash", + "inputs": [ + { + "name": "subnetwork", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "operator", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "slashedAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "captureTimestamp", + "type": "uint48", + "indexed": false, + "internalType": "uint48" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "CheckpointUnorderedInsertion", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientBurnerGas", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientSlash", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidCaptureTimestamp", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidInitialization", + "inputs": [] + }, + { + "type": "error", + "name": "NoBurner", + "inputs": [] + }, + { + "type": "error", + "name": "NotInitialized", + "inputs": [] + }, + { + "type": "error", + "name": "NotInitializing", + "inputs": [] + }, + { + "type": "error", + "name": "NotNetworkMiddleware", + "inputs": [] + }, + { + "type": "error", + "name": "NotVault", + "inputs": [] + }, + { + "type": "error", + "name": "ReentrancyGuardReentrantCall", + "inputs": [] + }, + { + "type": "error", + "name": "SafeCastOverflowedUintDowncast", + "inputs": [ + { + "name": "bits", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "value", + "type": "uint256", + "internalType": "uint256" + } + ] + } +] \ No newline at end of file diff --git a/src/lib/riContracts/Vault.json b/src/lib/riContracts/Vault.json new file mode 100644 index 0000000..2e0b87f --- /dev/null +++ b/src/lib/riContracts/Vault.json @@ -0,0 +1,1771 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "delegatorFactory", + "type": "address", + "internalType": "address" + }, + { + "name": "slasherFactory", + "type": "address", + "internalType": "address" + }, + { + "name": "vaultFactory", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "DEFAULT_ADMIN_ROLE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "DELEGATOR_FACTORY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "DEPOSITOR_WHITELIST_ROLE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "DEPOSIT_LIMIT_SET_ROLE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "DEPOSIT_WHITELIST_SET_ROLE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FACTORY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "IS_DEPOSIT_LIMIT_SET_ROLE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "SLASHER_FACTORY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activeBalanceOf", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activeBalanceOfAt", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + }, + { + "name": "timestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hints", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activeShares", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activeSharesAt", + "inputs": [ + { + "name": "timestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hint", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activeSharesOf", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activeSharesOfAt", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + }, + { + "name": "timestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hint", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activeStake", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activeStakeAt", + "inputs": [ + { + "name": "timestamp", + "type": "uint48", + "internalType": "uint48" + }, + { + "name": "hint", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "burner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "claim", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "epoch", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "claimBatch", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "epochs", + "type": "uint256[]", + "internalType": "uint256[]" + } + ], + "outputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "collateral", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "currentEpoch", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "currentEpochStart", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint48", + "internalType": "uint48" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "delegator", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "name": "onBehalfOf", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "depositedAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "mintedShares", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "depositLimit", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "depositWhitelist", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "epochAt", + "inputs": [ + { + "name": "timestamp", + "type": "uint48", + "internalType": "uint48" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "epochDuration", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint48", + "internalType": "uint48" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "epochDurationInit", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint48", + "internalType": "uint48" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRoleAdmin", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "grantRole", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "hasRole", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "initialVersion", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "owner_", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "isDelegatorInitialized", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isDepositLimit", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isDepositorWhitelisted", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "value", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isInitialized", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isSlasherInitialized", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isWithdrawalsClaimed", + "inputs": [ + { + "name": "epoch", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "value", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "migrate", + "inputs": [ + { + "name": "newVersion", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "nextEpochStart", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint48", + "internalType": "uint48" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "onSlash", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "captureTimestamp", + "type": "uint48", + "internalType": "uint48" + } + ], + "outputs": [ + { + "name": "slashedAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previousEpochStart", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint48", + "internalType": "uint48" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "redeem", + "inputs": [ + { + "name": "claimer", + "type": "address", + "internalType": "address" + }, + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "withdrawnAssets", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "mintedShares", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "renounceRole", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "callerConfirmation", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "revokeRole", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setDelegator", + "inputs": [ + { + "name": "delegator_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setDepositLimit", + "inputs": [ + { + "name": "limit", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setDepositWhitelist", + "inputs": [ + { + "name": "status", + "type": "bool", + "internalType": "bool" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setDepositorWhitelistStatus", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + }, + { + "name": "status", + "type": "bool", + "internalType": "bool" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setIsDepositLimit", + "inputs": [ + { + "name": "status", + "type": "bool", + "internalType": "bool" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSlasher", + "inputs": [ + { + "name": "slasher_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "slashableBalanceOf", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "slasher", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "staticDelegateCall", + "inputs": [ + { + "name": "target", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "supportsInterface", + "inputs": [ + { + "name": "interfaceId", + "type": "bytes4", + "internalType": "bytes4" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalStake", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "version", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "name": "claimer", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "burnedShares", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "mintedShares", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdrawalShares", + "inputs": [ + { + "name": "epoch", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "withdrawalSharesOf", + "inputs": [ + { + "name": "epoch", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "withdrawals", + "inputs": [ + { + "name": "epoch", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "withdrawalsOf", + "inputs": [ + { + "name": "epoch", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "Claim", + "inputs": [ + { + "name": "claimer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "epoch", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ClaimBatch", + "inputs": [ + { + "name": "claimer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "epochs", + "type": "uint256[]", + "indexed": false, + "internalType": "uint256[]" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Deposit", + "inputs": [ + { + "name": "depositor", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "onBehalfOf", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "shares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OnSlash", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "captureTimestamp", + "type": "uint48", + "indexed": false, + "internalType": "uint48" + }, + { + "name": "slashedAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleAdminChanged", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "previousAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "newAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleGranted", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleRevoked", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetDelegator", + "inputs": [ + { + "name": "delegator", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetDepositLimit", + "inputs": [ + { + "name": "limit", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetDepositWhitelist", + "inputs": [ + { + "name": "status", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetDepositorWhitelistStatus", + "inputs": [ + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "status", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetIsDepositLimit", + "inputs": [ + { + "name": "status", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetSlasher", + "inputs": [ + { + "name": "slasher", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Withdraw", + "inputs": [ + { + "name": "withdrawer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "claimer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "burnedShares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "mintedShares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AccessControlBadConfirmation", + "inputs": [] + }, + { + "type": "error", + "name": "AccessControlUnauthorizedAccount", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + }, + { + "name": "neededRole", + "type": "bytes32", + "internalType": "bytes32" + } + ] + }, + { + "type": "error", + "name": "AddressEmptyCode", + "inputs": [ + { + "name": "target", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "AddressInsufficientBalance", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "AlreadyClaimed", + "inputs": [] + }, + { + "type": "error", + "name": "AlreadyInitialized", + "inputs": [] + }, + { + "type": "error", + "name": "AlreadySet", + "inputs": [] + }, + { + "type": "error", + "name": "CheckpointUnorderedInsertion", + "inputs": [] + }, + { + "type": "error", + "name": "DelegatorAlreadyInitialized", + "inputs": [] + }, + { + "type": "error", + "name": "DepositLimitReached", + "inputs": [] + }, + { + "type": "error", + "name": "FailedInnerCall", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientClaim", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientDeposit", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientRedemption", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientWithdrawal", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidAccount", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidCaptureEpoch", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidClaimer", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidCollateral", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidDelegator", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidEpoch", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidEpochDuration", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidInitialization", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidLengthEpochs", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidOnBehalfOf", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidRecipient", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidSlasher", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidTimestamp", + "inputs": [] + }, + { + "type": "error", + "name": "MathOverflowedMulDiv", + "inputs": [] + }, + { + "type": "error", + "name": "MissingRoles", + "inputs": [] + }, + { + "type": "error", + "name": "NoPreviousEpoch", + "inputs": [] + }, + { + "type": "error", + "name": "NotDelegator", + "inputs": [] + }, + { + "type": "error", + "name": "NotFactory", + "inputs": [] + }, + { + "type": "error", + "name": "NotInitialized", + "inputs": [] + }, + { + "type": "error", + "name": "NotInitializing", + "inputs": [] + }, + { + "type": "error", + "name": "NotSlasher", + "inputs": [] + }, + { + "type": "error", + "name": "NotWhitelistedDepositor", + "inputs": [] + }, + { + "type": "error", + "name": "OwnableInvalidOwner", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "OwnableUnauthorizedAccount", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "ReentrancyGuardReentrantCall", + "inputs": [] + }, + { + "type": "error", + "name": "SafeCastOverflowedUintDowncast", + "inputs": [ + { + "name": "bits", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "value", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "SafeERC20FailedOperation", + "inputs": [ + { + "name": "token", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "SlasherAlreadyInitialized", + "inputs": [] + }, + { + "type": "error", + "name": "TooMuchRedeem", + "inputs": [] + }, + { + "type": "error", + "name": "TooMuchWithdraw", + "inputs": [] + } +] diff --git a/src/lib/riContracts/data.json b/src/lib/riContracts/data.json new file mode 100644 index 0000000..9679c88 --- /dev/null +++ b/src/lib/riContracts/data.json @@ -0,0 +1,30 @@ +{ + "vaults": { + "1": "0x285Ef2547470dc830FB079cbe41B5eDFF56E5868", + "2": "0xBA8bB5409a4E3ec92ea33f0A4a2872f1D2B9Ff52", + "3": "0x99D7544BaF70D5Dc7356E48917809f5CAD8db4B4", + "4": "0x9eD949a2F30fAcED19eACe8c86d683dE847e29c2" + }, + "delegators": { + "1": "0x6c2C4eeFff7b0218d5219f9b129f814a4392175d", + "2": "0xC77DA7aac64eEDD7d686ef3A2A74cb922a094Ee2", + "3": "0xf606703b8e346dc19662aa671EBE14D8887CD7B7", + "4": "0x43A6A228e394AeC2447398f4C1a604e18f39E227" + }, + "slashers": { + "1": "0x89C4fad2b403A12B92E692B01Ede13C251DB30e0", + "2": "0x7445019336d2Bcc64fc365092d3eB407b8F1fbB2", + "3": "0xb3e82C4F27a18ABF4E50b81914CF3d45B2478d6a", + "4": "0x82013332E6c36D327138025E2744783F1671D49a" + }, + "assets": { + "0": { + "address": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "decimals": 18, + "symbol": "wstETH", + "abi": ["function stEthPerToken() view returns (uint256)"], + "method": "stEthPerToken", + "protocolAssetCorrelationId": 0 + } + } +} diff --git a/src/lib/riContracts/index.js b/src/lib/riContracts/index.js new file mode 100644 index 0000000..da3055d --- /dev/null +++ b/src/lib/riContracts/index.js @@ -0,0 +1,29 @@ +const { ethers } = require('ethers'); + +const data = require('./data.json'); +const delegator = require('./Delegator.json'); +const slasher = require('./Slasher.json'); +const vault = require('./Vault.json'); + +module.exports = provider => { + const { vaults, delegators, slashers, assets } = data; + + console.log(vaults); + const symbiotic = {}; + + for (const key in vaults) { + symbiotic[`vault_${key}`] = new ethers.Contract(vaults[key], vault, provider); + symbiotic[`delegator_${key}`] = new ethers.Contract(delegators[key], delegator, provider); + symbiotic[`slasher_${key}`] = new ethers.Contract(slashers[key], slasher, provider); + } + + for (const key in assets) { + const contract = new ethers.Contract(assets[key].address, assets[key].abi, provider); + symbiotic[`asset_${key}`] = { + getRate: async () => contract[assets[key].method](), + protocolAssetCorrelationId: assets[key].protocolAssetCorrelationId, + }; + } + + return symbiotic; +}; diff --git a/src/lib/signQuote.js b/src/lib/signQuote.js new file mode 100644 index 0000000..515f235 --- /dev/null +++ b/src/lib/signQuote.js @@ -0,0 +1,88 @@ +const { addresses } = require('@nexusmutual/deployments'); +const { AwsKmsSigner } = require('@nexusmutual/ethers-v5-aws-kms-signer'); +const ethers = require('ethers'); + +const config = require('../config'); + +const { keccak256 } = ethers.utils; + +const domain = { + name: 'NexusMutualCover', + version: '1.0.0', + chainId: 1, + verifyingContract: addresses.Cover, +}; + +const types = { + RiQuote: [ + { name: 'coverId', type: 'uint256' }, + { name: 'productId', type: 'uint24' }, + { name: 'providerId', type: 'uint256' }, + { name: 'amount', type: 'uint256' }, + { name: 'premium', type: 'uint256' }, + { name: 'period', type: 'uint32' }, + { name: 'coverAsset', type: 'uint8' }, + { name: 'data', type: 'bytes32' }, + { name: 'dataFormat', type: 'uint8' }, + { name: 'deadline', type: 'uint32' }, + { name: 'nonce', type: 'uint256' }, + ], +}; + +const getSigner = () => { + const provider = new ethers.providers.JsonRpcProvider(config.get('providerUrl')); + + if ( + config.get('awsAccessKeyId') && + config.get('awsSecretAccessKey') && + config.get('awsKmsKeyId') && + config.get('awsRegion') + ) { + console.info('Using AWS KMS signer'); + return new AwsKmsSigner(config.get('awsKmsKeyId'), config.get('awsRegion'), provider); + } + + throw new Error('Could not get signer. AWS/KMS env vars must be set'); +}; + +const signRiQuote = async quote => { + const signer = getSigner(); + + const params = { + coverId: quote.coverId ?? 0, + productId: quote.productId, + providerId: quote.providerId, + amount: quote.amount, + premium: quote.premium, + period: quote.period, + coverAsset: quote.coverAsset, + nonce: quote.nonce, + data: keccak256(quote.data), + dataFormat: quote.dataFormat, + deadline: quote.deadline, + }; + + return signer._signTypedData(domain, types, params); +}; + +module.exports = { getSigner, signRiQuote }; + +if (require.main === module) { + (async () => { + const testMessage = 'Hello, mutants!'; + + // get signer and sign message + const [signature, ethAddress] = await Promise.all([getSigner().signMessage(testMessage), getSigner().getAddress()]); + + // recover address from signature + const eip191Hash = ethers.utils.hashMessage(testMessage); + const recoveredAddress = ethers.utils.recoverAddress(eip191Hash, signature); + + if (recoveredAddress !== ethAddress) { + throw new Error(`Recovered address ${recoveredAddress} does not match signer address ${ethAddress}`); + } + + console.log(`Recovered address matches signature address (${recoveredAddress}) ✅`); + process.exit(); + })().catch(console.error); +} diff --git a/src/lib/synchronizer.js b/src/lib/synchronizer.js index b7c62dc..a16bd2f 100644 --- a/src/lib/synchronizer.js +++ b/src/lib/synchronizer.js @@ -1,5 +1,7 @@ -const { FETCH_COVER_DATA_FROM_ID } = require('./constants'); -const { calculateTrancheId, promiseAllInBatches } = require('./helpers'); +const { ethers } = require('ethers'); + +const { FETCH_COVER_DATA_FROM_ID, RI_FETCH_COVER_DATA_FROM_BLOCK } = require('./constants'); +const { calculateTrancheId, promiseAllInBatches, decodeRiData } = require('./helpers'); const config = require('../config'); const { SET_ASSET_RATE, @@ -10,8 +12,17 @@ const { SET_COVER, SET_COVER_REFERENCE, RESET_PRODUCT_POOLS, + SET_RI_ASSET_RATE, + SET_RI_VAULT_PRODUCT, + SET_RI_VAULT_PRODUCTS, + SET_RI_EPOCH_EXPIRIES, + SET_VAULT_STAKE, + SET_RI_NONCE, } = require('../store/actions'); +const { BigNumber } = ethers; +const { WeiPerEther } = ethers.constants; + module.exports = async (store, chainApi, eventsApi) => { const updateProduct = async productId => { const { globalCapacityRatio } = store.getState(); @@ -90,6 +101,8 @@ module.exports = async (store, chainApi, eventsApi) => { const rate = await chainApi.fetchTokenPriceInAsset(assetId); store.dispatch({ type: SET_ASSET_RATE, payload: { assetId, rate } }); } + + await updateRiAssetNXMRates(); console.info('Update: Asset rates'); }; @@ -116,18 +129,163 @@ module.exports = async (store, chainApi, eventsApi) => { console.info(`Update: Cover reference for cover id ${coverId}`); }; + const updateRiVaultProductAllocations = async (coverId, data, dataFormat) => { + const allocations = decodeRiData(data, dataFormat); + const { vaultProducts } = store.getState(); + const { productId, originalCoverId, start, period } = await chainApi.fetchCover(coverId); + const now = Math.floor(Date.now() / 1000); + for (const allocation of allocations) { + const { amount, vaultId } = allocation; + const vaultProductId = `${productId}_${vaultId}`; + const { allocations } = vaultProducts[vaultProductId]; + + const newAllocations = allocations.filter( + allocation => allocation.originalCoverId !== originalCoverId && allocation.expiryTimestamp.gt(now), + ); + + store.dispatch({ + type: SET_RI_VAULT_PRODUCT, + payload: { + vaultProductId, + allocations: [ + ...newAllocations, + { amount, coverId, expiryTimestamp: BigNumber.from(start).add(period), originalCoverId }, + ], + }, + }); + } + console.info('Update: RI vault products'); + }; + + const updateRiAssetNXMRates = async () => { + const { riAssets, assetRates } = store.getState(); + const assetIds = Object.keys(riAssets); + + for (const assetId of assetIds) { + const { assetRate, protocolAssetCorrelationId } = await chainApi.fetchRiAssetRate(assetId); + const internalAssetRate = assetRates[protocolAssetCorrelationId]; + const rate = assetRate.mul(internalAssetRate).div(WeiPerEther); + store.dispatch({ type: SET_RI_ASSET_RATE, payload: { assetId, rate } }); + } + console.info('Update: RI asset rates'); + }; + + const updateEpoch = async timestamp => { + const { epochExpires } = store.getState(); + const expiredEpochs = Object.entries(epochExpires).filter(([key, value]) => value <= timestamp); + const expiries = {}; + + for (const epochExpiration of expiredEpochs) { + const [vaultId] = epochExpiration; + expiries[vaultId] = await chainApi.fetchVaultNextEpochStart(vaultId); + await updateRiVaultCapacity(vaultId); + } + + store.dispatch({ type: SET_RI_EPOCH_EXPIRIES, payload: { expiries } }); + }; + + const updateRiVaultCapacity = async vaultId => { + const { riSubnetworks } = store.getState(); + const productIds = Object.values(riSubnetworks).reduce((acc, { products }) => { + const subnetworkProductIds = Object.keys(products); + return [...new Set([...acc, ...subnetworkProductIds])]; + }, []); + + // Calculate activeStake for each product based on its weight in parallel + const [withdrawalAmount, ...stakeResults] = await Promise.all([ + chainApi.fetchVaultWithdrawals(vaultId), + ...productIds.map(productId => + chainApi + .fetchVaultStake(vaultId, Object.keys(riSubnetworks), productId, riSubnetworks) + .then(activeStake => ({ productId, activeStake })), + ), + ]); + + // Build productStakes object from results + const productStakes = {}; + stakeResults.forEach(({ productId, activeStake }) => { + productStakes[productId] = activeStake; + }); + + store.dispatch({ + type: SET_VAULT_STAKE, + payload: { vaultId, productIds, productStakes, withdrawalAmount }, + }); + }; + + const updatesOnBlockMined = async (blockNumber, blockTimestamp) => { + return Promise.all([updateAssetRates(), updateEpoch(blockTimestamp)]); + }; + + const updateRiData = async () => { + const allAllocations = await chainApi.fetchVaultAllocations(RI_FETCH_COVER_DATA_FROM_BLOCK); + const { riSubnetworks } = store.getState(); + const vaultProducts = {}; + for (const subnetwork of Object.values(riSubnetworks)) { + const { vaults, products } = subnetwork; + for (const vaultId of vaults) { + const withdrawalAmount = await chainApi.fetchVaultWithdrawals(vaultId); + for (const product of Object.values(products)) { + // Calculate activeStake for each product based on its weight + const activeStake = await chainApi.fetchVaultStake( + vaultId, + Object.keys(riSubnetworks), + product.productId, + riSubnetworks, + ); + const key = `${product.productId}_${vaultId}`; + vaultProducts[key] = { + vaultId, + product: product.productId, + allocations: allAllocations[key] || [], + price: product.price, + activeStake, + withdrawalAmount, + }; + } + } + } + store.dispatch({ type: SET_RI_VAULT_PRODUCTS, payload: { vaultProducts } }); + }; + + const updateRiNonce = async providerId => { + const { riNonces } = store.getState(); + const nonce = riNonces[providerId] + 1; + + store.dispatch({ type: SET_RI_NONCE, payload: { providerId, nonce } }); + }; + eventsApi.on('pool:change', updatePool); eventsApi.on('cover:bought', updateCover); eventsApi.on('cover:edit', updateCoverReference); eventsApi.on('product:change', updateProduct); eventsApi.on('tranche:change', updateAll); eventsApi.on('bucket:change', updateAll); - eventsApi.on('block', updateAssetRates); + eventsApi.on('block', updatesOnBlockMined); + // RI vault updates + eventsApi.on('ri:bought', updateRiVaultProductAllocations); + eventsApi.on('ri:withdraw', updateRiVaultCapacity); + eventsApi.on('ri:deposit', updateRiVaultCapacity); + eventsApi.on('ri:slash', updateRiVaultCapacity); + eventsApi.on('ri:setMaxNetworkLimit', updateRiVaultCapacity); + eventsApi.on('ri:setNetworkLimit', updateRiVaultCapacity); + eventsApi.on('ri:setOperatorNetworkShares', updateRiVaultCapacity); + eventsApi.on('ri:setOperatorNetworkLimit', updateRiVaultCapacity); + // NOTE on RI events related to capacity updates + // operator-vault/network opt-in changes can also affect capacity, but are not event-driven here + // since Nexus controls the Nexus operator for onboarded Nexus vaults, refresh capacity manually after opt-in changes return { updateAll, updateAssetRates, updateCover, updateCoverReference, + updateEpoch, + updateRiAssetNXMRates, + updateRiVaultProductAllocations, + updateRiVaultCapacity, + updatesOnBlockMined, + updateRiData, + updateRiNonce, }; }; diff --git a/src/routes/quote.js b/src/routes/quote.js index d978735..b4f4bbc 100644 --- a/src/routes/quote.js +++ b/src/routes/quote.js @@ -3,6 +3,7 @@ const express = require('express'); const { asyncRoute } = require('../lib/helpers'); const { quoteEngine } = require('../lib/quoteEngine'); +const { signRiQuote } = require('../lib/signQuote'); const { selectAsset } = require('../store/selectors'); const router = express.Router(); @@ -117,10 +118,13 @@ router.get( const amount = BigNumber.from(req.query.amount); const period = BigNumber.from(req.query.period).mul(24 * 3600); const coverAsset = Number(req.query.coverAsset); + const paymentAsset = Number(req.query.paymentAsset); const editedCoverId = req.query.coverEditId ? Number(req.query.coverEditId) : 0; + // make default true and use falsy values + const useRiQuote = req.query.useRiQuote ?? true; const store = req.app.get('store'); - const route = quoteEngine(store, productId, amount, period, coverAsset, editedCoverId); + const route = quoteEngine(store, productId, amount, period, coverAsset, editedCoverId, paymentAsset, useRiQuote); const poolAllocationRequests = route.poolsWithPremium.reduce((poolAllocationRequests, pool) => { return [ @@ -132,6 +136,8 @@ router.get( ]; }, []); + const signature = route.riQuote ? await signRiQuote(route.riQuote) : null; + return { body: { quote: { @@ -145,6 +151,7 @@ router.get( premiumInAssetWithRefund: route.premiumInAssetWithRefund.toString(), poolAllocationRequests, asset: selectAsset(store, coverAsset), + riRequest: route.riQuote ? { ...route.riQuote, signature } : null, }, }, }; diff --git a/src/store/actions.js b/src/store/actions.js index d47ff9b..c377eac 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -7,4 +7,11 @@ module.exports = { SET_COVER: 'SET_COVER', SET_COVER_REFERENCE: 'SET_COVER_REFERENCE', RESET_PRODUCT_POOLS: 'RESET_PRODUCT_POOLS', + SET_RI_ASSET_RATE: 'SET_RI_ASSET_RATE', + SET_RI_VAULT_PRODUCT: 'SET_RI_VAULT_PRODUCT', + SET_RI_VAULT_PRODUCTS: 'SET_RI_VAULT_PRODUCTS', + SET_RI_EPOCH_EXPIRES: 'SET_RI_EPOCH_EXPIRES', + SET_VAULT_STAKE: 'SET_VAULT_STAKE', + SET_RI_EPOCH_EXPIRIES: 'SET_RI_EPOCH_EXPIRIES', + SET_RI_NONCE: 'SET_RI_NONCE', }; diff --git a/src/store/cache.js b/src/store/cache.js index 10970b3..31aa113 100644 --- a/src/store/cache.js +++ b/src/store/cache.js @@ -43,6 +43,7 @@ const load = defaultState => { // refresh constants values parsedData.assets = { ...defaultState.assets }; parsedData.productPriorityPoolsFixedPrice = { ...defaultState.productPriorityPoolsFixedPrice }; + parsedData.riSubnetworks = { ...defaultState.riSubnetworks }; return parsedData; }; diff --git a/src/store/reducer.js b/src/store/reducer.js index 38aff43..ad01185 100644 --- a/src/store/reducer.js +++ b/src/store/reducer.js @@ -7,7 +7,14 @@ const { SET_COVER, SET_COVER_REFERENCE, RESET_PRODUCT_POOLS, + SET_RI_ASSET_RATE, + SET_RI_VAULT_PRODUCT, + SET_RI_VAULT_PRODUCTS, + SET_RI_EPOCH_EXPIRIES, + SET_VAULT_STAKE, + SET_RI_NONCE, } = require('./actions'); +const riSubnetworks = require('./riSubnetworks.json'); const config = require('../config'); // Automatically populate productPriorityPoolsFixedPrice @@ -19,6 +26,7 @@ for (const [productId, orderArray] of Object.entries(customPriorityPoolsOrder)) } const initialState = { + riSubnetworks, assetRates: {}, // assetId -> rate assets: { 0: { id: 0, symbol: 'ETH', decimals: 18 }, @@ -27,6 +35,10 @@ const initialState = { 7: { id: 7, symbol: 'cbBTC', decimals: 8 }, 255: { id: 255, symbol: 'NXM', decimals: 18 }, }, + riAssetRates: {}, // assetId -> rate + riAssets: { + 0: { id: 0, symbol: 'wstETH', decimals: 18 }, + }, globalCapacityRatio: 0, poolProducts: {}, // {productId}_{poolId} -> { allocations, trancheCapacities } productPoolIds: {}, // productId -> [ poolIds ] @@ -34,6 +46,9 @@ const initialState = { covers: {}, // coverId -> { cover } productPriorityPoolsFixedPrice, trancheId: 0, + vaultProducts: {}, // {productId}_{vaultId} -> { allocations, activeStake, withdrawalAmount, price } + epochExpires: {}, // vaultId -> timestamp + riNonces: {}, }; function reducer(state = initialState, { type, payload }) { @@ -97,6 +112,50 @@ function reducer(state = initialState, { type, payload }) { return { ...state, productPoolIds, poolProducts }; } + if (type === SET_RI_ASSET_RATE) { + const { assetId, rate } = payload; + const riAssetRates = { ...state.riAssetRates, [assetId]: rate }; + return { ...state, riAssetRates }; + } + + if (type === SET_RI_VAULT_PRODUCT) { + const { vaultProductId, allocations } = payload; + const vaultProduct = state.vaultProducts[vaultProductId]; + const vaultProducts = { ...state.vaultProducts, [vaultProductId]: { ...vaultProduct, allocations } }; + return { ...state, vaultProducts }; + } + + if (type === SET_VAULT_STAKE) { + const { vaultId, productIds, productStakes, withdrawalAmount } = payload; + const newVaultProducts = {}; + for (const productId of productIds) { + const key = `${productId}_${vaultId}`; + newVaultProducts[key] = { + ...state.vaultProducts[key], + activeStake: productStakes[productId], + withdrawalAmount, + }; + } + const vaultProducts = { ...state.vaultProducts, ...newVaultProducts }; + return { ...state, vaultProducts }; + } + + if (type === SET_RI_EPOCH_EXPIRIES) { + const { expiries } = payload; + + return { ...state, epochExpires: { ...state.epochExpires, ...expiries } }; + } + + if (type === SET_RI_VAULT_PRODUCTS) { + const { vaultProducts } = payload; + return { ...state, vaultProducts: { ...state.vaultProducts, ...vaultProducts } }; + } + + if (type === SET_RI_NONCE) { + const { providerId, nonce } = payload; + return { ...state, riNonces: { ...state.riNonces, [providerId]: nonce } }; + } + return state; } diff --git a/src/store/riSubnetworks.json b/src/store/riSubnetworks.json new file mode 100644 index 0000000..14f354f --- /dev/null +++ b/src/store/riSubnetworks.json @@ -0,0 +1,65 @@ +{ + "0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000": { + "products": { + "1": { + "productId": 1, + "price": 500, + "weight": 100, + "riCoverAmountPercentage": 80 + }, + "2": { + "productId": 5, + "price": 500, + "weight": 25, + "riCoverAmountPercentage": 100 + }, + "3": { + "productId": 8, + "price": 500, + "weight": 25 + }, + "4": { + "productId": 11, + "price": 500, + "weight": 25 + }, + "5": { + "productId": 25, + "price": 500, + "weight": 25 + } + }, + "vaults": [ + "1", + "2", + "3" + ], + "maxLimit": 50000000, + "limit": 50000000 + }, + "0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000001": { + "products": { + "1": { + "productId": 1, + "price": 500, + "weight": 100 + }, + "3": { + "productId": 21, + "price": 500, + "weight": 25 + }, + "5": { + "productId": 22, + "price": 500, + "weight": 25 + } + }, + "vaults": [ + "2", + "3" + ], + "maxLimit": 50000000, + "limit": 50000000 + } +} diff --git a/src/store/selectors.js b/src/store/selectors.js index c24deac..1bd3fde 100644 --- a/src/store/selectors.js +++ b/src/store/selectors.js @@ -1,3 +1,5 @@ +const { BigNumber } = require('ethers'); + const selectAssetRate = (store, assetId) => { const { assetRates } = store.getState(); return assetRates[assetId]; @@ -60,6 +62,79 @@ function selectProductsInPool(store, poolId) { }); } +const selectActiveCoverAmount = (store, productId, now) => { + const { covers } = store.getState(); + const nowBN = BigNumber.isBigNumber(now) ? now : BigNumber.from(now); + return Object.values(covers).reduce((acc, cover) => { + const coverEnd = BigNumber.from(cover.start).add(cover.period); + const isStillActive = nowBN.lt(coverEnd); + + if (isStillActive && cover.productId === productId) { + for (const pool of cover.poolAllocations) { + const coverAmount = BigNumber.isBigNumber(pool.coverAmountInNxm) + ? pool.coverAmountInNxm + : BigNumber.from(pool.coverAmountInNxm); + acc = acc.add(coverAmount); + } + } + return acc; + }, BigNumber.from(0)); +}; + +const selectRiAssetRate = (store, assetId) => { + const { riAssetRates } = store.getState(); + return riAssetRates[assetId]; +}; + +const selectVaultProducts = (store, productId, vaultId = null) => { + const { riSubnetworks = {}, vaultProducts = {} } = store.getState(); + + if (vaultId !== null && vaultId !== undefined) { + const key = `${productId}_${vaultId}`; + return vaultProducts[key] || null; + } + + const vaultsIdsSet = new Set(); + for (const subnetwork of Object.values(riSubnetworks)) { + const vaults = subnetwork.vaults; + if (Object.keys(subnetwork.products).includes(String(productId))) { + vaults.forEach(vaultId => vaultsIdsSet.add(vaultId)); + } + } + const vaultsIds = Array.from(vaultsIdsSet); + + return vaultsIds.map(vaultId => vaultProducts[`${productId}_${vaultId}`]).filter(Boolean); +}; + +const selectVaultEpochExpiryTimestamp = store => { + const { epochExpires = {} } = store.getState(); + return epochExpires; +}; + +/** + * Gets the RI cover amount percentage for a product from riSubnetworks. + * Returns the percentage from the first subnetwork that contains the product, + * or null if not found (will default to RI_COVER_AMOUNT_PERCENTAGE constant). + * + * @param {Object} store - The Redux store containing application state. + * @param {number|string} productId - The product ID. + * @returns {number|null} The RI cover amount percentage (0-100) or null if not found. + */ +const selectRiCoverAmountPercentage = (store, productId) => { + const { riSubnetworks = {} } = store.getState(); + + for (const subnetwork of Object.values(riSubnetworks)) { + if (subnetwork.products && subnetwork.products[String(productId)]) { + const product = subnetwork.products[String(productId)]; + if (product.riCoverAmountPercentage !== undefined) { + return product.riCoverAmountPercentage; + } + } + } + + return null; +}; + module.exports = { selectAssetRate, selectAsset, @@ -68,4 +143,9 @@ module.exports = { selectProductPools, selectProductPriorityPoolsFixedPrice, selectProductsInPool, + selectRiAssetRate, + selectVaultProducts, + selectActiveCoverAmount, + selectVaultEpochExpiryTimestamp, + selectRiCoverAmountPercentage, }; diff --git a/test/mocks/chainApi.js b/test/mocks/chainApi.js index 3f4d1a0..56eff4c 100644 --- a/test/mocks/chainApi.js +++ b/test/mocks/chainApi.js @@ -1,3 +1,5 @@ +const { BigNumber } = require('ethers'); + const data = require('./store'); const chainApiMock = { @@ -62,6 +64,14 @@ const chainApiMock = { fetchCoverPoolTrancheAllocations: async (coverId, poolId, allocationId) => { return data.covers[coverId].poolAllocations.find(p => p.poolId === poolId).packedTrancheAllocations; }, + + fetchRiAssetRate: async assetId => { + const rate = data.riAssetRates[assetId]; + return { + assetRate: BigNumber.from(rate), + protocolAssetCorrelationId: 0, + }; + }, }; module.exports = chainApiMock; diff --git a/test/mocks/store.js b/test/mocks/store.js index 8dda0ab..ddf281b 100644 --- a/test/mocks/store.js +++ b/test/mocks/store.js @@ -1,5 +1,4 @@ const { BigNumber, ethers } = require('ethers'); -const { parseEther } = ethers.utils; process.env.PRIORITY_POOLS_ORDER_186 = '18,22,1'; process.env.PRIORITY_POOLS_ORDER_195 = '1,23,22,2,5'; @@ -7,8 +6,10 @@ process.env.PRIORITY_POOLS_ORDER_196 = '1,23,22,2,5'; process.env.PRIORITY_POOLS_ORDER_227 = '8,23,22,2,1,5'; process.env.PRIORITY_POOLS_ORDER_233 = '22,2,1,23'; -// require config after setting env variable +const { parseEther } = ethers.utils; const config = require('../../src/config'); +const { RI_EPOCH_DURATION } = require('../../src/lib/constants'); +// require config after setting env variable const store = { assetRates: { @@ -533,6 +534,136 @@ const store = { ], }, }, + riSubnetworks: { + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000': { + products: { + 1: { productId: 1, price: 500, weight: 100, riCoverAmountPercentage: 80 }, + 2: { productId: 2, price: 500, weight: 25 }, + 3: { productId: 3, price: 500, weight: 25 }, + 4: { productId: 4, price: 500, weight: 25 }, + 5: { productId: 5, price: 500, weight: 25, riCoverAmountPercentage: 100 }, + }, + vaults: ['1'], + }, + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000001': { + products: { + 1: { productId: 1, price: 500, weight: 100 }, + 2: { productId: 2, price: 500, weight: 25 }, + 4: { productId: 4, price: 500, weight: 25 }, + }, + vaults: ['2', '3'], + }, + }, + vaultProducts: { + '0_1': { + vaultId: '1', + product: 0, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x0d3a51a84e0439ce65'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '1_1': { + vaultId: '1', + product: 1, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x34e946a13810e73994'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '2_1': { + vaultId: '1', + product: 2, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x0d3a51a84e0439ce65'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '3_1': { + vaultId: '1', + product: 3, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x0d3a51a84e0439ce65'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '4_1': { + vaultId: '1', + product: 4, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x0d3a51a84e0439ce65'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '1_2': { + vaultId: '2', + product: 1, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x557906d5e9daf7b31c'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '2_2': { + vaultId: '2', + product: 2, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x155e41b57a76bdecc7'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '4_2': { + vaultId: '2', + product: 4, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x155e41b57a76bdecc7'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '1_3': { + vaultId: '3', + product: 1, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x056bc75e2d63100000'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '2_3': { + vaultId: '3', + product: 2, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x015af1d78b58c40000'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + '4_3': { + vaultId: '3', + product: 4, + allocations: [], + price: 500, + activeStake: BigNumber.from('0x155e41b57a76bdecc7'), + withdrawalAmount: BigNumber.from(0), + asset: 0, + }, + }, + riAssetRates: { + 0: parseEther('1.2'), // 1 wstETH = 1.2 NXM + }, + epochExpires: { + 1: BigNumber.from(Math.floor(Date.now() / 1000)).add(RI_EPOCH_DURATION * 24 * 3600 + 365 * 24 * 3600), + 2: BigNumber.from(Math.floor(Date.now() / 1000)).add(RI_EPOCH_DURATION * 24 * 3600 + 365 * 24 * 3600), + 3: BigNumber.from(Math.floor(Date.now() / 1000)).add(RI_EPOCH_DURATION * 24 * 3600 + 365 * 24 * 3600), + }, + riNonces: {}, }; module.exports = store; diff --git a/test/unit/cache.js b/test/unit/cache.js index 98ebd88..ac5d5e6 100644 --- a/test/unit/cache.js +++ b/test/unit/cache.js @@ -27,7 +27,7 @@ describe('cache', function () { const state = { test: 'test2' }; fs.writeFileSync(stateFile, JSON.stringify(state, null, 2)); const loadedState = load(defaultState); - expect(loadedState).to.deep.equal({ ...state, assets: {}, productPriorityPoolsFixedPrice: {} }); + expect(loadedState).to.deep.equal({ ...state, assets: {}, productPriorityPoolsFixedPrice: {}, riSubnetworks: {} }); }); it('save should write the state to the file', function () { diff --git a/test/unit/capacityEngine.js b/test/unit/capacityEngine.js index 08c0f9a..679182d 100644 --- a/test/unit/capacityEngine.js +++ b/test/unit/capacityEngine.js @@ -12,7 +12,12 @@ const { calculatePoolUtilizationRate, calculateProductCapacity, } = require('../../src/lib/capacityEngine'); -const { MAX_COVER_PERIOD, SECONDS_PER_DAY, NXM_PER_ALLOCATION_UNIT } = require('../../src/lib/constants'); +const { + MAX_COVER_PERIOD, + SECONDS_PER_DAY, + NXM_PER_ALLOCATION_UNIT, + RI_EPOCH_DURATION, +} = require('../../src/lib/constants'); const { calculateAvailableCapacityInNXM, calculateBasePrice, @@ -22,6 +27,11 @@ const { calculateTrancheId, bufferedCapacityInNxm, } = require('../../src/lib/helpers'); +const { + selectVaultProducts, + selectRiAssetRate, + selectVaultEpochExpiryTimestamp, +} = require('../../src/store/selectors'); const mockStore = require('../mocks/store'); const { BigNumber } = ethers; @@ -363,8 +373,41 @@ describe('capacityEngine', function () { return total.add(availableCapacity); }, Zero); + // Add RI capacity if product is in riSubnetworks + const period = SECONDS_PER_DAY.mul(30); + const coverExpiry = now.add(storeProduct.gracePeriod).add(period); + const epochDuration = RI_EPOCH_DURATION * 24 * 3600; + const riVaults = selectVaultProducts(store, product.productId); + const expiries = selectVaultEpochExpiryTimestamp(store); + + const totalRiCapacity = riVaults + .filter(vault => vault && expiries[vault.vaultId] && expiries[vault.vaultId].add(epochDuration).gt(coverExpiry)) + .reduce((total, vault) => { + const assetRate = selectRiAssetRate(store, vault.asset); + if (!assetRate) { + return total; + } + const allocatedAmount = (vault.allocations || []).reduce((acc, allocation) => { + if (allocation.expiryTimestamp > now && allocation.active) { + acc = acc.add(BigNumber.from(allocation.amount)); + } + return acc; + }, Zero); + const activeStake = BigNumber.isBigNumber(vault.activeStake) + ? vault.activeStake + : BigNumber.from(vault.activeStake || 0); + const withdrawalAmount = BigNumber.isBigNumber(vault.withdrawalAmount) + ? vault.withdrawalAmount + : BigNumber.from(vault.withdrawalAmount || 0); + const availableCapacityInAsset = activeStake.add(withdrawalAmount).sub(allocatedAmount); + const availableCapacityInNXM = availableCapacityInAsset.mul(assetRate).div(WeiPerEther); + return total.add(availableCapacityInNXM); + }, Zero); + + const expectedTotalCapacity = expectedAvailableNXM.add(totalRiCapacity); + const nxmCapacity = product.availableCapacity.find(c => c.assetId === 255); - expect(nxmCapacity.amount.toString()).to.equal(expectedAvailableNXM.toString()); + expect(nxmCapacity.amount.toString()).to.equal(expectedTotalCapacity.toString()); expect(nxmCapacity.asset).to.deep.equal(assets[255]); const expectedUsedCapacity = poolIds.reduce((total, poolId) => { @@ -729,6 +772,41 @@ describe('capacityEngine', function () { ); return total.add(poolCapacity); }, Zero); + + // Add RI capacity if product is in riSubnetworks + const coverExpiry = now.add(products[expectedProductId].gracePeriod).add(period); + const epochDuration = RI_EPOCH_DURATION * 24 * 3600; + const riVaults = selectVaultProducts(store, Number(expectedProductId)); + const expiries = selectVaultEpochExpiryTimestamp(store); + + const totalRiCapacity = riVaults + .filter( + vault => + vault && expiries[vault.vaultId] && expiries[vault.vaultId].add(epochDuration).gt(coverExpiry), + ) + .reduce((total, vault) => { + const assetRate = selectRiAssetRate(store, vault.asset); + if (!assetRate) { + return total; + } + const allocatedAmount = (vault.allocations || []).reduce((acc, allocation) => { + if (allocation.expiryTimestamp > now && allocation.active) { + acc = acc.add(BigNumber.from(allocation.amount)); + } + return acc; + }, Zero); + const activeStake = BigNumber.isBigNumber(vault.activeStake) + ? vault.activeStake + : BigNumber.from(vault.activeStake || 0); + const withdrawalAmount = BigNumber.isBigNumber(vault.withdrawalAmount) + ? vault.withdrawalAmount + : BigNumber.from(vault.withdrawalAmount || 0); + const availableCapacityInAsset = activeStake.add(withdrawalAmount).sub(allocatedAmount); + const availableCapacityInNXM = availableCapacityInAsset.mul(assetRate).div(WeiPerEther); + return total.add(availableCapacityInNXM); + }, Zero); + + expectedAmount = expectedAmount.add(totalRiCapacity); } } else { // For other assets, convert from NXM using asset rate @@ -816,4 +894,154 @@ describe('capacityEngine', function () { expect(poolCapacityResponse.utilizationRate.toString()).to.equal(expectedUtilizationRate.toString()); }); }); + + describe('RI Capacity Integration', function () { + const { assets, assetRates } = mockStore; + const now = getCurrentTimestamp(); + const period = SECONDS_PER_DAY.mul(30); + + const calculatePoolCapacity = (poolProduct, firstUsableTrancheIndex = 0) => { + return calculateAvailableCapacityInNXM( + poolProduct.trancheCapacities, + poolProduct.allocations, + firstUsableTrancheIndex, + ); + }; + + it('should include RI capacity when calculating product capacity', function () { + const productId = '1'; + const riStore = { + getState: () => ({ + ...mockStore, + riSubnetworks: { + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000': { + products: { + 1: { + productId: 1, + price: 500, + weight: 25, + }, + }, + vaults: ['1', '2'], + }, + }, + vaultProducts: { + '1_1': { + vaultId: '1', + product: 1, + allocations: [], + price: 500, + activeStake: BigNumber.from(parseEther('1000')), // 1000 in vault asset units + withdrawalAmount: BigNumber.from(parseEther('100')), + asset: 0, // wstETH + }, + '1_2': { + vaultId: '2', + product: 1, + allocations: [], + price: 500, + activeStake: BigNumber.from(parseEther('500')), + withdrawalAmount: BigNumber.from(parseEther('50')), + asset: 0, + }, + }, + riAssetRates: { + 0: BigNumber.from(parseEther('1.2')), // 1 wstETH = 1.2 NXM + }, + epochExpires: { + 1: now.add(RI_EPOCH_DURATION * 24 * 3600 + period + 100), + 2: now.add(RI_EPOCH_DURATION * 24 * 3600 + period + 100), + }, + }), + }; + + const response = calculateProductCapacity(riStore, productId, { + period, + now, + assets, + assetRates, + }); + + // Calculate expected RI capacity + // Vault 1: (1000 + 100) * 1.2 = 1320 NXM + // Vault 2: (500 + 50) * 1.2 = 660 NXM + // Total RI: 1980 NXM + const expectedRiCapacity = parseEther('1980'); + + // Get pool capacity from mockStore (which is included in riStore) + const state = riStore.getState(); + const firstUsableTrancheIndex = calculateFirstUsableTrancheIndex( + now, + state.products[productId].gracePeriod, + period, + ); + const pool1Capacity = calculatePoolCapacity(state.poolProducts['1_1'], firstUsableTrancheIndex); + const pool2Capacity = calculatePoolCapacity(state.poolProducts['1_2'], firstUsableTrancheIndex); + const totalPoolCapacity = pool1Capacity.add(pool2Capacity); + + // Total should be pool + RI + const expectedTotal = totalPoolCapacity.add(expectedRiCapacity); + + const nxmCapacity = response.availableCapacity.find(c => c.assetId === 255); + expect(nxmCapacity.amount.toString()).to.equal(expectedTotal.toString()); + }); + + it('should exclude RI capacity when poolId is specified', function () { + const productId = '1'; + const poolId = 1; + const riStore = { + getState: () => ({ + ...mockStore, + riSubnetworks: { + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000': { + products: { + 1: { + productId: 1, + price: 500, + weight: 25, + }, + }, + vaults: ['1'], + }, + }, + vaultProducts: { + '1_1': { + vaultId: '1', + product: 1, + allocations: [], + price: 500, + activeStake: BigNumber.from(parseEther('1000')), + withdrawalAmount: BigNumber.from(parseEther('100')), + asset: 0, + }, + }, + riAssetRates: { + 0: BigNumber.from(parseEther('1.2')), + }, + epochExpires: { + 1: now.add(RI_EPOCH_DURATION * 24 * 3600 + period + 100), + }, + }), + }; + + const response = calculateProductCapacity(riStore, productId, { + poolId, + period, + now, + assets, + assetRates, + }); + + // Should only include pool capacity, not RI + const state = riStore.getState(); + const firstUsableTrancheIndex = calculateFirstUsableTrancheIndex( + now, + state.products[productId].gracePeriod, + period, + ); + const poolCapacity = calculatePoolCapacity(state.poolProducts['1_1'], firstUsableTrancheIndex); + const nxmCapacity = response.availableCapacity.find(c => c.assetId === 255); + expect(nxmCapacity.amount.toString()).to.equal(poolCapacity.toString()); + }); + }); }); diff --git a/test/unit/eventApi.js b/test/unit/eventApi.js index f67ef04..a482c46 100644 --- a/test/unit/eventApi.js +++ b/test/unit/eventApi.js @@ -4,6 +4,7 @@ const { getDefaultProvider } = require('ethers'); const contractFactory = require('../../src/lib/contracts'); const eventsApiConstructor = require('../../src/lib/eventsApi'); +const riContractFactory = require('../../src/lib/riContracts'); const stakingPoolEvents = ['StakeBurned', 'DepositExtended', 'StakeDeposited', 'PoolFeeChanged', 'Deallocated']; const coverEvents = ['CoverBought']; @@ -39,12 +40,14 @@ const settleEvents = () => new Promise(resolve => setTimeout(resolve, 0)); describe('Catching events', () => { let eventsApi; let contracts; + let riContracts; beforeEach(async function () { this.timeout(0); const provider = getDefaultProvider(); contracts = contractFactoryMock(addresses, provider); - eventsApi = await eventsApiConstructor(provider, contracts); + riContracts = riContractFactory(provider); + eventsApi = await eventsApiConstructor(provider, contracts, riContracts); }); it('should catch all events on staking pool', async function () { @@ -159,4 +162,33 @@ describe('Catching events', () => { expect(eventCounter).to.be.equal(stakingPoolFactoryEvents.length); expect(poolChangeCounter).to.be.equal(stakingPoolFactoryEvents.length); }); + + it('should catch SetOperatorNetworkShares and SetOperatorNetworkLimit on delegators', async function () { + const subnetwork = '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000001'; + const operator = '0xF99aA6479Eb153dcA93fd243A06caCD11f3268f9'; + const sharesVaultIds = []; + const limitVaultIds = []; + + eventsApi.on('ri:setOperatorNetworkShares', vaultId => { + sharesVaultIds.push(vaultId); + }); + eventsApi.on('ri:setOperatorNetworkLimit', vaultId => { + limitVaultIds.push(vaultId); + }); + + for (const contractName of Object.keys(riContracts)) { + if (contractName.startsWith('delegator_')) { + riContracts[contractName].emit('SetOperatorNetworkShares', subnetwork, operator, 1000); + riContracts[contractName].emit('SetOperatorNetworkLimit', subnetwork, operator, 5000); + } + } + + await settleEvents(); + + const delegatorKeys = Object.keys(riContracts).filter(k => k.startsWith('delegator_')); + const expectedVaultIds = delegatorKeys.map(k => k.split('_')[1]); + + expect(sharesVaultIds).to.have.members(expectedVaultIds); + expect(limitVaultIds).to.have.members(expectedVaultIds); + }); }); diff --git a/test/unit/quoteEngine.js b/test/unit/quoteEngine.js index 8d544b7..243bfc4 100644 --- a/test/unit/quoteEngine.js +++ b/test/unit/quoteEngine.js @@ -4,7 +4,14 @@ const { } = require('ethers'); const { BigNumber } = require('ethers'); -const { MIN_COVER_PERIOD, TRANCHE_DURATION, SECONDS_PER_DAY } = require('../../src/lib/constants'); +const { + MIN_COVER_PERIOD, + TRANCHE_DURATION, + SECONDS_PER_DAY, + RI_THRESHOLD, + RI_COVER_AMOUNT_PERCENTAGE, + RI_COVER_AMOUNT_DENOMINATOR, +} = require('../../src/lib/constants'); const { quoteEngine } = require('../../src/lib/quoteEngine'); const mockStore = require('../mocks/store'); @@ -176,7 +183,12 @@ describe('Quote Engine tests', () => { const now = BigNumber.from(Date.now()).div(1000); - mockStore.covers[1].start = now.sub(TRANCHE_DURATION + 14 * SECONDS_PER_DAY).toNumber(); + // Set cover to start in a previous tranche (TRANCHE_DURATION + 14 days ago) + // but extend period to ensure it's still active + const startTime = now.sub(TRANCHE_DURATION + 14 * SECONDS_PER_DAY); + mockStore.covers[1].start = startTime.toNumber(); + // Extend period to ensure cover is still active (original period was 30 days) + mockStore.covers[1].period = 120 * 24 * 3600; // 120 days to ensure it's still active const quote = quoteEngine(store, productId, amount, MIN_COVER_PERIOD, 1, 1); const [quote1, quote2, quote3] = quote.poolsWithPremium; @@ -234,4 +246,366 @@ describe('Quote Engine tests', () => { expect(() => quoteEngine(store, productId, amount, MIN_COVER_PERIOD, 1, 2)).to.throw('Not original cover id'); }); + + describe('RI Quote Tests', () => { + const now = BigNumber.from(Date.now()).div(1000); + const period = MIN_COVER_PERIOD; + + const createRiStore = ( + riSubnetworks, + vaultProducts, + activeCoverAmount = null, + poolCapacityAllocationUnits = null, + ) => { + const baseStore = { + ...mockStore, + riSubnetworks, + vaultProducts: { + ...mockStore.vaultProducts, + ...vaultProducts, + }, + riAssetRates: { + ...(mockStore.riAssetRates || {}), + 0: parseEther('1.2'), // 1 wstETH = 1.2 NXM + }, + epochExpires: { + ...(mockStore.epochExpires || {}), + 1: BigNumber.from(Date.now()) + .div(1000) + .add(365 * 24 * 3600), // Set far in the future to ensure it passes the check + 2: BigNumber.from(Date.now()) + .div(1000) + .add(365 * 24 * 3600), // Set far in the future to ensure it passes the check + }, + }; + + if (poolCapacityAllocationUnits != null) { + const cap = BigNumber.from(poolCapacityAllocationUnits); + baseStore.poolProducts = { + ...baseStore.poolProducts, + '1_1': { + ...baseStore.poolProducts['1_1'], + trancheCapacities: baseStore.poolProducts['1_1'].trancheCapacities.map((_, i) => + i === 7 ? cap : BigNumber.from(0), + ), + }, + '1_2': { + ...baseStore.poolProducts['1_2'], + trancheCapacities: baseStore.poolProducts['1_2'].trancheCapacities.map((_, i) => + i === 7 ? cap : BigNumber.from(0), + ), + }, + }; + } + + // Set active cover amount if provided (to meet RI threshold) + if (activeCoverAmount !== null) { + const usdcRate = mockStore.assetRates[6]; + const riThresholdInNXM = BigNumber.from(RI_THRESHOLD).mul(parseEther('1')).div(usdcRate); + const coverAmount = riThresholdInNXM.add(activeCoverAmount); + baseStore.covers = { + ...(mockStore.covers || {}), + 100: { + start: now.toNumber() - 1000, + period: 365 * 24 * 3600, + coverAsset: 6, + productId: 1, + poolAllocations: [ + { + poolId: 1, + coverAmountInNxm: coverAmount, + premiumInNXM: parseEther('100'), + }, + ], + }, + }; + } + + return { getState: () => baseStore }; + }; + + it('should return quote excluding RI when useRiVaults is false', () => { + const productId = 1; + const amount = parseEther('1000'); + const riStore = createRiStore( + { + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000': { + products: { + 1: { productId: 1, price: 500, weight: 25 }, + }, + vaults: ['1'], + }, + }, + { + '1_1': { + vaultId: '1', + id: '1', + providerId: 1, + product: 1, + allocations: [], + price: 500, + activeStake: parseEther('10000'), + withdrawalAmount: parseEther('1000'), + asset: 0, + }, + }, + ); + + const quote = quoteEngine(riStore, productId, amount, period, 1, 0, 1, false); + + expect(quote.riQuote).to.be.null; + expect(quote.poolsWithPremium.length).to.be.greaterThan(0); + }); + + it('should return quote with 80/20 split (default)', () => { + const productId = 1; + const amount = parseEther('200000'); + const riStore = createRiStore( + { + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000': { + products: { + 1: { productId: 1, price: 500, weight: 25 }, + }, + vaults: ['1'], + }, + }, + { + '1_1': { + vaultId: '1', + id: '1', + providerId: 1, + product: 1, + allocations: [], + price: 500, + activeStake: parseEther('100000'), + withdrawalAmount: parseEther('10000'), + asset: 0, + }, + }, + parseEther('1000000'), // Active cover to meet threshold + ); + // Ensure pool capacity can take 20% of amount (~1392 NXM); 150000 allocation units = 1500 NXM per pool + const riStoreWithPoolCap = { + getState: () => { + const s = riStore.getState(); + return { + ...s, + poolProducts: { + ...s.poolProducts, + '1_1': { + ...s.poolProducts['1_1'], + trancheCapacities: s.poolProducts['1_1'].trancheCapacities.map((_, i) => + i === 7 ? BigNumber.from(150000) : BigNumber.from(0), + ), + }, + '1_2': { + ...s.poolProducts['1_2'], + trancheCapacities: s.poolProducts['1_2'].trancheCapacities.map((_, i) => + i === 7 ? BigNumber.from(150000) : BigNumber.from(0), + ), + }, + }, + }; + }, + }; + + const quote = quoteEngine(riStoreWithPoolCap, productId, amount, period, 1, 0, 1, true); + + expect(quote.riQuote).to.not.be.null; + const amountInNXM = amount.mul(parseEther('1')).div(mockStore.assetRates[1]); + const expectedRiAmount = amountInNXM.mul(RI_COVER_AMOUNT_PERCENTAGE).div(RI_COVER_AMOUNT_DENOMINATOR); + const expectedPoolAmount = amountInNXM.sub(expectedRiAmount); + + // Verify RI quote has approximately 80% of the amount + expect(quote.riQuote.amount.gte(expectedRiAmount.mul(99).div(100))).to.be.true; + expect(quote.riQuote.amount.lte(expectedRiAmount.mul(101).div(100))).to.be.true; + + // Verify pools get the remaining amount + const totalPoolAmount = quote.poolsWithPremium.reduce( + (sum, pool) => sum.add(pool.coverAmountInNxm), + BigNumber.from(0), + ); + expect(totalPoolAmount.gte(expectedPoolAmount.mul(99).div(100))).to.be.true; + expect(totalPoolAmount.lte(expectedPoolAmount.mul(101).div(100))).to.be.true; + }); + + it('should return quote with 100% RI coverage when product has riCoverAmountPercentage set to 100', () => { + const productId = 1; + const amount = parseEther('200000'); + const riStore = createRiStore( + { + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000': { + products: { + 1: { productId: 1, price: 500, weight: 25, riCoverAmountPercentage: 100 }, + }, + vaults: ['1'], + }, + }, + { + '1_1': { + vaultId: '1', + id: '1', + providerId: 1, + product: 1, + allocations: [], + price: 500, + activeStake: parseEther('50000'), + withdrawalAmount: parseEther('5000'), + asset: 0, + }, + }, + parseEther('1000000'), // Active cover to meet threshold + ); + + const quote = quoteEngine(riStore, productId, amount, period, 1, 0, 1, true); + + expect(quote.riQuote).to.not.be.null; + const amountInNXM = amount.mul(parseEther('1')).div(mockStore.assetRates[1]); + + // Should allocate 100% to RI + expect(quote.riQuote.amount.gte(amountInNXM.mul(99).div(100))).to.be.true; + expect(quote.riQuote.amount.lte(amountInNXM.mul(101).div(100))).to.be.true; + + // Pools should get minimal or zero allocation + const totalPoolAmount = quote.poolsWithPremium.reduce( + (sum, pool) => sum.add(pool.coverAmountInNxm), + BigNumber.from(0), + ); + expect(totalPoolAmount.lte(amountInNXM.mul(5).div(100))).to.be.true; // Less than 5% rounding + }); + + it('should allocate remaining amount to RI when pool capacity is insufficient', () => { + const productId = 1; + const amount = parseEther('200000'); + const riStore = createRiStore( + { + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000': { + products: { + 1: { productId: 1, price: 500, weight: 25 }, + }, + vaults: ['1'], + }, + }, + { + '1_1': { + vaultId: '1', + id: '1', + providerId: 1, + product: 1, + allocations: [], + price: 500, + activeStake: parseEther('1000000'), // Large RI capacity + withdrawalAmount: parseEther('100000'), + asset: 0, + }, + }, + parseEther('1000000'), // Active cover to meet threshold + ); + + // Create store with limited pool capacity + const limitedPoolStore = { + getState: () => { + const state = riStore.getState(); + // Reduce pool capacity significantly + return { + ...state, + poolProducts: { + ...state.poolProducts, + '1_1': { + ...state.poolProducts['1_1'], + trancheCapacities: state.poolProducts['1_1'].trancheCapacities.map(() => BigNumber.from(1000)), + }, + '1_2': { + ...state.poolProducts['1_2'], + trancheCapacities: state.poolProducts['1_2'].trancheCapacities.map(() => BigNumber.from(1000)), + }, + }, + }; + }, + }; + + const quote = quoteEngine(limitedPoolStore, productId, amount, period, 1, 0, 1, true); + + expect(quote.riQuote).to.not.be.null; + const amountInNXM = amount.mul(parseEther('1')).div(mockStore.assetRates[1]); + + // RI should get more than the default 80% because pools are insufficient + const defaultRiAmount = amountInNXM.mul(RI_COVER_AMOUNT_PERCENTAGE).div(RI_COVER_AMOUNT_DENOMINATOR); + expect(quote.riQuote.amount.gte(defaultRiAmount)).to.be.true; + }); + + it('should allocate remaining amount to pools when RI capacity is insufficient', () => { + const productId = 1; + // RI capacity above min but below 80% of amount; pool capacity high so total >= amountToAllocate + const amount = parseEther('172000'); // ~6000 NXM; 80% = 4800, we cap RI to 4000, pools get 2000 + const riStore = createRiStore( + { + '0xf99aa6479eb153dca93fd243a06cacd11f3268f9000000000000000000000000': { + products: { + 1: { productId: 1, price: 500, weight: 25 }, + }, + vaults: ['1'], + }, + }, + { + '1_1': { + vaultId: '1', + id: '1', + providerId: 1, + product: 1, + allocations: [], + price: 500, + // ~4000 NXM RI capacity (above min, below 80% of 6000); 3800*1.2=4560 so RI is capped below 80% + activeStake: parseEther('3167'), + withdrawalAmount: parseEther('633'), + asset: 0, + }, + }, + parseEther('1000000'), // Active cover to meet threshold + ); + + // Bump pool capacity so pools can take remainder (2000 NXM = 200000 allocation units per pool) + const storeWithPoolCap = { + getState: () => { + const state = riStore.getState(); + const cap = BigNumber.from(200000); + return { + ...state, + poolProducts: { + ...state.poolProducts, + '1_1': { + ...state.poolProducts['1_1'], + trancheCapacities: state.poolProducts['1_1'].trancheCapacities.map((_, i) => + i === 7 ? cap : BigNumber.from(0), + ), + }, + '1_2': { + ...state.poolProducts['1_2'], + trancheCapacities: state.poolProducts['1_2'].trancheCapacities.map((_, i) => + i === 7 ? cap : BigNumber.from(0), + ), + }, + }, + }; + }, + }; + + const quote = quoteEngine(storeWithPoolCap, productId, amount, period, 1, 0, 1, true); + + expect(quote.riQuote).to.not.be.null; + const amountInNXM = amount.mul(parseEther('1')).div(mockStore.assetRates[1]); + const defaultRiAmount = amountInNXM.mul(RI_COVER_AMOUNT_PERCENTAGE).div(RI_COVER_AMOUNT_DENOMINATOR); + + // RI should get less than default 80% because capacity is limited + expect(quote.riQuote.amount.lte(defaultRiAmount)).to.be.true; + + // Pools should get more than the default 20% + const totalPoolAmount = quote.poolsWithPremium.reduce( + (sum, pool) => sum.add(pool.coverAmountInNxm), + BigNumber.from(0), + ); + const defaultPoolAmount = amountInNXM.sub(defaultRiAmount); + expect(totalPoolAmount.gte(defaultPoolAmount)).to.be.true; + }); + }); }); diff --git a/test/unit/responses.js b/test/unit/responses.js index 5c6217c..0392864 100644 --- a/test/unit/responses.js +++ b/test/unit/responses.js @@ -50,27 +50,27 @@ const capacities = [ availableCapacity: [ { assetId: 0, - amount: '5262352631887064160', + amount: '37986609171898107700', asset: assets[0], }, { assetId: 1, - amount: '14704040331174418918482', + amount: '106142000048342086604649', asset: assets[1], }, { assetId: 6, - amount: '14704040324', + amount: '106141999997', asset: assets[6], }, { assetId: 7, - amount: '42116060', + amount: '304017318', asset: assets[7], }, { assetId: 255, - amount: '511900000000000000000', + amount: '3695180957138099995244', asset: assets[255], }, ], @@ -83,27 +83,27 @@ const capacities = [ availableCapacity: [ { assetId: 0, - amount: '4756985850516546336', + amount: '12938049985519307221', asset: assets[0], }, { assetId: 1, - amount: '13291946909255031471651', + amount: '36151436838546948393193', asset: assets[1], }, { assetId: 6, - amount: '13291946902', + amount: '36151436821', asset: assets[6], }, { assetId: 7, - amount: '38071470', + amount: '103546785', asset: assets[7], }, { assetId: 255, - amount: '462740000000000000000', + amount: '1258560239284524998811', asset: assets[255], }, ], @@ -113,27 +113,27 @@ const capacities = [ availableCapacity: [ { assetId: 0, - amount: '650305069624035537600', + amount: '653315188760254613847', asset: assets[0], }, { assetId: 1, - amount: '1817079287575234550428386', + amount: '1825490147940693063719621', asset: assets[1], }, { assetId: 6, - amount: '1817079286701', + amount: '1825490147062', asset: assets[6], }, { assetId: 7, - amount: '5204570966', + amount: '5228661780', asset: assets[7], }, { assetId: 255, - amount: '63259000000000000000000', + amount: '63551811997526283240569', asset: assets[255], }, ], @@ -146,27 +146,27 @@ const capacities = [ availableCapacity: [ { assetId: 0, - amount: '37190101809685157280', + amount: '49925308525200018802', asset: assets[0], }, { assetId: 1, - amount: '103916403020296337803075', + amount: '139501056172592462935684', asset: assets[1], }, { assetId: 6, - amount: '103916402970', + amount: '139501056105', asset: assets[6], }, { assetId: 7, - amount: '297642649', + amount: '399566024', asset: assets[7], }, { assetId: 255, - amount: '3617700000000000000000', + amount: '4856528481042766757053', asset: assets[255], }, ], @@ -420,6 +420,7 @@ const ethQuote = { coverAmountInAsset: '100024792163041872', }, ], + riRequest: null, }; const daiQuote = { @@ -437,6 +438,7 @@ const daiQuote = { coverAmountInAsset: '1148977560552796946', }, ], + riRequest: null, }; const usdcQuote = { @@ -454,6 +456,7 @@ const usdcQuote = { coverAmountInAsset: '10053553', }, ], + riRequest: null, }; const quoteMapping = { diff --git a/test/unit/routes/quote.js b/test/unit/routes/quote.js index 4349505..5e3675e 100644 --- a/test/unit/routes/quote.js +++ b/test/unit/routes/quote.js @@ -78,7 +78,7 @@ describe('GET /quote', () => { }) .expect('Content-Type', /json/) .expect(400); - expect(message).to.be.equal('Not enough capacity for the cover amount'); + expect(message).to.be.include('Not enough capacity'); }); it('should return 400 error for invalid productId', async function () { diff --git a/test/unit/synchronizer.js b/test/unit/synchronizer.js index 95ed491..4773776 100644 --- a/test/unit/synchronizer.js +++ b/test/unit/synchronizer.js @@ -93,4 +93,38 @@ describe('synchronizer', () => { expect(cover1AfterEdit.originalCoverId).to.be.equal(1); expect(cover1AfterEdit.latestCoverId).to.be.equal(2); }); + + const riCapacityEvents = [ + { event: 'ri:setOperatorNetworkShares', stake: '0x1000', withdrawal: '0x500' }, + { event: 'ri:setOperatorNetworkLimit', stake: '0x2000', withdrawal: '0x800' }, + ]; + + for (const { event, stake, withdrawal } of riCapacityEvents) { + it(`should trigger updateRiVaultCapacity on ${event} event`, async () => { + const riEventApi = new EventEmitter(); + const store = createStore({ + ...extendedMockStore, + riSubnetworks: mockStore.riSubnetworks, + vaultProducts: mockStore.vaultProducts, + }); + await createSynchronizer( + store, + { + ...mockChainApi, + fetchVaultStake: async () => BigNumber.from(stake), + fetchVaultWithdrawals: async () => BigNumber.from(withdrawal), + }, + riEventApi, + ); + + riEventApi.emit(event, '1'); + + // Allow async handler to complete + await new Promise(resolve => setTimeout(resolve, 100)); + + const { vaultProducts } = store.getState(); + expect(vaultProducts['1_1'].activeStake.toString()).to.equal(BigNumber.from(stake).toString()); + expect(vaultProducts['1_1'].withdrawalAmount.toString()).to.equal(BigNumber.from(withdrawal).toString()); + }); + } });