@waiaas/daemon 2.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/error-hints.d.ts +15 -0
- package/dist/api/error-hints.d.ts.map +1 -0
- package/dist/api/error-hints.js +71 -0
- package/dist/api/error-hints.js.map +1 -0
- package/dist/api/index.d.ts +11 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +14 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/middleware/address-validation.d.ts +38 -0
- package/dist/api/middleware/address-validation.d.ts.map +1 -0
- package/dist/api/middleware/address-validation.js +134 -0
- package/dist/api/middleware/address-validation.js.map +1 -0
- package/dist/api/middleware/csp.d.ts +17 -0
- package/dist/api/middleware/csp.d.ts.map +1 -0
- package/dist/api/middleware/csp.js +31 -0
- package/dist/api/middleware/csp.js.map +1 -0
- package/dist/api/middleware/error-handler.d.ts +16 -0
- package/dist/api/middleware/error-handler.d.ts.map +1 -0
- package/dist/api/middleware/error-handler.js +46 -0
- package/dist/api/middleware/error-handler.js.map +1 -0
- package/dist/api/middleware/host-guard.d.ts +11 -0
- package/dist/api/middleware/host-guard.d.ts.map +1 -0
- package/dist/api/middleware/host-guard.js +25 -0
- package/dist/api/middleware/host-guard.js.map +1 -0
- package/dist/api/middleware/index.d.ts +13 -0
- package/dist/api/middleware/index.d.ts.map +1 -0
- package/dist/api/middleware/index.js +13 -0
- package/dist/api/middleware/index.js.map +1 -0
- package/dist/api/middleware/kill-switch-guard.d.ts +19 -0
- package/dist/api/middleware/kill-switch-guard.d.ts.map +1 -0
- package/dist/api/middleware/kill-switch-guard.js +49 -0
- package/dist/api/middleware/kill-switch-guard.js.map +1 -0
- package/dist/api/middleware/master-auth.d.ts +15 -0
- package/dist/api/middleware/master-auth.d.ts.map +1 -0
- package/dist/api/middleware/master-auth.js +35 -0
- package/dist/api/middleware/master-auth.js.map +1 -0
- package/dist/api/middleware/owner-auth.d.ts +30 -0
- package/dist/api/middleware/owner-auth.d.ts.map +1 -0
- package/dist/api/middleware/owner-auth.js +133 -0
- package/dist/api/middleware/owner-auth.js.map +1 -0
- package/dist/api/middleware/request-id.d.ts +10 -0
- package/dist/api/middleware/request-id.d.ts.map +1 -0
- package/dist/api/middleware/request-id.js +18 -0
- package/dist/api/middleware/request-id.js.map +1 -0
- package/dist/api/middleware/request-logger.d.ts +9 -0
- package/dist/api/middleware/request-logger.d.ts.map +1 -0
- package/dist/api/middleware/request-logger.js +18 -0
- package/dist/api/middleware/request-logger.js.map +1 -0
- package/dist/api/middleware/session-auth.d.ts +21 -0
- package/dist/api/middleware/session-auth.d.ts.map +1 -0
- package/dist/api/middleware/session-auth.js +51 -0
- package/dist/api/middleware/session-auth.js.map +1 -0
- package/dist/api/middleware/siwe-verify.d.ts +31 -0
- package/dist/api/middleware/siwe-verify.d.ts.map +1 -0
- package/dist/api/middleware/siwe-verify.js +55 -0
- package/dist/api/middleware/siwe-verify.js.map +1 -0
- package/dist/api/routes/actions.d.ts +56 -0
- package/dist/api/routes/actions.d.ts.map +1 -0
- package/dist/api/routes/actions.js +291 -0
- package/dist/api/routes/actions.js.map +1 -0
- package/dist/api/routes/admin.d.ts +99 -0
- package/dist/api/routes/admin.d.ts.map +1 -0
- package/dist/api/routes/admin.js +1304 -0
- package/dist/api/routes/admin.js.map +1 -0
- package/dist/api/routes/display-currency-helper.d.ts +26 -0
- package/dist/api/routes/display-currency-helper.d.ts.map +1 -0
- package/dist/api/routes/display-currency-helper.js +47 -0
- package/dist/api/routes/display-currency-helper.js.map +1 -0
- package/dist/api/routes/health.d.ts +14 -0
- package/dist/api/routes/health.d.ts.map +1 -0
- package/dist/api/routes/health.js +47 -0
- package/dist/api/routes/health.js.map +1 -0
- package/dist/api/routes/index.d.ts +15 -0
- package/dist/api/routes/index.d.ts.map +1 -0
- package/dist/api/routes/index.js +15 -0
- package/dist/api/routes/index.js.map +1 -0
- package/dist/api/routes/mcp.d.ts +30 -0
- package/dist/api/routes/mcp.d.ts.map +1 -0
- package/dist/api/routes/mcp.js +156 -0
- package/dist/api/routes/mcp.js.map +1 -0
- package/dist/api/routes/nonce.d.ts +20 -0
- package/dist/api/routes/nonce.d.ts.map +1 -0
- package/dist/api/routes/nonce.js +48 -0
- package/dist/api/routes/nonce.js.map +1 -0
- package/dist/api/routes/openapi-schemas.d.ts +2281 -0
- package/dist/api/routes/openapi-schemas.d.ts.map +1 -0
- package/dist/api/routes/openapi-schemas.js +770 -0
- package/dist/api/routes/openapi-schemas.js.map +1 -0
- package/dist/api/routes/policies.d.ts +29 -0
- package/dist/api/routes/policies.d.ts.map +1 -0
- package/dist/api/routes/policies.js +332 -0
- package/dist/api/routes/policies.js.map +1 -0
- package/dist/api/routes/sessions.d.ts +35 -0
- package/dist/api/routes/sessions.d.ts.map +1 -0
- package/dist/api/routes/sessions.js +347 -0
- package/dist/api/routes/sessions.js.map +1 -0
- package/dist/api/routes/skills.d.ts +9 -0
- package/dist/api/routes/skills.d.ts.map +1 -0
- package/dist/api/routes/skills.js +59 -0
- package/dist/api/routes/skills.js.map +1 -0
- package/dist/api/routes/tokens.d.ts +25 -0
- package/dist/api/routes/tokens.d.ts.map +1 -0
- package/dist/api/routes/tokens.js +161 -0
- package/dist/api/routes/tokens.js.map +1 -0
- package/dist/api/routes/transactions.d.ts +68 -0
- package/dist/api/routes/transactions.d.ts.map +1 -0
- package/dist/api/routes/transactions.js +576 -0
- package/dist/api/routes/transactions.js.map +1 -0
- package/dist/api/routes/utils.d.ts +9 -0
- package/dist/api/routes/utils.d.ts.map +1 -0
- package/dist/api/routes/utils.js +52 -0
- package/dist/api/routes/utils.js.map +1 -0
- package/dist/api/routes/wallet.d.ts +36 -0
- package/dist/api/routes/wallet.d.ts.map +1 -0
- package/dist/api/routes/wallet.js +358 -0
- package/dist/api/routes/wallet.js.map +1 -0
- package/dist/api/routes/wallets.d.ts +43 -0
- package/dist/api/routes/wallets.d.ts.map +1 -0
- package/dist/api/routes/wallets.js +630 -0
- package/dist/api/routes/wallets.js.map +1 -0
- package/dist/api/routes/wc.d.ts +46 -0
- package/dist/api/routes/wc.d.ts.map +1 -0
- package/dist/api/routes/wc.js +354 -0
- package/dist/api/routes/wc.js.map +1 -0
- package/dist/api/routes/x402.d.ts +61 -0
- package/dist/api/routes/x402.d.ts.map +1 -0
- package/dist/api/routes/x402.js +493 -0
- package/dist/api/routes/x402.js.map +1 -0
- package/dist/api/server.d.ts +81 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +406 -0
- package/dist/api/server.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/action/action-provider-registry.d.ts +77 -0
- package/dist/infrastructure/action/action-provider-registry.d.ts.map +1 -0
- package/dist/infrastructure/action/action-provider-registry.js +239 -0
- package/dist/infrastructure/action/action-provider-registry.js.map +1 -0
- package/dist/infrastructure/action/api-key-store.d.ts +60 -0
- package/dist/infrastructure/action/api-key-store.d.ts.map +1 -0
- package/dist/infrastructure/action/api-key-store.js +130 -0
- package/dist/infrastructure/action/api-key-store.js.map +1 -0
- package/dist/infrastructure/action/index.d.ts +10 -0
- package/dist/infrastructure/action/index.d.ts.map +1 -0
- package/dist/infrastructure/action/index.js +9 -0
- package/dist/infrastructure/action/index.js.map +1 -0
- package/dist/infrastructure/adapter-pool.d.ts +50 -0
- package/dist/infrastructure/adapter-pool.d.ts.map +1 -0
- package/dist/infrastructure/adapter-pool.js +110 -0
- package/dist/infrastructure/adapter-pool.js.map +1 -0
- package/dist/infrastructure/backup/backup-service.d.ts +53 -0
- package/dist/infrastructure/backup/backup-service.d.ts.map +1 -0
- package/dist/infrastructure/backup/backup-service.js +158 -0
- package/dist/infrastructure/backup/backup-service.js.map +1 -0
- package/dist/infrastructure/backup/index.d.ts +2 -0
- package/dist/infrastructure/backup/index.d.ts.map +1 -0
- package/dist/infrastructure/backup/index.js +2 -0
- package/dist/infrastructure/backup/index.js.map +1 -0
- package/dist/infrastructure/config/index.d.ts +8 -0
- package/dist/infrastructure/config/index.d.ts.map +1 -0
- package/dist/infrastructure/config/index.js +7 -0
- package/dist/infrastructure/config/index.js.map +1 -0
- package/dist/infrastructure/config/loader.d.ts +555 -0
- package/dist/infrastructure/config/loader.d.ts.map +1 -0
- package/dist/infrastructure/config/loader.js +311 -0
- package/dist/infrastructure/config/loader.js.map +1 -0
- package/dist/infrastructure/database/checks.d.ts +19 -0
- package/dist/infrastructure/database/checks.d.ts.map +1 -0
- package/dist/infrastructure/database/checks.js +27 -0
- package/dist/infrastructure/database/checks.js.map +1 -0
- package/dist/infrastructure/database/compatibility.d.ts +36 -0
- package/dist/infrastructure/database/compatibility.d.ts.map +1 -0
- package/dist/infrastructure/database/compatibility.js +75 -0
- package/dist/infrastructure/database/compatibility.js.map +1 -0
- package/dist/infrastructure/database/connection.d.ts +36 -0
- package/dist/infrastructure/database/connection.d.ts.map +1 -0
- package/dist/infrastructure/database/connection.js +47 -0
- package/dist/infrastructure/database/connection.js.map +1 -0
- package/dist/infrastructure/database/id.d.ts +17 -0
- package/dist/infrastructure/database/id.d.ts.map +1 -0
- package/dist/infrastructure/database/id.js +20 -0
- package/dist/infrastructure/database/id.js.map +1 -0
- package/dist/infrastructure/database/index.d.ts +15 -0
- package/dist/infrastructure/database/index.d.ts.map +1 -0
- package/dist/infrastructure/database/index.js +12 -0
- package/dist/infrastructure/database/index.js.map +1 -0
- package/dist/infrastructure/database/migrate.d.ts +76 -0
- package/dist/infrastructure/database/migrate.d.ts.map +1 -0
- package/dist/infrastructure/database/migrate.js +1214 -0
- package/dist/infrastructure/database/migrate.js.map +1 -0
- package/dist/infrastructure/database/schema.d.ts +2352 -0
- package/dist/infrastructure/database/schema.d.ts.map +1 -0
- package/dist/infrastructure/database/schema.js +288 -0
- package/dist/infrastructure/database/schema.js.map +1 -0
- package/dist/infrastructure/jwt/index.d.ts +2 -0
- package/dist/infrastructure/jwt/index.d.ts.map +1 -0
- package/dist/infrastructure/jwt/index.js +2 -0
- package/dist/infrastructure/jwt/index.js.map +1 -0
- package/dist/infrastructure/jwt/jwt-secret-manager.d.ts +58 -0
- package/dist/infrastructure/jwt/jwt-secret-manager.d.ts.map +1 -0
- package/dist/infrastructure/jwt/jwt-secret-manager.js +222 -0
- package/dist/infrastructure/jwt/jwt-secret-manager.js.map +1 -0
- package/dist/infrastructure/keystore/crypto.d.ts +62 -0
- package/dist/infrastructure/keystore/crypto.d.ts.map +1 -0
- package/dist/infrastructure/keystore/crypto.js +89 -0
- package/dist/infrastructure/keystore/crypto.js.map +1 -0
- package/dist/infrastructure/keystore/index.d.ts +4 -0
- package/dist/infrastructure/keystore/index.d.ts.map +1 -0
- package/dist/infrastructure/keystore/index.js +5 -0
- package/dist/infrastructure/keystore/index.js.map +1 -0
- package/dist/infrastructure/keystore/keystore.d.ts +115 -0
- package/dist/infrastructure/keystore/keystore.d.ts.map +1 -0
- package/dist/infrastructure/keystore/keystore.js +327 -0
- package/dist/infrastructure/keystore/keystore.js.map +1 -0
- package/dist/infrastructure/keystore/memory.d.ts +45 -0
- package/dist/infrastructure/keystore/memory.d.ts.map +1 -0
- package/dist/infrastructure/keystore/memory.js +105 -0
- package/dist/infrastructure/keystore/memory.js.map +1 -0
- package/dist/infrastructure/oracle/coingecko-forex.d.ts +35 -0
- package/dist/infrastructure/oracle/coingecko-forex.d.ts.map +1 -0
- package/dist/infrastructure/oracle/coingecko-forex.js +69 -0
- package/dist/infrastructure/oracle/coingecko-forex.js.map +1 -0
- package/dist/infrastructure/oracle/coingecko-oracle.d.ts +73 -0
- package/dist/infrastructure/oracle/coingecko-oracle.d.ts.map +1 -0
- package/dist/infrastructure/oracle/coingecko-oracle.js +199 -0
- package/dist/infrastructure/oracle/coingecko-oracle.js.map +1 -0
- package/dist/infrastructure/oracle/coingecko-platform-ids.d.ts +32 -0
- package/dist/infrastructure/oracle/coingecko-platform-ids.d.ts.map +1 -0
- package/dist/infrastructure/oracle/coingecko-platform-ids.js +30 -0
- package/dist/infrastructure/oracle/coingecko-platform-ids.js.map +1 -0
- package/dist/infrastructure/oracle/forex-currencies.d.ts +36 -0
- package/dist/infrastructure/oracle/forex-currencies.d.ts.map +1 -0
- package/dist/infrastructure/oracle/forex-currencies.js +71 -0
- package/dist/infrastructure/oracle/forex-currencies.js.map +1 -0
- package/dist/infrastructure/oracle/forex-rate-service.d.ts +51 -0
- package/dist/infrastructure/oracle/forex-rate-service.d.ts.map +1 -0
- package/dist/infrastructure/oracle/forex-rate-service.js +149 -0
- package/dist/infrastructure/oracle/forex-rate-service.js.map +1 -0
- package/dist/infrastructure/oracle/index.d.ts +18 -0
- package/dist/infrastructure/oracle/index.d.ts.map +1 -0
- package/dist/infrastructure/oracle/index.js +19 -0
- package/dist/infrastructure/oracle/index.js.map +1 -0
- package/dist/infrastructure/oracle/oracle-chain.d.ts +101 -0
- package/dist/infrastructure/oracle/oracle-chain.d.ts.map +1 -0
- package/dist/infrastructure/oracle/oracle-chain.js +163 -0
- package/dist/infrastructure/oracle/oracle-chain.js.map +1 -0
- package/dist/infrastructure/oracle/oracle-errors.d.ts +42 -0
- package/dist/infrastructure/oracle/oracle-errors.d.ts.map +1 -0
- package/dist/infrastructure/oracle/oracle-errors.js +53 -0
- package/dist/infrastructure/oracle/oracle-errors.js.map +1 -0
- package/dist/infrastructure/oracle/price-age.d.ts +38 -0
- package/dist/infrastructure/oracle/price-age.d.ts.map +1 -0
- package/dist/infrastructure/oracle/price-age.js +44 -0
- package/dist/infrastructure/oracle/price-age.js.map +1 -0
- package/dist/infrastructure/oracle/price-cache.d.ts +99 -0
- package/dist/infrastructure/oracle/price-cache.d.ts.map +1 -0
- package/dist/infrastructure/oracle/price-cache.js +173 -0
- package/dist/infrastructure/oracle/price-cache.js.map +1 -0
- package/dist/infrastructure/oracle/pyth-feed-ids.d.ts +31 -0
- package/dist/infrastructure/oracle/pyth-feed-ids.d.ts.map +1 -0
- package/dist/infrastructure/oracle/pyth-feed-ids.js +44 -0
- package/dist/infrastructure/oracle/pyth-feed-ids.js.map +1 -0
- package/dist/infrastructure/oracle/pyth-oracle.d.ts +69 -0
- package/dist/infrastructure/oracle/pyth-oracle.d.ts.map +1 -0
- package/dist/infrastructure/oracle/pyth-oracle.js +149 -0
- package/dist/infrastructure/oracle/pyth-oracle.js.map +1 -0
- package/dist/infrastructure/settings/hot-reload.d.ts +71 -0
- package/dist/infrastructure/settings/hot-reload.d.ts.map +1 -0
- package/dist/infrastructure/settings/hot-reload.js +315 -0
- package/dist/infrastructure/settings/hot-reload.js.map +1 -0
- package/dist/infrastructure/settings/index.d.ts +13 -0
- package/dist/infrastructure/settings/index.d.ts.map +1 -0
- package/dist/infrastructure/settings/index.js +10 -0
- package/dist/infrastructure/settings/index.js.map +1 -0
- package/dist/infrastructure/settings/setting-keys.d.ts +28 -0
- package/dist/infrastructure/settings/setting-keys.d.ts.map +1 -0
- package/dist/infrastructure/settings/setting-keys.js +105 -0
- package/dist/infrastructure/settings/setting-keys.js.map +1 -0
- package/dist/infrastructure/settings/settings-crypto.d.ts +39 -0
- package/dist/infrastructure/settings/settings-crypto.d.ts.map +1 -0
- package/dist/infrastructure/settings/settings-crypto.js +73 -0
- package/dist/infrastructure/settings/settings-crypto.js.map +1 -0
- package/dist/infrastructure/settings/settings-service.d.ts +82 -0
- package/dist/infrastructure/settings/settings-service.d.ts.map +1 -0
- package/dist/infrastructure/settings/settings-service.js +267 -0
- package/dist/infrastructure/settings/settings-service.js.map +1 -0
- package/dist/infrastructure/telegram/index.d.ts +6 -0
- package/dist/infrastructure/telegram/index.d.ts.map +1 -0
- package/dist/infrastructure/telegram/index.js +5 -0
- package/dist/infrastructure/telegram/index.js.map +1 -0
- package/dist/infrastructure/telegram/telegram-api.d.ts +35 -0
- package/dist/infrastructure/telegram/telegram-api.d.ts.map +1 -0
- package/dist/infrastructure/telegram/telegram-api.js +82 -0
- package/dist/infrastructure/telegram/telegram-api.js.map +1 -0
- package/dist/infrastructure/telegram/telegram-auth.d.ts +57 -0
- package/dist/infrastructure/telegram/telegram-auth.d.ts.map +1 -0
- package/dist/infrastructure/telegram/telegram-auth.js +88 -0
- package/dist/infrastructure/telegram/telegram-auth.js.map +1 -0
- package/dist/infrastructure/telegram/telegram-bot-service.d.ts +95 -0
- package/dist/infrastructure/telegram/telegram-bot-service.d.ts.map +1 -0
- package/dist/infrastructure/telegram/telegram-bot-service.js +564 -0
- package/dist/infrastructure/telegram/telegram-bot-service.js.map +1 -0
- package/dist/infrastructure/telegram/telegram-keyboard.d.ts +27 -0
- package/dist/infrastructure/telegram/telegram-keyboard.d.ts.map +1 -0
- package/dist/infrastructure/telegram/telegram-keyboard.js +52 -0
- package/dist/infrastructure/telegram/telegram-keyboard.js.map +1 -0
- package/dist/infrastructure/telegram/telegram-types.d.ts +43 -0
- package/dist/infrastructure/telegram/telegram-types.d.ts.map +1 -0
- package/dist/infrastructure/telegram/telegram-types.js +8 -0
- package/dist/infrastructure/telegram/telegram-types.js.map +1 -0
- package/dist/infrastructure/token-registry/builtin-tokens.d.ts +39 -0
- package/dist/infrastructure/token-registry/builtin-tokens.d.ts.map +1 -0
- package/dist/infrastructure/token-registry/builtin-tokens.js +135 -0
- package/dist/infrastructure/token-registry/builtin-tokens.js.map +1 -0
- package/dist/infrastructure/token-registry/index.d.ts +8 -0
- package/dist/infrastructure/token-registry/index.d.ts.map +1 -0
- package/dist/infrastructure/token-registry/index.js +8 -0
- package/dist/infrastructure/token-registry/index.js.map +1 -0
- package/dist/infrastructure/token-registry/token-registry-service.d.ts +49 -0
- package/dist/infrastructure/token-registry/token-registry-service.d.ts.map +1 -0
- package/dist/infrastructure/token-registry/token-registry-service.js +93 -0
- package/dist/infrastructure/token-registry/token-registry-service.js.map +1 -0
- package/dist/infrastructure/version/index.d.ts +5 -0
- package/dist/infrastructure/version/index.d.ts.map +1 -0
- package/dist/infrastructure/version/index.js +5 -0
- package/dist/infrastructure/version/index.js.map +1 -0
- package/dist/infrastructure/version/version-check-service.d.ts +35 -0
- package/dist/infrastructure/version/version-check-service.d.ts.map +1 -0
- package/dist/infrastructure/version/version-check-service.js +92 -0
- package/dist/infrastructure/version/version-check-service.js.map +1 -0
- package/dist/lifecycle/daemon.d.ts +103 -0
- package/dist/lifecycle/daemon.d.ts.map +1 -0
- package/dist/lifecycle/daemon.js +934 -0
- package/dist/lifecycle/daemon.js.map +1 -0
- package/dist/lifecycle/index.d.ts +9 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +9 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle/signal-handler.d.ts +18 -0
- package/dist/lifecycle/signal-handler.d.ts.map +1 -0
- package/dist/lifecycle/signal-handler.js +37 -0
- package/dist/lifecycle/signal-handler.js.map +1 -0
- package/dist/lifecycle/workers.d.ts +46 -0
- package/dist/lifecycle/workers.d.ts.map +1 -0
- package/dist/lifecycle/workers.js +101 -0
- package/dist/lifecycle/workers.js.map +1 -0
- package/dist/notifications/channels/discord.d.ts +10 -0
- package/dist/notifications/channels/discord.d.ts.map +1 -0
- package/dist/notifications/channels/discord.js +54 -0
- package/dist/notifications/channels/discord.js.map +1 -0
- package/dist/notifications/channels/ntfy.d.ts +13 -0
- package/dist/notifications/channels/ntfy.d.ts.map +1 -0
- package/dist/notifications/channels/ntfy.js +58 -0
- package/dist/notifications/channels/ntfy.js.map +1 -0
- package/dist/notifications/channels/slack.d.ts +10 -0
- package/dist/notifications/channels/slack.d.ts.map +1 -0
- package/dist/notifications/channels/slack.js +55 -0
- package/dist/notifications/channels/slack.js.map +1 -0
- package/dist/notifications/channels/telegram.d.ts +10 -0
- package/dist/notifications/channels/telegram.d.ts.map +1 -0
- package/dist/notifications/channels/telegram.js +40 -0
- package/dist/notifications/channels/telegram.js.map +1 -0
- package/dist/notifications/index.d.ts +9 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +7 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/notification-service.d.ts +75 -0
- package/dist/notifications/notification-service.d.ts.map +1 -0
- package/dist/notifications/notification-service.js +213 -0
- package/dist/notifications/notification-service.js.map +1 -0
- package/dist/notifications/templates/message-templates.d.ts +12 -0
- package/dist/notifications/templates/message-templates.d.ts.map +1 -0
- package/dist/notifications/templates/message-templates.js +22 -0
- package/dist/notifications/templates/message-templates.js.map +1 -0
- package/dist/pipeline/database-policy-engine.d.ts +286 -0
- package/dist/pipeline/database-policy-engine.d.ts.map +1 -0
- package/dist/pipeline/database-policy-engine.js +992 -0
- package/dist/pipeline/database-policy-engine.js.map +1 -0
- package/dist/pipeline/default-policy-engine.d.ts +26 -0
- package/dist/pipeline/default-policy-engine.d.ts.map +1 -0
- package/dist/pipeline/default-policy-engine.js +25 -0
- package/dist/pipeline/default-policy-engine.js.map +1 -0
- package/dist/pipeline/index.d.ts +9 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +9 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/network-resolver.d.ts +22 -0
- package/dist/pipeline/network-resolver.d.ts.map +1 -0
- package/dist/pipeline/network-resolver.js +32 -0
- package/dist/pipeline/network-resolver.js.map +1 -0
- package/dist/pipeline/pipeline.d.ts +72 -0
- package/dist/pipeline/pipeline.d.ts.map +1 -0
- package/dist/pipeline/pipeline.js +87 -0
- package/dist/pipeline/pipeline.js.map +1 -0
- package/dist/pipeline/resolve-effective-amount-usd.d.ts +41 -0
- package/dist/pipeline/resolve-effective-amount-usd.d.ts.map +1 -0
- package/dist/pipeline/resolve-effective-amount-usd.js +208 -0
- package/dist/pipeline/resolve-effective-amount-usd.js.map +1 -0
- package/dist/pipeline/sign-only.d.ts +99 -0
- package/dist/pipeline/sign-only.d.ts.map +1 -0
- package/dist/pipeline/sign-only.js +267 -0
- package/dist/pipeline/sign-only.js.map +1 -0
- package/dist/pipeline/sleep.d.ts +6 -0
- package/dist/pipeline/sleep.d.ts.map +1 -0
- package/dist/pipeline/sleep.js +8 -0
- package/dist/pipeline/sleep.js.map +1 -0
- package/dist/pipeline/stages.d.ts +82 -0
- package/dist/pipeline/stages.d.ts.map +1 -0
- package/dist/pipeline/stages.js +784 -0
- package/dist/pipeline/stages.js.map +1 -0
- package/dist/services/autostop-rules.d.ts +79 -0
- package/dist/services/autostop-rules.d.ts.map +1 -0
- package/dist/services/autostop-rules.js +174 -0
- package/dist/services/autostop-rules.js.map +1 -0
- package/dist/services/autostop-service.d.ts +82 -0
- package/dist/services/autostop-service.d.ts.map +1 -0
- package/dist/services/autostop-service.js +223 -0
- package/dist/services/autostop-service.js.map +1 -0
- package/dist/services/kill-switch-service.d.ts +118 -0
- package/dist/services/kill-switch-service.d.ts.map +1 -0
- package/dist/services/kill-switch-service.js +291 -0
- package/dist/services/kill-switch-service.js.map +1 -0
- package/dist/services/monitoring/balance-monitor-service.d.ts +65 -0
- package/dist/services/monitoring/balance-monitor-service.d.ts.map +1 -0
- package/dist/services/monitoring/balance-monitor-service.js +207 -0
- package/dist/services/monitoring/balance-monitor-service.js.map +1 -0
- package/dist/services/wc-session-service.d.ts +123 -0
- package/dist/services/wc-session-service.d.ts.map +1 -0
- package/dist/services/wc-session-service.js +363 -0
- package/dist/services/wc-session-service.js.map +1 -0
- package/dist/services/wc-signing-bridge.d.ts +60 -0
- package/dist/services/wc-signing-bridge.d.ts.map +1 -0
- package/dist/services/wc-signing-bridge.js +334 -0
- package/dist/services/wc-signing-bridge.js.map +1 -0
- package/dist/services/wc-storage.d.ts +32 -0
- package/dist/services/wc-storage.d.ts.map +1 -0
- package/dist/services/wc-storage.js +64 -0
- package/dist/services/wc-storage.js.map +1 -0
- package/dist/services/x402/payment-signer.d.ts +88 -0
- package/dist/services/x402/payment-signer.d.ts.map +1 -0
- package/dist/services/x402/payment-signer.js +311 -0
- package/dist/services/x402/payment-signer.js.map +1 -0
- package/dist/services/x402/ssrf-guard.d.ts +27 -0
- package/dist/services/x402/ssrf-guard.d.ts.map +1 -0
- package/dist/services/x402/ssrf-guard.js +236 -0
- package/dist/services/x402/ssrf-guard.js.map +1 -0
- package/dist/services/x402/x402-domain-policy.d.ts +50 -0
- package/dist/services/x402/x402-domain-policy.d.ts.map +1 -0
- package/dist/services/x402/x402-domain-policy.js +78 -0
- package/dist/services/x402/x402-domain-policy.js.map +1 -0
- package/dist/services/x402/x402-handler.d.ts +71 -0
- package/dist/services/x402/x402-handler.d.ts.map +1 -0
- package/dist/services/x402/x402-handler.js +195 -0
- package/dist/services/x402/x402-handler.js.map +1 -0
- package/dist/services/x402/x402-usd-resolver.d.ts +26 -0
- package/dist/services/x402/x402-usd-resolver.d.ts.map +1 -0
- package/dist/services/x402/x402-usd-resolver.js +79 -0
- package/dist/services/x402/x402-usd-resolver.js.map +1 -0
- package/dist/workflow/approval-workflow.d.ts +103 -0
- package/dist/workflow/approval-workflow.d.ts.map +1 -0
- package/dist/workflow/approval-workflow.js +202 -0
- package/dist/workflow/approval-workflow.js.map +1 -0
- package/dist/workflow/delay-queue.d.ts +78 -0
- package/dist/workflow/delay-queue.d.ts.map +1 -0
- package/dist/workflow/delay-queue.js +174 -0
- package/dist/workflow/delay-queue.js.map +1 -0
- package/dist/workflow/index.d.ts +11 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +9 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/owner-state.d.ts +97 -0
- package/dist/workflow/owner-state.d.ts.map +1 -0
- package/dist/workflow/owner-state.js +168 -0
- package/dist/workflow/owner-state.js.map +1 -0
- package/package.json +71 -0
- package/public/admin/assets/index-BPoUSH8W.css +1 -0
- package/public/admin/assets/index-CDi1qoXB.js +1 -0
- package/public/admin/index.html +13 -0
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 6-stage transaction pipeline stages.
|
|
3
|
+
*
|
|
4
|
+
* Stage 1: Validate request + INSERT PENDING transaction (with sessionId audit trail)
|
|
5
|
+
* Stage 2: Auth (sessionId passthrough from route handler)
|
|
6
|
+
* Stage 3: Policy evaluation (evaluateAndReserve TOCTOU-safe + downgradeIfNoOwner)
|
|
7
|
+
* Stage 4: Wait (v1.1 passthrough, INSTANT tier only)
|
|
8
|
+
* Stage 5: On-chain execution (build -> simulate -> sign -> submit)
|
|
9
|
+
* Stage 6: Confirmation wait
|
|
10
|
+
*
|
|
11
|
+
* @see docs/32-pipeline-design.md
|
|
12
|
+
*/
|
|
13
|
+
import { eq } from 'drizzle-orm';
|
|
14
|
+
import { WAIaaSError, ChainError, SendTransactionRequestSchema, TransactionRequestSchema, } from '@waiaas/core';
|
|
15
|
+
import { wallets, transactions, auditLog } from '../infrastructure/database/schema.js';
|
|
16
|
+
import { generateId } from '../infrastructure/database/id.js';
|
|
17
|
+
import { DatabasePolicyEngine } from './database-policy-engine.js';
|
|
18
|
+
import { downgradeIfNoOwner } from '../workflow/owner-state.js';
|
|
19
|
+
import { formatDisplayCurrency } from '@waiaas/core';
|
|
20
|
+
import { resolveEffectiveAmountUsd } from './resolve-effective-amount-usd.js';
|
|
21
|
+
import { sleep } from './sleep.js';
|
|
22
|
+
// v1.5: CoinGecko 키 안내 힌트 최초 1회 추적 (데몬 재시작 시 리셋 OK)
|
|
23
|
+
const hintedTokens = new Set();
|
|
24
|
+
// Exported for test cleanup
|
|
25
|
+
export { hintedTokens };
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Helper: safe request field accessors for union type
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/** Safely extract `amount` from SendTransactionRequest | TransactionRequest. */
|
|
30
|
+
function getRequestAmount(req) {
|
|
31
|
+
if ('amount' in req && typeof req.amount === 'string')
|
|
32
|
+
return req.amount;
|
|
33
|
+
return '0';
|
|
34
|
+
}
|
|
35
|
+
/** Safely extract `to` from SendTransactionRequest | TransactionRequest. */
|
|
36
|
+
function getRequestTo(req) {
|
|
37
|
+
if ('to' in req && typeof req.to === 'string')
|
|
38
|
+
return req.to;
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
/** Safely extract `memo` from SendTransactionRequest | TransactionRequest. */
|
|
42
|
+
function getRequestMemo(req) {
|
|
43
|
+
if ('memo' in req && typeof req.memo === 'string')
|
|
44
|
+
return req.memo;
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Helper: resolve display amount for notification messages
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
/**
|
|
51
|
+
* Convert amountUsd to display currency string for notification variables.
|
|
52
|
+
* Returns empty string on failure (graceful fallback -- no display_amount in message).
|
|
53
|
+
*/
|
|
54
|
+
async function resolveDisplayAmount(amountUsd, settingsService, forexRateService) {
|
|
55
|
+
if (!amountUsd || !settingsService || !forexRateService)
|
|
56
|
+
return '';
|
|
57
|
+
try {
|
|
58
|
+
const currency = settingsService.get('display.currency') ?? 'USD';
|
|
59
|
+
if (currency === 'USD')
|
|
60
|
+
return `($${amountUsd.toFixed(2)})`;
|
|
61
|
+
const rate = await forexRateService.getRate(currency);
|
|
62
|
+
if (!rate)
|
|
63
|
+
return `($${amountUsd.toFixed(2)})`;
|
|
64
|
+
return `(${formatDisplayCurrency(amountUsd, currency, rate.rate)})`;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Helper: extract policy type from evaluation reason string
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
function extractPolicyType(reason) {
|
|
74
|
+
if (!reason)
|
|
75
|
+
return '';
|
|
76
|
+
if (reason.includes('not in allowed list') || reason.includes('Token transfer not allowed'))
|
|
77
|
+
return 'ALLOWED_TOKENS';
|
|
78
|
+
if (reason.includes('not whitelisted') || reason.includes('Contract calls disabled'))
|
|
79
|
+
return 'CONTRACT_WHITELIST';
|
|
80
|
+
if (reason.includes('Method not whitelisted'))
|
|
81
|
+
return 'METHOD_WHITELIST';
|
|
82
|
+
if (reason.includes('not in approved list') || reason.includes('Token approvals disabled'))
|
|
83
|
+
return 'APPROVED_SPENDERS';
|
|
84
|
+
if (reason.includes('not in whitelist') || reason.includes('not in allowed addresses'))
|
|
85
|
+
return 'WHITELIST';
|
|
86
|
+
if (reason.includes('not in allowed networks'))
|
|
87
|
+
return 'ALLOWED_NETWORKS';
|
|
88
|
+
if (reason.includes('exceeds limit') || reason.includes('Unlimited token approval'))
|
|
89
|
+
return 'APPROVE_AMOUNT_LIMIT';
|
|
90
|
+
if (reason.includes('Spending limit'))
|
|
91
|
+
return 'SPENDING_LIMIT';
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
function buildTransactionParam(req, txType, chain) {
|
|
95
|
+
switch (txType) {
|
|
96
|
+
case 'TOKEN_TRANSFER': {
|
|
97
|
+
const r = req;
|
|
98
|
+
return {
|
|
99
|
+
type: 'TOKEN_TRANSFER',
|
|
100
|
+
amount: r.amount,
|
|
101
|
+
toAddress: r.to,
|
|
102
|
+
chain,
|
|
103
|
+
tokenAddress: r.token.address,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
case 'CONTRACT_CALL': {
|
|
107
|
+
const r = req;
|
|
108
|
+
return {
|
|
109
|
+
type: 'CONTRACT_CALL',
|
|
110
|
+
amount: r.value ?? '0',
|
|
111
|
+
toAddress: r.to,
|
|
112
|
+
chain,
|
|
113
|
+
contractAddress: r.to,
|
|
114
|
+
selector: r.calldata?.slice(0, 10),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
case 'APPROVE': {
|
|
118
|
+
const r = req;
|
|
119
|
+
return {
|
|
120
|
+
type: 'APPROVE',
|
|
121
|
+
amount: r.amount,
|
|
122
|
+
toAddress: r.spender,
|
|
123
|
+
chain,
|
|
124
|
+
spenderAddress: r.spender,
|
|
125
|
+
approveAmount: r.amount,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
case 'TRANSFER':
|
|
129
|
+
default: {
|
|
130
|
+
const r = req;
|
|
131
|
+
return {
|
|
132
|
+
type: 'TRANSFER',
|
|
133
|
+
amount: r.amount,
|
|
134
|
+
toAddress: r.to,
|
|
135
|
+
chain,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Stage 1: Validate + DB INSERT
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
export async function stage1Validate(ctx) {
|
|
144
|
+
// Validate request with appropriate Zod schema
|
|
145
|
+
// If request has a `type` field, use discriminatedUnion schema (5-type)
|
|
146
|
+
// Otherwise, use legacy SendTransactionRequestSchema (backward compat)
|
|
147
|
+
const req = ctx.request;
|
|
148
|
+
if ('type' in req && req.type) {
|
|
149
|
+
TransactionRequestSchema.parse(req);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
SendTransactionRequestSchema.parse(req);
|
|
153
|
+
}
|
|
154
|
+
// Determine transaction type from request
|
|
155
|
+
const txType = ('type' in req && req.type) ? req.type : 'TRANSFER';
|
|
156
|
+
// Generate transaction ID
|
|
157
|
+
ctx.txId = generateId();
|
|
158
|
+
// INSERT PENDING transaction into DB
|
|
159
|
+
const now = new Date(Math.floor(Date.now() / 1000) * 1000);
|
|
160
|
+
// Extract common and type-specific fields for DB INSERT
|
|
161
|
+
const amount = 'amount' in req ? req.amount : undefined;
|
|
162
|
+
const toAddress = 'to' in req ? req.to : undefined;
|
|
163
|
+
await ctx.db.insert(transactions).values({
|
|
164
|
+
id: ctx.txId,
|
|
165
|
+
walletId: ctx.walletId,
|
|
166
|
+
chain: ctx.wallet.chain,
|
|
167
|
+
network: ctx.resolvedNetwork,
|
|
168
|
+
type: txType,
|
|
169
|
+
status: 'PENDING',
|
|
170
|
+
amount: amount ?? null,
|
|
171
|
+
toAddress: toAddress ?? null,
|
|
172
|
+
sessionId: ctx.sessionId ?? null,
|
|
173
|
+
createdAt: now,
|
|
174
|
+
});
|
|
175
|
+
// Fire-and-forget: notify TX_REQUESTED (never blocks pipeline)
|
|
176
|
+
// display_amount is empty at Stage 1 -- amountUsd not yet computed
|
|
177
|
+
void ctx.notificationService?.notify('TX_REQUESTED', ctx.walletId, {
|
|
178
|
+
amount: amount ?? '0',
|
|
179
|
+
to: toAddress ?? '',
|
|
180
|
+
type: txType,
|
|
181
|
+
display_amount: '',
|
|
182
|
+
}, { txId: ctx.txId });
|
|
183
|
+
// v1.6: emit wallet:activity TX_REQUESTED event
|
|
184
|
+
ctx.eventBus?.emit('wallet:activity', {
|
|
185
|
+
walletId: ctx.walletId,
|
|
186
|
+
activity: 'TX_REQUESTED',
|
|
187
|
+
details: { txId: ctx.txId },
|
|
188
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Stage 2: Auth (v1.1 passthrough)
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
export async function stage2Auth(_ctx) {
|
|
195
|
+
// sessionId is set on PipelineContext by the route handler from Hono c.get('sessionId').
|
|
196
|
+
// In v1.2 this stage validates session is still active.
|
|
197
|
+
// For now, the sessionAuth middleware already validated the JWT and set sessionId.
|
|
198
|
+
}
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// Stage 3: Policy evaluation
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
export async function stage3Policy(ctx) {
|
|
203
|
+
let evaluation;
|
|
204
|
+
// Determine transaction type from request
|
|
205
|
+
const req = ctx.request;
|
|
206
|
+
const txType = ('type' in req && req.type) ? req.type : 'TRANSFER';
|
|
207
|
+
// Build type-specific TransactionParam (hoisted for notification use)
|
|
208
|
+
const txParam = buildTransactionParam(req, txType, ctx.wallet.chain);
|
|
209
|
+
txParam.network = ctx.resolvedNetwork;
|
|
210
|
+
// [Phase 127] Oracle HTTP 호출 (evaluateAndReserve 진입 전 완료)
|
|
211
|
+
let priceResult;
|
|
212
|
+
if (ctx.priceOracle) {
|
|
213
|
+
priceResult = await resolveEffectiveAmountUsd(req, txType, ctx.wallet.chain, ctx.priceOracle);
|
|
214
|
+
}
|
|
215
|
+
// BATCH type uses evaluateBatch (2-stage policy evaluation)
|
|
216
|
+
if (txType === 'BATCH' && ctx.policyEngine instanceof DatabasePolicyEngine) {
|
|
217
|
+
const batchReq = req;
|
|
218
|
+
// Classify each instruction and build TransactionParam array
|
|
219
|
+
const params = batchReq.instructions.map((instr) => {
|
|
220
|
+
let instrType = 'TRANSFER';
|
|
221
|
+
if ('spender' in instr)
|
|
222
|
+
instrType = 'APPROVE';
|
|
223
|
+
else if ('token' in instr)
|
|
224
|
+
instrType = 'TOKEN_TRANSFER';
|
|
225
|
+
else if ('programId' in instr || 'calldata' in instr)
|
|
226
|
+
instrType = 'CONTRACT_CALL';
|
|
227
|
+
return {
|
|
228
|
+
type: instrType,
|
|
229
|
+
amount: 'amount' in instr ? instr.amount ?? '0' : '0',
|
|
230
|
+
toAddress: 'to' in instr ? instr.to ?? '' : '',
|
|
231
|
+
chain: ctx.wallet.chain,
|
|
232
|
+
network: ctx.resolvedNetwork,
|
|
233
|
+
tokenAddress: 'token' in instr ? instr.token?.address : undefined,
|
|
234
|
+
contractAddress: instrType === 'CONTRACT_CALL' ? ('to' in instr ? instr.to : undefined) : undefined,
|
|
235
|
+
selector: 'calldata' in instr ? instr.calldata?.slice(0, 10) : undefined,
|
|
236
|
+
spenderAddress: 'spender' in instr ? instr.spender : undefined,
|
|
237
|
+
approveAmount: instrType === 'APPROVE' && 'amount' in instr ? instr.amount : undefined,
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
// evaluateBatch에 batchUsdAmount 전달 (Phase 127)
|
|
241
|
+
const batchUsdAmount = priceResult?.type === 'success' ? priceResult.usdAmount : undefined;
|
|
242
|
+
evaluation = await ctx.policyEngine.evaluateBatch(ctx.walletId, params, batchUsdAmount);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// evaluateAndReserve에 usdAmount 전달 (Phase 127)
|
|
246
|
+
const usdAmount = priceResult?.type === 'success' ? priceResult.usdAmount : undefined;
|
|
247
|
+
// Use evaluateAndReserve for TOCTOU-safe evaluation when DatabasePolicyEngine + sqlite available
|
|
248
|
+
if (ctx.policyEngine instanceof DatabasePolicyEngine && ctx.sqlite) {
|
|
249
|
+
evaluation = ctx.policyEngine.evaluateAndReserve(ctx.walletId, txParam, ctx.txId, usdAmount);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
evaluation = await ctx.policyEngine.evaluate(ctx.walletId, txParam);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (!evaluation.allowed) {
|
|
256
|
+
// Update tx status to CANCELLED (REJECTED not in TRANSACTION_STATUSES enum)
|
|
257
|
+
await ctx.db
|
|
258
|
+
.update(transactions)
|
|
259
|
+
.set({ status: 'CANCELLED', error: evaluation.reason ?? 'Policy denied' })
|
|
260
|
+
.where(eq(transactions.id, ctx.txId));
|
|
261
|
+
// Fire-and-forget: notify POLICY_VIOLATION (never blocks pipeline)
|
|
262
|
+
void ctx.notificationService?.notify('POLICY_VIOLATION', ctx.walletId, {
|
|
263
|
+
reason: evaluation.reason ?? 'Policy denied',
|
|
264
|
+
amount: getRequestAmount(ctx.request),
|
|
265
|
+
to: getRequestTo(ctx.request),
|
|
266
|
+
policyType: extractPolicyType(evaluation.reason),
|
|
267
|
+
tokenAddress: txParam.tokenAddress ?? '',
|
|
268
|
+
contractAddress: txParam.contractAddress ?? '',
|
|
269
|
+
adminLink: '/admin/policies',
|
|
270
|
+
}, { txId: ctx.txId });
|
|
271
|
+
throw new WAIaaSError('POLICY_DENIED', {
|
|
272
|
+
message: evaluation.reason ?? 'Transaction denied by policy',
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
let tier = evaluation.tier;
|
|
276
|
+
let downgraded = false;
|
|
277
|
+
// [Phase 127] PriceResult에 따른 후처리
|
|
278
|
+
if (priceResult?.type === 'notListed') {
|
|
279
|
+
// 감사 로그: UNLISTED_TOKEN_TRANSFER
|
|
280
|
+
await ctx.db.insert(auditLog).values({
|
|
281
|
+
timestamp: new Date(Math.floor(Date.now() / 1000) * 1000),
|
|
282
|
+
eventType: 'UNLISTED_TOKEN_TRANSFER',
|
|
283
|
+
actor: ctx.sessionId ?? 'system',
|
|
284
|
+
walletId: ctx.walletId,
|
|
285
|
+
txId: ctx.txId,
|
|
286
|
+
details: JSON.stringify({
|
|
287
|
+
tokenAddress: priceResult.tokenAddress,
|
|
288
|
+
chain: priceResult.chain,
|
|
289
|
+
failedCount: priceResult.failedCount,
|
|
290
|
+
}),
|
|
291
|
+
severity: 'warning',
|
|
292
|
+
});
|
|
293
|
+
// 최소 NOTIFY 격상 (evaluation tier와 NOTIFY 중 보수적)
|
|
294
|
+
const TIER_ORDER = ['INSTANT', 'NOTIFY', 'DELAY', 'APPROVAL'];
|
|
295
|
+
const currentIdx = TIER_ORDER.indexOf(tier);
|
|
296
|
+
const notifyIdx = TIER_ORDER.indexOf('NOTIFY');
|
|
297
|
+
if (currentIdx < notifyIdx) {
|
|
298
|
+
tier = 'NOTIFY';
|
|
299
|
+
}
|
|
300
|
+
// CoinGecko 키 미설정 + 최초 1회 힌트
|
|
301
|
+
const cacheKey = `${priceResult.chain}:${priceResult.tokenAddress}`;
|
|
302
|
+
const coingeckoKey = ctx.settingsService?.get('oracle.coingecko_api_key');
|
|
303
|
+
const shouldShowHint = !coingeckoKey && !hintedTokens.has(cacheKey);
|
|
304
|
+
if (shouldShowHint) {
|
|
305
|
+
hintedTokens.add(cacheKey);
|
|
306
|
+
}
|
|
307
|
+
// 가격 불명 토큰 알림 발송 (allowed=true인 상태에서도 notListed는 발생)
|
|
308
|
+
const hint = shouldShowHint
|
|
309
|
+
? 'CoinGecko API 키를 설정하면 이 토큰의 USD 가격을 조회할 수 있습니다. Admin Settings > Oracle에서 설정하세요.'
|
|
310
|
+
: undefined;
|
|
311
|
+
const notifyVars = {
|
|
312
|
+
amount: getRequestAmount(ctx.request),
|
|
313
|
+
to: getRequestTo(ctx.request),
|
|
314
|
+
reason: `가격 불명 토큰 (${priceResult.tokenAddress}) -- 최소 NOTIFY 격상`,
|
|
315
|
+
policyType: 'SPENDING_LIMIT',
|
|
316
|
+
};
|
|
317
|
+
if (hint)
|
|
318
|
+
notifyVars.hint = hint;
|
|
319
|
+
void ctx.notificationService?.notify('POLICY_VIOLATION', ctx.walletId, notifyVars, { txId: ctx.txId });
|
|
320
|
+
}
|
|
321
|
+
// oracleDown: 아무것도 하지 않음 -- 네이티브 금액 기준 tier가 이미 설정됨
|
|
322
|
+
// Check for APPROVAL -> DELAY downgrade when no owner registered
|
|
323
|
+
if (tier === 'APPROVAL') {
|
|
324
|
+
const walletRow = await ctx.db.select().from(wallets).where(eq(wallets.id, ctx.walletId)).get();
|
|
325
|
+
if (walletRow) {
|
|
326
|
+
const result = downgradeIfNoOwner({
|
|
327
|
+
ownerAddress: walletRow.ownerAddress ?? null,
|
|
328
|
+
ownerVerified: walletRow.ownerVerified ?? false,
|
|
329
|
+
}, tier);
|
|
330
|
+
tier = result.tier;
|
|
331
|
+
downgraded = result.downgraded;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Set tier and metadata on context
|
|
335
|
+
ctx.tier = tier;
|
|
336
|
+
ctx.downgraded = downgraded;
|
|
337
|
+
if (evaluation.delaySeconds !== undefined) {
|
|
338
|
+
ctx.delaySeconds = evaluation.delaySeconds;
|
|
339
|
+
}
|
|
340
|
+
// [Phase 139] Cache amountUsd on context for Stage 5/6 display_amount
|
|
341
|
+
const stageAmountUsd = priceResult?.type === 'success' ? priceResult.usdAmount : undefined;
|
|
342
|
+
ctx.amountUsd = stageAmountUsd;
|
|
343
|
+
// [Phase 139] Resolve display amount for notifications
|
|
344
|
+
const displayAmount = await resolveDisplayAmount(stageAmountUsd ?? null, ctx.settingsService, ctx.forexRateService);
|
|
345
|
+
// [Phase 136] Cumulative spending warning notification (80% threshold)
|
|
346
|
+
if (evaluation.cumulativeWarning) {
|
|
347
|
+
const w = evaluation.cumulativeWarning;
|
|
348
|
+
void ctx.notificationService?.notify('CUMULATIVE_LIMIT_WARNING', ctx.walletId, {
|
|
349
|
+
type: w.type,
|
|
350
|
+
spent: String(w.spent.toFixed(2)),
|
|
351
|
+
limit: String(w.limit.toFixed(2)),
|
|
352
|
+
ratio: String(Math.round(w.ratio * 100)),
|
|
353
|
+
display_amount: displayAmount,
|
|
354
|
+
}, { txId: ctx.txId });
|
|
355
|
+
}
|
|
356
|
+
// [Phase 136] APPROVAL tier notification with reason
|
|
357
|
+
if (tier === 'APPROVAL' && !downgraded) {
|
|
358
|
+
const reason = evaluation.approvalReason ?? 'per_tx';
|
|
359
|
+
void ctx.notificationService?.notify('TX_APPROVAL_REQUIRED', ctx.walletId, {
|
|
360
|
+
amount: getRequestAmount(ctx.request),
|
|
361
|
+
to: getRequestTo(ctx.request),
|
|
362
|
+
reason,
|
|
363
|
+
display_amount: displayAmount,
|
|
364
|
+
}, { txId: ctx.txId });
|
|
365
|
+
}
|
|
366
|
+
// Update DB with tier
|
|
367
|
+
await ctx.db
|
|
368
|
+
.update(transactions)
|
|
369
|
+
.set({ tier })
|
|
370
|
+
.where(eq(transactions.id, ctx.txId));
|
|
371
|
+
}
|
|
372
|
+
// ---------------------------------------------------------------------------
|
|
373
|
+
// Stage 4: Wait (DELAY/APPROVAL branching, INSTANT/NOTIFY passthrough)
|
|
374
|
+
// ---------------------------------------------------------------------------
|
|
375
|
+
export async function stage4Wait(ctx) {
|
|
376
|
+
const tier = ctx.tier;
|
|
377
|
+
// INSTANT and NOTIFY: pass through to stage5
|
|
378
|
+
if (tier === 'INSTANT' || tier === 'NOTIFY') {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
// DELAY: queue with cooldown, halt pipeline
|
|
382
|
+
if (tier === 'DELAY') {
|
|
383
|
+
if (!ctx.delayQueue) {
|
|
384
|
+
// Fallback: if no DelayQueue, treat as INSTANT (backward compat)
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const delaySeconds = ctx.delaySeconds
|
|
388
|
+
?? ctx.config?.policy_defaults_delay_seconds
|
|
389
|
+
?? 60;
|
|
390
|
+
ctx.delayQueue.queueDelay(ctx.txId, delaySeconds);
|
|
391
|
+
// Halt pipeline -- transaction will be picked up by processExpired worker
|
|
392
|
+
throw new WAIaaSError('PIPELINE_HALTED', {
|
|
393
|
+
message: `Transaction ${ctx.txId} queued for ${delaySeconds}s delay`,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
// APPROVAL: create pending approval, halt pipeline
|
|
397
|
+
if (tier === 'APPROVAL') {
|
|
398
|
+
if (!ctx.approvalWorkflow) {
|
|
399
|
+
// Fallback: if no ApprovalWorkflow, treat as INSTANT (backward compat)
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
ctx.approvalWorkflow.requestApproval(ctx.txId);
|
|
403
|
+
// v1.6.1: fire-and-forget WC signing request (non-blocking)
|
|
404
|
+
if (ctx.wcSigningBridge) {
|
|
405
|
+
void ctx.wcSigningBridge.requestSignature(ctx.walletId, ctx.txId, ctx.wallet.chain);
|
|
406
|
+
}
|
|
407
|
+
// Halt pipeline -- transaction will be picked up by approve/reject/expire
|
|
408
|
+
throw new WAIaaSError('PIPELINE_HALTED', {
|
|
409
|
+
message: `Transaction ${ctx.txId} queued for owner approval`,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
// Helper: buildByType -- route to correct adapter method based on request.type
|
|
415
|
+
// ---------------------------------------------------------------------------
|
|
416
|
+
/**
|
|
417
|
+
* Build unsigned transaction by dispatching to the correct IChainAdapter method
|
|
418
|
+
* based on request.type (TRANSFER/TOKEN_TRANSFER/CONTRACT_CALL/APPROVE/BATCH).
|
|
419
|
+
*/
|
|
420
|
+
async function buildByType(adapter, request, walletPublicKey) {
|
|
421
|
+
const type = ('type' in request && request.type) || 'TRANSFER';
|
|
422
|
+
switch (type) {
|
|
423
|
+
case 'TRANSFER': {
|
|
424
|
+
return adapter.buildTransaction({
|
|
425
|
+
from: walletPublicKey,
|
|
426
|
+
to: getRequestTo(request),
|
|
427
|
+
amount: BigInt(getRequestAmount(request)),
|
|
428
|
+
memo: getRequestMemo(request),
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
case 'TOKEN_TRANSFER': {
|
|
432
|
+
const req = request;
|
|
433
|
+
return adapter.buildTokenTransfer({
|
|
434
|
+
from: walletPublicKey,
|
|
435
|
+
to: req.to,
|
|
436
|
+
amount: BigInt(req.amount),
|
|
437
|
+
token: req.token,
|
|
438
|
+
memo: req.memo,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
case 'CONTRACT_CALL': {
|
|
442
|
+
const req = request;
|
|
443
|
+
return adapter.buildContractCall({
|
|
444
|
+
from: walletPublicKey,
|
|
445
|
+
to: req.to,
|
|
446
|
+
calldata: req.calldata,
|
|
447
|
+
abi: req.abi,
|
|
448
|
+
value: req.value ? BigInt(req.value) : undefined,
|
|
449
|
+
programId: req.programId,
|
|
450
|
+
instructionData: req.instructionData
|
|
451
|
+
? Buffer.from(req.instructionData, 'base64')
|
|
452
|
+
: undefined,
|
|
453
|
+
accounts: req.accounts,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
case 'APPROVE': {
|
|
457
|
+
const req = request;
|
|
458
|
+
return adapter.buildApprove({
|
|
459
|
+
from: walletPublicKey,
|
|
460
|
+
spender: req.spender,
|
|
461
|
+
token: req.token,
|
|
462
|
+
amount: BigInt(req.amount),
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
case 'BATCH': {
|
|
466
|
+
const req = request;
|
|
467
|
+
return adapter.buildBatch({
|
|
468
|
+
from: walletPublicKey,
|
|
469
|
+
instructions: req.instructions.map((instr) => {
|
|
470
|
+
// Classify by field presence (same logic as classifyInstruction in Phase 80)
|
|
471
|
+
if ('spender' in instr) {
|
|
472
|
+
const a = instr;
|
|
473
|
+
return {
|
|
474
|
+
from: walletPublicKey,
|
|
475
|
+
spender: a.spender,
|
|
476
|
+
token: a.token,
|
|
477
|
+
amount: BigInt(a.amount),
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
if ('token' in instr) {
|
|
481
|
+
const t = instr;
|
|
482
|
+
return {
|
|
483
|
+
from: walletPublicKey,
|
|
484
|
+
to: t.to,
|
|
485
|
+
amount: BigInt(t.amount),
|
|
486
|
+
token: t.token,
|
|
487
|
+
memo: t.memo,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
if ('programId' in instr || 'calldata' in instr) {
|
|
491
|
+
const c = instr;
|
|
492
|
+
return {
|
|
493
|
+
from: walletPublicKey,
|
|
494
|
+
to: c.to,
|
|
495
|
+
calldata: c.calldata,
|
|
496
|
+
programId: c.programId,
|
|
497
|
+
instructionData: c.instructionData
|
|
498
|
+
? Buffer.from(c.instructionData, 'base64')
|
|
499
|
+
: undefined,
|
|
500
|
+
accounts: c.accounts,
|
|
501
|
+
value: c.value ? BigInt(c.value) : undefined,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
// Default: TRANSFER instruction
|
|
505
|
+
const tr = instr;
|
|
506
|
+
return {
|
|
507
|
+
from: walletPublicKey,
|
|
508
|
+
to: tr.to,
|
|
509
|
+
amount: BigInt(tr.amount),
|
|
510
|
+
memo: tr.memo,
|
|
511
|
+
};
|
|
512
|
+
}),
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
default:
|
|
516
|
+
throw new WAIaaSError('CHAIN_ERROR', {
|
|
517
|
+
message: `Unknown transaction type: ${type}`,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// ---------------------------------------------------------------------------
|
|
522
|
+
// Stage 5: On-chain execution (CONC-01 retry loop)
|
|
523
|
+
// ---------------------------------------------------------------------------
|
|
524
|
+
/**
|
|
525
|
+
* Stage 5: Build -> Simulate -> Sign -> Submit with CONC-01 retry logic.
|
|
526
|
+
*
|
|
527
|
+
* ChainError category-based retry:
|
|
528
|
+
* - PERMANENT: immediate FAILED, no retry
|
|
529
|
+
* - TRANSIENT: exponential backoff (1s, 2s, 4s), max 3 retries (retryCount >= 3 guard)
|
|
530
|
+
* - STALE: rebuild from Stage 5a, max 1 (retryCount >= 1 guard)
|
|
531
|
+
*
|
|
532
|
+
* retryCount is shared between TRANSIENT and STALE to limit total retry count.
|
|
533
|
+
* Total attempts: initial 1 + up to 3 retries = 4 max.
|
|
534
|
+
*/
|
|
535
|
+
export async function stage5Execute(ctx) {
|
|
536
|
+
const reqAmount = getRequestAmount(ctx.request);
|
|
537
|
+
const reqTo = getRequestTo(ctx.request);
|
|
538
|
+
// [Phase 139] Resolve display amount once for all Stage 5 notifications
|
|
539
|
+
const displayAmount = await resolveDisplayAmount(ctx.amountUsd ?? null, ctx.settingsService, ctx.forexRateService);
|
|
540
|
+
let retryCount = 0;
|
|
541
|
+
// Outer buildLoop: STALE errors return here to rebuild from Stage 5a
|
|
542
|
+
buildLoop: while (true) {
|
|
543
|
+
try {
|
|
544
|
+
// Stage 5a: Build unsigned transaction (type-routed)
|
|
545
|
+
ctx.unsignedTx = await buildByType(ctx.adapter, ctx.request, ctx.wallet.publicKey);
|
|
546
|
+
// Stage 5b: Simulate
|
|
547
|
+
const simResult = await ctx.adapter.simulateTransaction(ctx.unsignedTx);
|
|
548
|
+
if (!simResult.success) {
|
|
549
|
+
await ctx.db
|
|
550
|
+
.update(transactions)
|
|
551
|
+
.set({ status: 'FAILED', error: simResult.error ?? 'Simulation failed' })
|
|
552
|
+
.where(eq(transactions.id, ctx.txId));
|
|
553
|
+
// Fire-and-forget: notify TX_FAILED on simulation failure
|
|
554
|
+
void ctx.notificationService?.notify('TX_FAILED', ctx.walletId, {
|
|
555
|
+
reason: simResult.error ?? 'Simulation failed',
|
|
556
|
+
amount: reqAmount,
|
|
557
|
+
display_amount: displayAmount,
|
|
558
|
+
}, { txId: ctx.txId });
|
|
559
|
+
// v1.6: emit transaction:failed event (simulation failure)
|
|
560
|
+
ctx.eventBus?.emit('transaction:failed', {
|
|
561
|
+
walletId: ctx.walletId,
|
|
562
|
+
txId: ctx.txId,
|
|
563
|
+
error: simResult.error ?? 'Simulation failed',
|
|
564
|
+
network: ctx.resolvedNetwork,
|
|
565
|
+
type: ('type' in ctx.request && ctx.request.type) ? ctx.request.type : 'TRANSFER',
|
|
566
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
567
|
+
});
|
|
568
|
+
throw new WAIaaSError('SIMULATION_FAILED', {
|
|
569
|
+
message: simResult.error ?? 'Transaction simulation failed',
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
// Stage 5c: Decrypt private key, sign
|
|
573
|
+
// CRITICAL: key MUST be released in finally block
|
|
574
|
+
let privateKey = null;
|
|
575
|
+
try {
|
|
576
|
+
privateKey = await ctx.keyStore.decryptPrivateKey(ctx.walletId, ctx.masterPassword);
|
|
577
|
+
ctx.signedTx = await ctx.adapter.signTransaction(ctx.unsignedTx, privateKey);
|
|
578
|
+
}
|
|
579
|
+
finally {
|
|
580
|
+
if (privateKey) {
|
|
581
|
+
ctx.keyStore.releaseKey(privateKey);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Stage 5d: Submit
|
|
585
|
+
ctx.submitResult = await ctx.adapter.submitTransaction(ctx.signedTx);
|
|
586
|
+
// Success: Update DB SUBMITTED + txHash
|
|
587
|
+
await ctx.db
|
|
588
|
+
.update(transactions)
|
|
589
|
+
.set({ status: 'SUBMITTED', txHash: ctx.submitResult.txHash })
|
|
590
|
+
.where(eq(transactions.id, ctx.txId));
|
|
591
|
+
// Fire-and-forget: notify TX_SUBMITTED
|
|
592
|
+
void ctx.notificationService?.notify('TX_SUBMITTED', ctx.walletId, {
|
|
593
|
+
txHash: ctx.submitResult.txHash,
|
|
594
|
+
amount: reqAmount,
|
|
595
|
+
to: reqTo,
|
|
596
|
+
display_amount: displayAmount,
|
|
597
|
+
}, { txId: ctx.txId });
|
|
598
|
+
// v1.6: emit wallet:activity TX_SUBMITTED event
|
|
599
|
+
ctx.eventBus?.emit('wallet:activity', {
|
|
600
|
+
walletId: ctx.walletId,
|
|
601
|
+
activity: 'TX_SUBMITTED',
|
|
602
|
+
details: { txId: ctx.txId, txHash: ctx.submitResult.txHash },
|
|
603
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
604
|
+
});
|
|
605
|
+
return; // Success -- exit the loop
|
|
606
|
+
}
|
|
607
|
+
catch (err) {
|
|
608
|
+
// Non-ChainError: rethrow as-is (WAIaaSError, validation errors, etc.)
|
|
609
|
+
if (!(err instanceof ChainError)) {
|
|
610
|
+
throw err;
|
|
611
|
+
}
|
|
612
|
+
// ChainError: category-based retry logic
|
|
613
|
+
switch (err.category) {
|
|
614
|
+
case 'PERMANENT': {
|
|
615
|
+
// Immediate failure, no retry
|
|
616
|
+
await ctx.db
|
|
617
|
+
.update(transactions)
|
|
618
|
+
.set({ status: 'FAILED', error: err.message })
|
|
619
|
+
.where(eq(transactions.id, ctx.txId));
|
|
620
|
+
// Fire-and-forget: notify TX_FAILED
|
|
621
|
+
void ctx.notificationService?.notify('TX_FAILED', ctx.walletId, {
|
|
622
|
+
reason: err.message,
|
|
623
|
+
amount: reqAmount,
|
|
624
|
+
display_amount: displayAmount,
|
|
625
|
+
}, { txId: ctx.txId });
|
|
626
|
+
// v1.6: emit transaction:failed event (permanent chain error)
|
|
627
|
+
ctx.eventBus?.emit('transaction:failed', {
|
|
628
|
+
walletId: ctx.walletId,
|
|
629
|
+
txId: ctx.txId,
|
|
630
|
+
error: err.message,
|
|
631
|
+
network: ctx.resolvedNetwork,
|
|
632
|
+
type: ('type' in ctx.request && ctx.request.type) ? ctx.request.type : 'TRANSFER',
|
|
633
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
634
|
+
});
|
|
635
|
+
throw new WAIaaSError('CHAIN_ERROR', {
|
|
636
|
+
message: err.message,
|
|
637
|
+
cause: err,
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
case 'TRANSIENT': {
|
|
641
|
+
if (retryCount >= 3) {
|
|
642
|
+
// Max retries exhausted
|
|
643
|
+
await ctx.db
|
|
644
|
+
.update(transactions)
|
|
645
|
+
.set({ status: 'FAILED', error: `${err.code} (max retries exceeded)` })
|
|
646
|
+
.where(eq(transactions.id, ctx.txId));
|
|
647
|
+
// Fire-and-forget: notify TX_FAILED
|
|
648
|
+
void ctx.notificationService?.notify('TX_FAILED', ctx.walletId, {
|
|
649
|
+
reason: `${err.code} (max retries exceeded)`,
|
|
650
|
+
amount: reqAmount,
|
|
651
|
+
display_amount: displayAmount,
|
|
652
|
+
}, { txId: ctx.txId });
|
|
653
|
+
// v1.6: emit transaction:failed event (transient max retries)
|
|
654
|
+
ctx.eventBus?.emit('transaction:failed', {
|
|
655
|
+
walletId: ctx.walletId,
|
|
656
|
+
txId: ctx.txId,
|
|
657
|
+
error: `${err.code} (max retries exceeded)`,
|
|
658
|
+
network: ctx.resolvedNetwork,
|
|
659
|
+
type: ('type' in ctx.request && ctx.request.type) ? ctx.request.type : 'TRANSFER',
|
|
660
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
661
|
+
});
|
|
662
|
+
throw new WAIaaSError('CHAIN_ERROR', {
|
|
663
|
+
message: `${err.message} (max retries exceeded)`,
|
|
664
|
+
cause: err,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
// Exponential backoff: 1s, 2s, 4s
|
|
668
|
+
await sleep(1000 * Math.pow(2, retryCount));
|
|
669
|
+
retryCount++;
|
|
670
|
+
continue buildLoop; // Retry from Stage 5a (rebuild)
|
|
671
|
+
}
|
|
672
|
+
case 'STALE': {
|
|
673
|
+
if (retryCount >= 1) {
|
|
674
|
+
// Stale retry exhausted (shared retryCount)
|
|
675
|
+
await ctx.db
|
|
676
|
+
.update(transactions)
|
|
677
|
+
.set({ status: 'FAILED', error: `${err.code} (stale retry exhausted)` })
|
|
678
|
+
.where(eq(transactions.id, ctx.txId));
|
|
679
|
+
// Fire-and-forget: notify TX_FAILED
|
|
680
|
+
void ctx.notificationService?.notify('TX_FAILED', ctx.walletId, {
|
|
681
|
+
reason: `${err.code} (stale retry exhausted)`,
|
|
682
|
+
amount: reqAmount,
|
|
683
|
+
display_amount: displayAmount,
|
|
684
|
+
}, { txId: ctx.txId });
|
|
685
|
+
// v1.6: emit transaction:failed event (stale retry exhausted)
|
|
686
|
+
ctx.eventBus?.emit('transaction:failed', {
|
|
687
|
+
walletId: ctx.walletId,
|
|
688
|
+
txId: ctx.txId,
|
|
689
|
+
error: `${err.code} (stale retry exhausted)`,
|
|
690
|
+
network: ctx.resolvedNetwork,
|
|
691
|
+
type: ('type' in ctx.request && ctx.request.type) ? ctx.request.type : 'TRANSFER',
|
|
692
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
693
|
+
});
|
|
694
|
+
throw new WAIaaSError('CHAIN_ERROR', {
|
|
695
|
+
message: `${err.message} (stale retry exhausted)`,
|
|
696
|
+
cause: err,
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
// Rebuild from Stage 5a with new blockhash/nonce
|
|
700
|
+
retryCount++;
|
|
701
|
+
continue buildLoop;
|
|
702
|
+
}
|
|
703
|
+
default: {
|
|
704
|
+
// Unknown category: treat as permanent
|
|
705
|
+
await ctx.db
|
|
706
|
+
.update(transactions)
|
|
707
|
+
.set({ status: 'FAILED', error: err.message })
|
|
708
|
+
.where(eq(transactions.id, ctx.txId));
|
|
709
|
+
throw new WAIaaSError('CHAIN_ERROR', {
|
|
710
|
+
message: err.message,
|
|
711
|
+
cause: err,
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
// ---------------------------------------------------------------------------
|
|
719
|
+
// Stage 6: Confirmation wait
|
|
720
|
+
// ---------------------------------------------------------------------------
|
|
721
|
+
export async function stage6Confirm(ctx) {
|
|
722
|
+
const reqAmount = getRequestAmount(ctx.request);
|
|
723
|
+
const reqTo = getRequestTo(ctx.request);
|
|
724
|
+
// [Phase 139] Resolve display amount for Stage 6 notifications
|
|
725
|
+
const displayAmount = await resolveDisplayAmount(ctx.amountUsd ?? null, ctx.settingsService, ctx.forexRateService);
|
|
726
|
+
const result = await ctx.adapter.waitForConfirmation(ctx.submitResult.txHash, 30_000);
|
|
727
|
+
if (result.status === 'confirmed' || result.status === 'finalized') {
|
|
728
|
+
// On-chain confirmed
|
|
729
|
+
const executedAt = new Date(Math.floor(Date.now() / 1000) * 1000);
|
|
730
|
+
await ctx.db
|
|
731
|
+
.update(transactions)
|
|
732
|
+
.set({ status: 'CONFIRMED', executedAt })
|
|
733
|
+
.where(eq(transactions.id, ctx.txId));
|
|
734
|
+
// Fire-and-forget: notify TX_CONFIRMED (never blocks pipeline)
|
|
735
|
+
void ctx.notificationService?.notify('TX_CONFIRMED', ctx.walletId, {
|
|
736
|
+
txHash: ctx.submitResult.txHash,
|
|
737
|
+
amount: reqAmount,
|
|
738
|
+
to: reqTo,
|
|
739
|
+
display_amount: displayAmount,
|
|
740
|
+
}, { txId: ctx.txId });
|
|
741
|
+
// v1.6: emit transaction:completed event
|
|
742
|
+
ctx.eventBus?.emit('transaction:completed', {
|
|
743
|
+
walletId: ctx.walletId,
|
|
744
|
+
txId: ctx.txId,
|
|
745
|
+
txHash: ctx.submitResult.txHash,
|
|
746
|
+
amount: getRequestAmount(ctx.request),
|
|
747
|
+
network: ctx.resolvedNetwork,
|
|
748
|
+
type: ('type' in ctx.request && ctx.request.type) ? ctx.request.type : 'TRANSFER',
|
|
749
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
else if (result.status === 'failed') {
|
|
753
|
+
// On-chain revert
|
|
754
|
+
await ctx.db
|
|
755
|
+
.update(transactions)
|
|
756
|
+
.set({ status: 'FAILED', error: 'Transaction reverted on-chain' })
|
|
757
|
+
.where(eq(transactions.id, ctx.txId));
|
|
758
|
+
// Fire-and-forget: notify TX_FAILED on on-chain revert (never blocks pipeline)
|
|
759
|
+
void ctx.notificationService?.notify('TX_FAILED', ctx.walletId, {
|
|
760
|
+
reason: 'Transaction reverted on-chain',
|
|
761
|
+
amount: reqAmount,
|
|
762
|
+
display_amount: displayAmount,
|
|
763
|
+
}, { txId: ctx.txId });
|
|
764
|
+
// v1.6: emit transaction:failed event (on-chain revert)
|
|
765
|
+
ctx.eventBus?.emit('transaction:failed', {
|
|
766
|
+
walletId: ctx.walletId,
|
|
767
|
+
txId: ctx.txId,
|
|
768
|
+
error: 'Transaction reverted on-chain',
|
|
769
|
+
network: ctx.resolvedNetwork,
|
|
770
|
+
type: ('type' in ctx.request && ctx.request.type) ? ctx.request.type : 'TRANSFER',
|
|
771
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
772
|
+
});
|
|
773
|
+
throw new WAIaaSError('CHAIN_ERROR', {
|
|
774
|
+
message: 'Transaction reverted on-chain',
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
// status === 'submitted': still pending, NOT failed
|
|
779
|
+
// Keep SUBMITTED status (already set by Stage 5)
|
|
780
|
+
// Do NOT overwrite to FAILED -- tx may confirm later
|
|
781
|
+
// No notification: no state change occurred
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
//# sourceMappingURL=stages.js.map
|