@waiaas/daemon 2.11.0-rc.8 → 2.11.0
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/README.md +5 -5
- package/dist/api/middleware/address-validation.d.ts +6 -33
- package/dist/api/middleware/address-validation.d.ts.map +1 -1
- package/dist/api/middleware/address-validation.js +5 -129
- package/dist/api/middleware/address-validation.js.map +1 -1
- package/dist/api/middleware/host-guard.d.ts +1 -1
- package/dist/api/middleware/host-guard.js +2 -2
- package/dist/api/middleware/host-guard.js.map +1 -1
- package/dist/api/middleware/index.d.ts +1 -0
- package/dist/api/middleware/index.d.ts.map +1 -1
- package/dist/api/middleware/index.js +1 -0
- package/dist/api/middleware/index.js.map +1 -1
- package/dist/api/middleware/master-auth.d.ts +2 -5
- package/dist/api/middleware/master-auth.d.ts.map +1 -1
- package/dist/api/middleware/master-auth.js.map +1 -1
- package/dist/api/middleware/rate-limiter.d.ts +51 -0
- package/dist/api/middleware/rate-limiter.d.ts.map +1 -0
- package/dist/api/middleware/rate-limiter.js +146 -0
- package/dist/api/middleware/rate-limiter.js.map +1 -0
- package/dist/api/middleware/siwe-verify.d.ts +6 -26
- package/dist/api/middleware/siwe-verify.d.ts.map +1 -1
- package/dist/api/middleware/siwe-verify.js +5 -50
- package/dist/api/middleware/siwe-verify.js.map +1 -1
- package/dist/api/routes/actions.d.ts +1 -0
- package/dist/api/routes/actions.d.ts.map +1 -1
- package/dist/api/routes/actions.js +52 -4
- package/dist/api/routes/actions.js.map +1 -1
- package/dist/api/routes/admin-actions.d.ts +1 -0
- package/dist/api/routes/admin-actions.d.ts.map +1 -1
- package/dist/api/routes/admin-actions.js +3 -3
- package/dist/api/routes/admin-actions.js.map +1 -1
- package/dist/api/routes/admin-auth.d.ts.map +1 -1
- package/dist/api/routes/admin-auth.js +12 -7
- package/dist/api/routes/admin-auth.js.map +1 -1
- package/dist/api/routes/admin-credentials.js +2 -2
- package/dist/api/routes/admin-credentials.js.map +1 -1
- package/dist/api/routes/admin-monitoring.d.ts +10 -0
- package/dist/api/routes/admin-monitoring.d.ts.map +1 -1
- package/dist/api/routes/admin-monitoring.js +59 -14
- package/dist/api/routes/admin-monitoring.js.map +1 -1
- package/dist/api/routes/admin-notifications.d.ts.map +1 -1
- package/dist/api/routes/admin-notifications.js +2 -15
- package/dist/api/routes/admin-notifications.js.map +1 -1
- package/dist/api/routes/admin-settings.d.ts.map +1 -1
- package/dist/api/routes/admin-settings.js +90 -1
- package/dist/api/routes/admin-settings.js.map +1 -1
- package/dist/api/routes/admin-wallets.d.ts +16 -1
- package/dist/api/routes/admin-wallets.d.ts.map +1 -1
- package/dist/api/routes/admin-wallets.js +64 -75
- package/dist/api/routes/admin-wallets.js.map +1 -1
- package/dist/api/routes/admin.d.ts +1 -0
- package/dist/api/routes/admin.d.ts.map +1 -1
- package/dist/api/routes/admin.js.map +1 -1
- package/dist/api/routes/credentials.js +2 -2
- package/dist/api/routes/credentials.js.map +1 -1
- package/dist/api/routes/defi-positions.js.map +1 -1
- package/dist/api/routes/nfts.js.map +1 -1
- package/dist/api/routes/openapi-schemas.d.ts +412 -12
- package/dist/api/routes/openapi-schemas.d.ts.map +1 -1
- package/dist/api/routes/openapi-schemas.js +38 -5
- package/dist/api/routes/openapi-schemas.js.map +1 -1
- package/dist/api/routes/policies.d.ts +2 -0
- package/dist/api/routes/policies.d.ts.map +1 -1
- package/dist/api/routes/policies.js +55 -6
- package/dist/api/routes/policies.js.map +1 -1
- package/dist/api/routes/rpc-proxy.js.map +1 -1
- package/dist/api/routes/sessions.d.ts.map +1 -1
- package/dist/api/routes/sessions.js +47 -28
- package/dist/api/routes/sessions.js.map +1 -1
- package/dist/api/routes/staking.d.ts.map +1 -1
- package/dist/api/routes/staking.js +4 -76
- package/dist/api/routes/staking.js.map +1 -1
- package/dist/api/routes/tokens.d.ts.map +1 -1
- package/dist/api/routes/tokens.js.map +1 -1
- package/dist/api/routes/transactions.d.ts +1 -0
- package/dist/api/routes/transactions.d.ts.map +1 -1
- package/dist/api/routes/transactions.js +8 -2
- package/dist/api/routes/transactions.js.map +1 -1
- package/dist/api/routes/userop.d.ts.map +1 -1
- package/dist/api/routes/userop.js +0 -2
- package/dist/api/routes/userop.js.map +1 -1
- package/dist/api/routes/wallet-apps.d.ts.map +1 -1
- package/dist/api/routes/wallet-apps.js +20 -13
- package/dist/api/routes/wallet-apps.js.map +1 -1
- package/dist/api/routes/wallet.js.map +1 -1
- package/dist/api/routes/wallets.d.ts.map +1 -1
- package/dist/api/routes/wallets.js +3 -0
- package/dist/api/routes/wallets.js.map +1 -1
- package/dist/api/routes/wc.d.ts.map +1 -1
- package/dist/api/routes/wc.js +13 -8
- package/dist/api/routes/wc.js.map +1 -1
- package/dist/api/routes/x402.d.ts.map +1 -1
- package/dist/api/routes/x402.js +1 -2
- package/dist/api/routes/x402.js.map +1 -1
- package/dist/api/server.d.ts +8 -4
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +46 -5
- package/dist/api/server.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/infrastructure/action/action-provider-registry.d.ts.map +1 -1
- package/dist/infrastructure/action/action-provider-registry.js +2 -3
- package/dist/infrastructure/action/action-provider-registry.js.map +1 -1
- package/dist/infrastructure/action/builtin-metadata.d.ts +22 -0
- package/dist/infrastructure/action/builtin-metadata.d.ts.map +1 -0
- package/dist/infrastructure/action/builtin-metadata.js +29 -0
- package/dist/infrastructure/action/builtin-metadata.js.map +1 -0
- package/dist/infrastructure/adapter-pool.d.ts +2 -1
- package/dist/infrastructure/adapter-pool.d.ts.map +1 -1
- package/dist/infrastructure/adapter-pool.js.map +1 -1
- package/dist/infrastructure/auth/address-validation.d.ts +38 -0
- package/dist/infrastructure/auth/address-validation.d.ts.map +1 -0
- package/dist/infrastructure/auth/address-validation.js +134 -0
- package/dist/infrastructure/auth/address-validation.js.map +1 -0
- package/dist/infrastructure/auth/siwe-verify.d.ts +34 -0
- package/dist/infrastructure/auth/siwe-verify.d.ts.map +1 -0
- package/dist/infrastructure/auth/siwe-verify.js +58 -0
- package/dist/infrastructure/auth/siwe-verify.js.map +1 -0
- package/dist/infrastructure/auth/types.d.ts +12 -0
- package/dist/infrastructure/auth/types.d.ts.map +1 -0
- package/dist/infrastructure/auth/types.js +8 -0
- package/dist/infrastructure/auth/types.js.map +1 -0
- package/dist/infrastructure/config/loader.d.ts +1 -10
- package/dist/infrastructure/config/loader.d.ts.map +1 -1
- package/dist/infrastructure/config/loader.js +0 -2
- package/dist/infrastructure/config/loader.js.map +1 -1
- package/dist/infrastructure/database/migrate.d.ts +6 -18
- package/dist/infrastructure/database/migrate.d.ts.map +1 -1
- package/dist/infrastructure/database/migrate.js +25 -2856
- package/dist/infrastructure/database/migrate.js.map +1 -1
- package/dist/infrastructure/database/migrations/v11-v20.d.ts +17 -0
- package/dist/infrastructure/database/migrations/v11-v20.d.ts.map +1 -0
- package/dist/infrastructure/database/migrations/v11-v20.js +295 -0
- package/dist/infrastructure/database/migrations/v11-v20.js.map +1 -0
- package/dist/infrastructure/database/migrations/v2-v10.d.ts +16 -0
- package/dist/infrastructure/database/migrations/v2-v10.d.ts.map +1 -0
- package/dist/infrastructure/database/migrations/v2-v10.js +539 -0
- package/dist/infrastructure/database/migrations/v2-v10.js.map +1 -0
- package/dist/infrastructure/database/migrations/v21-v30.d.ts +17 -0
- package/dist/infrastructure/database/migrations/v21-v30.d.ts.map +1 -0
- package/dist/infrastructure/database/migrations/v21-v30.js +507 -0
- package/dist/infrastructure/database/migrations/v21-v30.js.map +1 -0
- package/dist/infrastructure/database/migrations/v31-v40.d.ts +17 -0
- package/dist/infrastructure/database/migrations/v31-v40.d.ts.map +1 -0
- package/dist/infrastructure/database/migrations/v31-v40.js +203 -0
- package/dist/infrastructure/database/migrations/v31-v40.js.map +1 -0
- package/dist/infrastructure/database/migrations/v41-v50.d.ts +17 -0
- package/dist/infrastructure/database/migrations/v41-v50.d.ts.map +1 -0
- package/dist/infrastructure/database/migrations/v41-v50.js +188 -0
- package/dist/infrastructure/database/migrations/v41-v50.js.map +1 -0
- package/dist/infrastructure/database/migrations/v51-v59.d.ts +17 -0
- package/dist/infrastructure/database/migrations/v51-v59.d.ts.map +1 -0
- package/dist/infrastructure/database/migrations/v51-v59.js +420 -0
- package/dist/infrastructure/database/migrations/v51-v59.js.map +1 -0
- package/dist/infrastructure/database/schema-ddl.d.ts +24 -0
- package/dist/infrastructure/database/schema-ddl.d.ts.map +1 -0
- package/dist/infrastructure/database/schema-ddl.js +596 -0
- package/dist/infrastructure/database/schema-ddl.js.map +1 -0
- package/dist/infrastructure/database/schema.d.ts +38 -0
- package/dist/infrastructure/database/schema.d.ts.map +1 -1
- package/dist/infrastructure/database/schema.js +2 -0
- package/dist/infrastructure/database/schema.js.map +1 -1
- package/dist/infrastructure/jwt/jwt-secret-manager.d.ts.map +1 -1
- package/dist/infrastructure/jwt/jwt-secret-manager.js +16 -3
- package/dist/infrastructure/jwt/jwt-secret-manager.js.map +1 -1
- package/dist/infrastructure/nft/alchemy-nft-indexer.d.ts.map +1 -1
- package/dist/infrastructure/nft/alchemy-nft-indexer.js +0 -1
- package/dist/infrastructure/nft/alchemy-nft-indexer.js.map +1 -1
- package/dist/infrastructure/nft/helius-nft-indexer.d.ts.map +1 -1
- package/dist/infrastructure/nft/helius-nft-indexer.js +1 -2
- package/dist/infrastructure/nft/helius-nft-indexer.js.map +1 -1
- package/dist/infrastructure/nft/nft-indexer-client.d.ts.map +1 -1
- package/dist/infrastructure/nft/nft-indexer-client.js +0 -2
- package/dist/infrastructure/nft/nft-indexer-client.js.map +1 -1
- package/dist/infrastructure/security/ssrf-guard.d.ts +33 -0
- package/dist/infrastructure/security/ssrf-guard.d.ts.map +1 -0
- package/dist/infrastructure/security/ssrf-guard.js +244 -0
- package/dist/infrastructure/security/ssrf-guard.js.map +1 -0
- package/dist/infrastructure/settings/hot-reload.d.ts +1 -1
- package/dist/infrastructure/settings/hot-reload.d.ts.map +1 -1
- package/dist/infrastructure/settings/hot-reload.js +0 -2
- package/dist/infrastructure/settings/hot-reload.js.map +1 -1
- package/dist/infrastructure/settings/index.d.ts +2 -2
- package/dist/infrastructure/settings/index.d.ts.map +1 -1
- package/dist/infrastructure/settings/index.js +1 -1
- package/dist/infrastructure/settings/index.js.map +1 -1
- package/dist/infrastructure/settings/setting-keys.d.ts +14 -0
- package/dist/infrastructure/settings/setting-keys.d.ts.map +1 -1
- package/dist/infrastructure/settings/setting-keys.js +296 -214
- package/dist/infrastructure/settings/setting-keys.js.map +1 -1
- package/dist/infrastructure/settings/settings-service.d.ts +6 -1
- package/dist/infrastructure/settings/settings-service.d.ts.map +1 -1
- package/dist/infrastructure/settings/settings-service.js +15 -5
- package/dist/infrastructure/settings/settings-service.js.map +1 -1
- package/dist/infrastructure/telegram/telegram-bot-service.d.ts.map +1 -1
- package/dist/infrastructure/telegram/telegram-bot-service.js +3 -2
- package/dist/infrastructure/telegram/telegram-bot-service.js.map +1 -1
- package/dist/infrastructure/token-registry/builtin-tokens.d.ts.map +1 -1
- package/dist/infrastructure/token-registry/builtin-tokens.js +4 -7
- package/dist/infrastructure/token-registry/builtin-tokens.js.map +1 -1
- package/dist/lifecycle/daemon-pipeline.d.ts +49 -0
- package/dist/lifecycle/daemon-pipeline.d.ts.map +1 -0
- package/dist/lifecycle/daemon-pipeline.js +281 -0
- package/dist/lifecycle/daemon-pipeline.js.map +1 -0
- package/dist/lifecycle/daemon-shutdown.d.ts +14 -0
- package/dist/lifecycle/daemon-shutdown.d.ts.map +1 -0
- package/dist/lifecycle/daemon-shutdown.js +176 -0
- package/dist/lifecycle/daemon-shutdown.js.map +1 -0
- package/dist/lifecycle/daemon-startup.d.ts +15 -0
- package/dist/lifecycle/daemon-startup.d.ts.map +1 -0
- package/dist/lifecycle/daemon-startup.js +1527 -0
- package/dist/lifecycle/daemon-startup.js.map +1 -0
- package/dist/lifecycle/daemon.d.ts +171 -114
- package/dist/lifecycle/daemon.d.ts.map +1 -1
- package/dist/lifecycle/daemon.js +22 -1904
- package/dist/lifecycle/daemon.js.map +1 -1
- package/dist/notifications/channels/discord.d.ts.map +1 -1
- package/dist/notifications/channels/discord.js +1 -0
- package/dist/notifications/channels/discord.js.map +1 -1
- package/dist/notifications/channels/slack.d.ts.map +1 -1
- package/dist/notifications/channels/slack.js +1 -0
- package/dist/notifications/channels/slack.js.map +1 -1
- package/dist/notifications/index.d.ts +0 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +0 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/notification-service.d.ts.map +1 -1
- package/dist/notifications/notification-service.js +8 -6
- package/dist/notifications/notification-service.js.map +1 -1
- package/dist/pipeline/database-policy-engine.d.ts +18 -438
- package/dist/pipeline/database-policy-engine.d.ts.map +1 -1
- package/dist/pipeline/database-policy-engine.js +154 -1321
- package/dist/pipeline/database-policy-engine.js.map +1 -1
- package/dist/pipeline/dry-run.d.ts +5 -2
- package/dist/pipeline/dry-run.d.ts.map +1 -1
- package/dist/pipeline/dry-run.js +102 -8
- package/dist/pipeline/dry-run.js.map +1 -1
- package/dist/pipeline/evaluators/allowed-tokens.d.ts +28 -0
- package/dist/pipeline/evaluators/allowed-tokens.d.ts.map +1 -0
- package/dist/pipeline/evaluators/allowed-tokens.js +129 -0
- package/dist/pipeline/evaluators/allowed-tokens.js.map +1 -0
- package/dist/pipeline/evaluators/approved-spenders.d.ts +26 -0
- package/dist/pipeline/evaluators/approved-spenders.d.ts.map +1 -0
- package/dist/pipeline/evaluators/approved-spenders.js +115 -0
- package/dist/pipeline/evaluators/approved-spenders.js.map +1 -0
- package/dist/pipeline/evaluators/contract-whitelist.d.ts +28 -0
- package/dist/pipeline/evaluators/contract-whitelist.d.ts.map +1 -0
- package/dist/pipeline/evaluators/contract-whitelist.js +168 -0
- package/dist/pipeline/evaluators/contract-whitelist.js.map +1 -0
- package/dist/pipeline/evaluators/helpers.d.ts +9 -0
- package/dist/pipeline/evaluators/helpers.d.ts.map +1 -0
- package/dist/pipeline/evaluators/helpers.js +13 -0
- package/dist/pipeline/evaluators/helpers.js.map +1 -0
- package/dist/pipeline/evaluators/lending-asset-whitelist.d.ts +18 -0
- package/dist/pipeline/evaluators/lending-asset-whitelist.d.ts.map +1 -0
- package/dist/pipeline/evaluators/lending-asset-whitelist.js +44 -0
- package/dist/pipeline/evaluators/lending-asset-whitelist.js.map +1 -0
- package/dist/pipeline/evaluators/lending-ltv-limit.d.ts +24 -0
- package/dist/pipeline/evaluators/lending-ltv-limit.d.ts.map +1 -0
- package/dist/pipeline/evaluators/lending-ltv-limit.js +130 -0
- package/dist/pipeline/evaluators/lending-ltv-limit.js.map +1 -0
- package/dist/pipeline/evaluators/spending-limit.d.ts +46 -0
- package/dist/pipeline/evaluators/spending-limit.d.ts.map +1 -0
- package/dist/pipeline/evaluators/spending-limit.js +241 -0
- package/dist/pipeline/evaluators/spending-limit.js.map +1 -0
- package/dist/pipeline/evaluators/types.d.ts +71 -0
- package/dist/pipeline/evaluators/types.d.ts.map +1 -0
- package/dist/pipeline/evaluators/types.js +7 -0
- package/dist/pipeline/evaluators/types.js.map +1 -0
- package/dist/pipeline/external-action-pipeline.js.map +1 -1
- package/dist/pipeline/gas-condition-tracker.d.ts +1 -1
- package/dist/pipeline/gas-condition-tracker.js +1 -1
- package/dist/pipeline/pipeline-helpers.d.ts +146 -0
- package/dist/pipeline/pipeline-helpers.d.ts.map +1 -0
- package/dist/pipeline/pipeline-helpers.js +260 -0
- package/dist/pipeline/pipeline-helpers.js.map +1 -0
- package/dist/pipeline/pipeline.d.ts +1 -0
- package/dist/pipeline/pipeline.d.ts.map +1 -1
- package/dist/pipeline/pipeline.js +3 -2
- package/dist/pipeline/pipeline.js.map +1 -1
- package/dist/pipeline/resolve-effective-amount-usd.d.ts.map +1 -1
- package/dist/pipeline/resolve-effective-amount-usd.js +4 -10
- package/dist/pipeline/resolve-effective-amount-usd.js.map +1 -1
- package/dist/pipeline/sign-message.js +1 -1
- package/dist/pipeline/sign-message.js.map +1 -1
- package/dist/pipeline/sleep.d.ts +1 -5
- package/dist/pipeline/sleep.d.ts.map +1 -1
- package/dist/pipeline/sleep.js +2 -7
- package/dist/pipeline/sleep.js.map +1 -1
- package/dist/pipeline/stage1-validate.d.ts +8 -0
- package/dist/pipeline/stage1-validate.d.ts.map +1 -0
- package/dist/pipeline/stage1-validate.js +69 -0
- package/dist/pipeline/stage1-validate.js.map +1 -0
- package/dist/pipeline/stage2-auth.d.ts +12 -0
- package/dist/pipeline/stage2-auth.d.ts.map +1 -0
- package/dist/pipeline/stage2-auth.js +18 -0
- package/dist/pipeline/stage2-auth.js.map +1 -0
- package/dist/pipeline/stage3-policy.d.ts +26 -0
- package/dist/pipeline/stage3-policy.d.ts.map +1 -0
- package/dist/pipeline/stage3-policy.js +384 -0
- package/dist/pipeline/stage3-policy.js.map +1 -0
- package/dist/pipeline/stage4-wait.d.ts +8 -0
- package/dist/pipeline/stage4-wait.d.ts.map +1 -0
- package/dist/pipeline/stage4-wait.js +87 -0
- package/dist/pipeline/stage4-wait.js.map +1 -0
- package/dist/pipeline/stage5-execute.d.ts +120 -0
- package/dist/pipeline/stage5-execute.d.ts.map +1 -0
- package/dist/pipeline/stage5-execute.js +1070 -0
- package/dist/pipeline/stage5-execute.js.map +1 -0
- package/dist/pipeline/stage6-confirm.d.ts +11 -0
- package/dist/pipeline/stage6-confirm.d.ts.map +1 -0
- package/dist/pipeline/stage6-confirm.js +110 -0
- package/dist/pipeline/stage6-confirm.js.map +1 -0
- package/dist/pipeline/stages.d.ts +11 -245
- package/dist/pipeline/stages.d.ts.map +1 -1
- package/dist/pipeline/stages.js +11 -1896
- package/dist/pipeline/stages.js.map +1 -1
- package/dist/rpc-proxy/sync-pipeline.js +2 -2
- package/dist/rpc-proxy/sync-pipeline.js.map +1 -1
- package/dist/services/autostop/autostop-service.d.ts +4 -1
- package/dist/services/autostop/autostop-service.d.ts.map +1 -1
- package/dist/services/autostop/autostop-service.js +27 -7
- package/dist/services/autostop/autostop-service.js.map +1 -1
- package/dist/services/defi/position-tracker.d.ts +5 -0
- package/dist/services/defi/position-tracker.d.ts.map +1 -1
- package/dist/services/defi/position-tracker.js +41 -6
- package/dist/services/defi/position-tracker.js.map +1 -1
- package/dist/services/defi/position-write-queue.d.ts.map +1 -1
- package/dist/services/defi/position-write-queue.js +3 -2
- package/dist/services/defi/position-write-queue.js.map +1 -1
- package/dist/services/incoming/__tests__/integration-wiring.test.js +58 -0
- package/dist/services/incoming/__tests__/integration-wiring.test.js.map +1 -1
- package/dist/services/incoming/incoming-tx-monitor-service.d.ts.map +1 -1
- package/dist/services/incoming/incoming-tx-monitor-service.js +11 -14
- package/dist/services/incoming/incoming-tx-monitor-service.js.map +1 -1
- package/dist/services/incoming/incoming-tx-workers.d.ts +2 -2
- package/dist/services/incoming/incoming-tx-workers.d.ts.map +1 -1
- package/dist/services/incoming/incoming-tx-workers.js +1 -1
- package/dist/services/incoming/incoming-tx-workers.js.map +1 -1
- package/dist/services/incoming/safety-rules.d.ts.map +1 -1
- package/dist/services/incoming/safety-rules.js +3 -2
- package/dist/services/incoming/safety-rules.js.map +1 -1
- package/dist/services/incoming/subscription-multiplexer.d.ts +2 -6
- package/dist/services/incoming/subscription-multiplexer.d.ts.map +1 -1
- package/dist/services/incoming/subscription-multiplexer.js +1 -3
- package/dist/services/incoming/subscription-multiplexer.js.map +1 -1
- package/dist/services/monitoring/balance-monitor-service.d.ts.map +1 -1
- package/dist/services/monitoring/balance-monitor-service.js +2 -2
- package/dist/services/monitoring/balance-monitor-service.js.map +1 -1
- package/dist/services/signing-sdk/approval-channel-router.d.ts +7 -7
- package/dist/services/signing-sdk/approval-channel-router.d.ts.map +1 -1
- package/dist/services/signing-sdk/approval-channel-router.js +13 -13
- package/dist/services/signing-sdk/approval-channel-router.js.map +1 -1
- package/dist/services/signing-sdk/channels/index.d.ts +2 -2
- package/dist/services/signing-sdk/channels/index.d.ts.map +1 -1
- package/dist/services/signing-sdk/channels/index.js +1 -1
- package/dist/services/signing-sdk/channels/index.js.map +1 -1
- package/dist/services/signing-sdk/channels/push-relay-signing-channel.d.ts +59 -0
- package/dist/services/signing-sdk/channels/push-relay-signing-channel.d.ts.map +1 -0
- package/dist/services/signing-sdk/channels/push-relay-signing-channel.js +190 -0
- package/dist/services/signing-sdk/channels/push-relay-signing-channel.js.map +1 -0
- package/dist/services/signing-sdk/channels/telegram-signing-channel.d.ts +1 -1
- package/dist/services/signing-sdk/channels/telegram-signing-channel.js +1 -1
- package/dist/services/signing-sdk/channels/wallet-notification-channel.d.ts +6 -7
- package/dist/services/signing-sdk/channels/wallet-notification-channel.d.ts.map +1 -1
- package/dist/services/signing-sdk/channels/wallet-notification-channel.js +38 -55
- package/dist/services/signing-sdk/channels/wallet-notification-channel.js.map +1 -1
- package/dist/services/signing-sdk/index.d.ts +3 -3
- package/dist/services/signing-sdk/index.d.ts.map +1 -1
- package/dist/services/signing-sdk/index.js +2 -2
- package/dist/services/signing-sdk/index.js.map +1 -1
- package/dist/services/signing-sdk/preset-auto-setup.js +2 -2
- package/dist/services/signing-sdk/preset-auto-setup.js.map +1 -1
- package/dist/services/signing-sdk/sign-request-builder.d.ts +2 -2
- package/dist/services/signing-sdk/sign-request-builder.d.ts.map +1 -1
- package/dist/services/signing-sdk/sign-request-builder.js +17 -25
- package/dist/services/signing-sdk/sign-request-builder.js.map +1 -1
- package/dist/services/signing-sdk/wallet-app-service.d.ts +4 -0
- package/dist/services/signing-sdk/wallet-app-service.d.ts.map +1 -1
- package/dist/services/signing-sdk/wallet-app-service.js +12 -5
- package/dist/services/signing-sdk/wallet-app-service.js.map +1 -1
- package/dist/services/staking/aggregate-staking-balance.d.ts +24 -0
- package/dist/services/staking/aggregate-staking-balance.d.ts.map +1 -0
- package/dist/services/staking/aggregate-staking-balance.js +82 -0
- package/dist/services/staking/aggregate-staking-balance.js.map +1 -0
- package/dist/services/wc-session-service.d.ts.map +1 -1
- package/dist/services/wc-session-service.js +2 -1
- package/dist/services/wc-session-service.js.map +1 -1
- package/dist/services/wc-signing-bridge.js +2 -2
- package/dist/services/wc-signing-bridge.js.map +1 -1
- package/dist/services/x402/payment-signer.d.ts.map +1 -1
- package/dist/services/x402/payment-signer.js +2 -5
- package/dist/services/x402/payment-signer.js.map +1 -1
- package/dist/services/x402/ssrf-guard.d.ts +4 -23
- package/dist/services/x402/ssrf-guard.d.ts.map +1 -1
- package/dist/services/x402/ssrf-guard.js +3 -232
- package/dist/services/x402/ssrf-guard.js.map +1 -1
- package/dist/signing/capabilities/eip712-signer.d.ts.map +1 -1
- package/dist/signing/capabilities/eip712-signer.js +2 -0
- package/dist/signing/capabilities/eip712-signer.js.map +1 -1
- package/package.json +5 -5
- package/public/admin/assets/index-CpFF2lCo.js +3 -0
- package/public/admin/index.html +1 -1
- package/dist/notifications/channels/ntfy.d.ts +0 -13
- package/dist/notifications/channels/ntfy.d.ts.map +0 -1
- package/dist/notifications/channels/ntfy.js +0 -74
- package/dist/notifications/channels/ntfy.js.map +0 -1
- package/dist/services/signing-sdk/channels/ntfy-signing-channel.d.ts +0 -66
- package/dist/services/signing-sdk/channels/ntfy-signing-channel.d.ts.map +0 -1
- package/dist/services/signing-sdk/channels/ntfy-signing-channel.js +0 -270
- package/dist/services/signing-sdk/channels/ntfy-signing-channel.js.map +0 -1
- package/public/admin/assets/index-CQ3i4P2U.js +0 -3
|
@@ -1,2878 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Schema push + incremental migration runner for daemon SQLite database.
|
|
3
3
|
*
|
|
4
|
-
* Creates all
|
|
4
|
+
* Creates all tables with indexes, foreign keys, and CHECK constraints
|
|
5
5
|
* using CREATE TABLE IF NOT EXISTS statements. After initial schema creation,
|
|
6
6
|
* runs incremental migrations via runMigrations() for ALTER TABLE changes.
|
|
7
7
|
*
|
|
8
8
|
* v1.4+: DB schema changes MUST use ALTER TABLE incremental migrations (MIG-01~06).
|
|
9
9
|
* DB deletion and recreation is prohibited.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* migrations
|
|
14
|
-
*
|
|
15
|
-
* v1.4.6: Environment model migration:
|
|
16
|
-
* v6a (version 6): Add network column to transactions with backfill from wallets
|
|
17
|
-
* v6b (version 7): Replace wallets.network with environment + default_network (12-step)
|
|
18
|
-
* v8 (version 8): Add network column to policies (12-step)
|
|
11
|
+
* DDL statements and migration definitions are split into submodules:
|
|
12
|
+
* - schema-ddl.ts: getCreateTableStatements(), getCreateIndexStatements(), LATEST_SCHEMA_VERSION
|
|
13
|
+
* - migrations/v2-v10.ts through migrations/v51-v59.ts: individual migration definitions
|
|
19
14
|
*
|
|
20
15
|
* @see docs/25-sqlite-schema.md
|
|
21
16
|
* @see docs/65-migration-strategy.md
|
|
22
|
-
* @see docs/69-db-migration-v6-design.md
|
|
23
|
-
* @see docs/71-policy-engine-network-extension-design.md
|
|
24
|
-
*/
|
|
25
|
-
import { WALLET_STATUSES, CHAIN_TYPES, NETWORK_TYPES, ENVIRONMENT_TYPES, TRANSACTION_STATUSES, TRANSACTION_TYPES, POLICY_TYPES, POLICY_TIERS, NOTIFICATION_LOG_STATUSES, INCOMING_TX_STATUSES, POSITION_CATEGORIES, POSITION_STATUSES, NETWORK_TO_CAIP2, tokenAssetId, ACCOUNT_TYPES, } from '@waiaas/core';
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// Utility: build CHECK IN clause from SSoT arrays
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
const inList = (values) => values.map((v) => `'${v}'`).join(', ');
|
|
30
|
-
/**
|
|
31
|
-
* NETWORK_TYPES_WITH_LEGACY includes both old ('mainnet', 'devnet', 'testnet') and new
|
|
32
|
-
* ('solana-mainnet', 'solana-devnet', 'solana-testnet') Solana network names.
|
|
33
|
-
* Used in pre-v29 migrations so CHECK constraints accept data in either format
|
|
34
|
-
* during the migration chain. Migration v29 converts old -> new, and post-v29
|
|
35
|
-
* tables use NETWORK_TYPES (new names only).
|
|
36
|
-
*/
|
|
37
|
-
const LEGACY_SOLANA_NETWORKS = ['mainnet', 'devnet', 'testnet'];
|
|
38
|
-
const NETWORK_TYPES_WITH_LEGACY = [...new Set([...NETWORK_TYPES, ...LEGACY_SOLANA_NETWORKS])];
|
|
39
|
-
/**
|
|
40
|
-
* Map legacy bare Solana network names to their prefixed form.
|
|
41
|
-
* Used by pre-v29 migrations (e.g., v22 asset_id backfill) that need to look up
|
|
42
|
-
* NETWORK_TO_CAIP2 with potentially old-format data.
|
|
43
|
-
*/
|
|
44
|
-
const LEGACY_NETWORK_NORMALIZE = {
|
|
45
|
-
mainnet: 'solana-mainnet',
|
|
46
|
-
devnet: 'solana-devnet',
|
|
47
|
-
testnet: 'solana-testnet',
|
|
48
|
-
};
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// DDL statements for all 31 tables (latest schema: wallets + wallet_id + session_wallets + token_registry + settings + telegram_users + wc_sessions + wc_store + incoming_transactions + incoming_tx_cursors + defi_positions + wallet_apps + webhooks + webhook_logs + agent_identities + reputation_cache + nft_metadata_cache + userop_builds + hyperliquid_orders + hyperliquid_sub_accounts + polymarket_orders + polymarket_positions + polymarket_api_keys + wallet_credentials)
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
/**
|
|
53
|
-
* The latest schema version that getCreateTableStatements() represents.
|
|
54
|
-
* pushSchema() records this version for fresh databases so migrations are skipped.
|
|
55
|
-
* Increment this whenever DDL statements are updated to match a new migration.
|
|
56
17
|
*/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
owner_address TEXT,
|
|
69
|
-
owner_verified INTEGER NOT NULL DEFAULT 0 CHECK (owner_verified IN (0, 1)),
|
|
70
|
-
created_at INTEGER NOT NULL,
|
|
71
|
-
updated_at INTEGER NOT NULL,
|
|
72
|
-
suspended_at INTEGER,
|
|
73
|
-
suspension_reason TEXT,
|
|
74
|
-
monitor_incoming INTEGER NOT NULL DEFAULT 0,
|
|
75
|
-
owner_approval_method TEXT CHECK (owner_approval_method IS NULL OR owner_approval_method IN ('sdk_ntfy', 'sdk_telegram', 'walletconnect', 'telegram_bot', 'rest')),
|
|
76
|
-
wallet_type TEXT,
|
|
77
|
-
account_type TEXT NOT NULL DEFAULT 'eoa' CHECK (account_type IN (${inList(ACCOUNT_TYPES)})),
|
|
78
|
-
signer_key TEXT,
|
|
79
|
-
deployed INTEGER NOT NULL DEFAULT 1,
|
|
80
|
-
entry_point TEXT,
|
|
81
|
-
aa_provider TEXT CHECK (aa_provider IS NULL OR aa_provider IN ('pimlico', 'alchemy', 'custom')),
|
|
82
|
-
aa_provider_api_key_encrypted TEXT,
|
|
83
|
-
aa_bundler_url TEXT,
|
|
84
|
-
aa_paymaster_url TEXT,
|
|
85
|
-
aa_paymaster_policy_id TEXT,
|
|
86
|
-
factory_address TEXT
|
|
87
|
-
)`,
|
|
88
|
-
// Table 2: sessions (v26.4: wallet_id removed, v26.5: token_issued_count added)
|
|
89
|
-
`CREATE TABLE IF NOT EXISTS sessions (
|
|
90
|
-
id TEXT PRIMARY KEY,
|
|
91
|
-
token_hash TEXT NOT NULL,
|
|
92
|
-
expires_at INTEGER NOT NULL,
|
|
93
|
-
constraints TEXT,
|
|
94
|
-
usage_stats TEXT,
|
|
95
|
-
revoked_at INTEGER,
|
|
96
|
-
renewal_count INTEGER NOT NULL DEFAULT 0,
|
|
97
|
-
max_renewals INTEGER NOT NULL DEFAULT 0,
|
|
98
|
-
last_renewed_at INTEGER,
|
|
99
|
-
absolute_expires_at INTEGER NOT NULL,
|
|
100
|
-
created_at INTEGER NOT NULL,
|
|
101
|
-
source TEXT NOT NULL DEFAULT 'api',
|
|
102
|
-
token_issued_count INTEGER NOT NULL DEFAULT 1
|
|
103
|
-
)`,
|
|
104
|
-
// Table 2b: session_wallets (v26.4: session-wallet junction for 1:N model, v29.3: is_default removed)
|
|
105
|
-
`CREATE TABLE IF NOT EXISTS session_wallets (
|
|
106
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
107
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
108
|
-
created_at INTEGER NOT NULL,
|
|
109
|
-
PRIMARY KEY (session_id, wallet_id)
|
|
110
|
-
)`,
|
|
111
|
-
// Table 3: transactions (bridge_status + bridge_metadata added in v23)
|
|
112
|
-
`CREATE TABLE IF NOT EXISTS transactions (
|
|
113
|
-
id TEXT PRIMARY KEY,
|
|
114
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
115
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
116
|
-
chain TEXT NOT NULL,
|
|
117
|
-
tx_hash TEXT,
|
|
118
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
119
|
-
amount TEXT,
|
|
120
|
-
to_address TEXT,
|
|
121
|
-
token_mint TEXT,
|
|
122
|
-
contract_address TEXT,
|
|
123
|
-
method_signature TEXT,
|
|
124
|
-
spender_address TEXT,
|
|
125
|
-
approved_amount TEXT,
|
|
126
|
-
parent_id TEXT REFERENCES transactions(id) ON DELETE CASCADE,
|
|
127
|
-
batch_index INTEGER,
|
|
128
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
129
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
130
|
-
queued_at INTEGER,
|
|
131
|
-
executed_at INTEGER,
|
|
132
|
-
created_at INTEGER NOT NULL,
|
|
133
|
-
reserved_amount TEXT,
|
|
134
|
-
amount_usd REAL,
|
|
135
|
-
reserved_amount_usd REAL,
|
|
136
|
-
error TEXT,
|
|
137
|
-
metadata TEXT,
|
|
138
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
139
|
-
bridge_status TEXT CHECK (bridge_status IS NULL OR bridge_status IN ('PENDING', 'COMPLETED', 'FAILED', 'BRIDGE_MONITORING', 'TIMEOUT', 'REFUNDED', 'PARTIALLY_FILLED', 'FILLED', 'CANCELED', 'SETTLED', 'EXPIRED')),
|
|
140
|
-
bridge_metadata TEXT,
|
|
141
|
-
action_kind TEXT NOT NULL DEFAULT 'contractCall',
|
|
142
|
-
venue TEXT,
|
|
143
|
-
operation TEXT,
|
|
144
|
-
external_id TEXT
|
|
145
|
-
)`,
|
|
146
|
-
// Table 4: policies (network column added in v8)
|
|
147
|
-
`CREATE TABLE IF NOT EXISTS policies (
|
|
148
|
-
id TEXT PRIMARY KEY,
|
|
149
|
-
wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
|
|
150
|
-
type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
|
|
151
|
-
rules TEXT NOT NULL,
|
|
152
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
153
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
154
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
155
|
-
created_at INTEGER NOT NULL,
|
|
156
|
-
updated_at INTEGER NOT NULL
|
|
157
|
-
)`,
|
|
158
|
-
// Table 5: pending_approvals (approval_channel added in v16, approval_type added in v39, typed_data_json added in v40)
|
|
159
|
-
`CREATE TABLE IF NOT EXISTS pending_approvals (
|
|
160
|
-
id TEXT PRIMARY KEY,
|
|
161
|
-
tx_id TEXT NOT NULL REFERENCES transactions(id) ON DELETE CASCADE,
|
|
162
|
-
required_by INTEGER NOT NULL,
|
|
163
|
-
expires_at INTEGER NOT NULL,
|
|
164
|
-
approved_at INTEGER,
|
|
165
|
-
rejected_at INTEGER,
|
|
166
|
-
owner_signature TEXT,
|
|
167
|
-
approval_channel TEXT DEFAULT 'rest_api',
|
|
168
|
-
approval_type TEXT NOT NULL DEFAULT 'SIWE' CHECK (approval_type IN ('SIWE', 'EIP712')),
|
|
169
|
-
typed_data_json TEXT,
|
|
170
|
-
created_at INTEGER NOT NULL
|
|
171
|
-
)`,
|
|
172
|
-
// Table 6: audit_log
|
|
173
|
-
`CREATE TABLE IF NOT EXISTS audit_log (
|
|
174
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
175
|
-
timestamp INTEGER NOT NULL,
|
|
176
|
-
event_type TEXT NOT NULL,
|
|
177
|
-
actor TEXT NOT NULL,
|
|
178
|
-
wallet_id TEXT,
|
|
179
|
-
session_id TEXT,
|
|
180
|
-
tx_id TEXT,
|
|
181
|
-
details TEXT NOT NULL,
|
|
182
|
-
severity TEXT NOT NULL DEFAULT 'info' CHECK (severity IN ('info', 'warning', 'critical')),
|
|
183
|
-
ip_address TEXT
|
|
184
|
-
)`,
|
|
185
|
-
// Table 7: key_value_store
|
|
186
|
-
`CREATE TABLE IF NOT EXISTS key_value_store (
|
|
187
|
-
key TEXT PRIMARY KEY,
|
|
188
|
-
value TEXT NOT NULL,
|
|
189
|
-
updated_at INTEGER NOT NULL
|
|
190
|
-
)`,
|
|
191
|
-
// Table 8: notification_logs
|
|
192
|
-
`CREATE TABLE IF NOT EXISTS notification_logs (
|
|
193
|
-
id TEXT PRIMARY KEY,
|
|
194
|
-
event_type TEXT NOT NULL,
|
|
195
|
-
wallet_id TEXT,
|
|
196
|
-
channel TEXT NOT NULL,
|
|
197
|
-
status TEXT NOT NULL CHECK (status IN (${inList(NOTIFICATION_LOG_STATUSES)})),
|
|
198
|
-
error TEXT,
|
|
199
|
-
message TEXT,
|
|
200
|
-
created_at INTEGER NOT NULL
|
|
201
|
-
)`,
|
|
202
|
-
// Table 9: token_registry (asset_id added in v22)
|
|
203
|
-
`CREATE TABLE IF NOT EXISTS token_registry (
|
|
204
|
-
id TEXT PRIMARY KEY,
|
|
205
|
-
network TEXT NOT NULL,
|
|
206
|
-
address TEXT NOT NULL,
|
|
207
|
-
symbol TEXT NOT NULL,
|
|
208
|
-
name TEXT NOT NULL,
|
|
209
|
-
decimals INTEGER NOT NULL,
|
|
210
|
-
source TEXT NOT NULL DEFAULT 'custom' CHECK (source IN ('builtin', 'custom')),
|
|
211
|
-
asset_id TEXT,
|
|
212
|
-
created_at INTEGER NOT NULL
|
|
213
|
-
)`,
|
|
214
|
-
// Table 10: settings
|
|
215
|
-
`CREATE TABLE IF NOT EXISTS settings (
|
|
216
|
-
key TEXT PRIMARY KEY,
|
|
217
|
-
value TEXT NOT NULL,
|
|
218
|
-
encrypted INTEGER NOT NULL DEFAULT 0 CHECK (encrypted IN (0, 1)),
|
|
219
|
-
category TEXT NOT NULL,
|
|
220
|
-
updated_at INTEGER NOT NULL
|
|
221
|
-
)`,
|
|
222
|
-
// Table 11: schema_version
|
|
223
|
-
`CREATE TABLE IF NOT EXISTS schema_version (
|
|
224
|
-
version INTEGER PRIMARY KEY,
|
|
225
|
-
applied_at INTEGER NOT NULL,
|
|
226
|
-
description TEXT NOT NULL
|
|
227
|
-
)`,
|
|
228
|
-
// Table 13: telegram_users (Telegram Bot user management, v1.6)
|
|
229
|
-
`CREATE TABLE IF NOT EXISTS telegram_users (
|
|
230
|
-
chat_id INTEGER PRIMARY KEY,
|
|
231
|
-
username TEXT,
|
|
232
|
-
role TEXT NOT NULL DEFAULT 'PENDING' CHECK (role IN ('PENDING', 'ADMIN', 'READONLY')),
|
|
233
|
-
registered_at INTEGER NOT NULL,
|
|
234
|
-
approved_at INTEGER
|
|
235
|
-
)`,
|
|
236
|
-
// Table 14: wc_sessions (WalletConnect session metadata, v1.6.1)
|
|
237
|
-
`CREATE TABLE IF NOT EXISTS wc_sessions (
|
|
238
|
-
wallet_id TEXT PRIMARY KEY REFERENCES wallets(id) ON DELETE CASCADE,
|
|
239
|
-
topic TEXT NOT NULL UNIQUE,
|
|
240
|
-
peer_meta TEXT,
|
|
241
|
-
chain_id TEXT NOT NULL,
|
|
242
|
-
owner_address TEXT NOT NULL,
|
|
243
|
-
namespaces TEXT,
|
|
244
|
-
expiry INTEGER NOT NULL,
|
|
245
|
-
created_at INTEGER NOT NULL
|
|
246
|
-
)`,
|
|
247
|
-
// Table 15: wc_store (WalletConnect IKeyValueStorage, v1.6.1)
|
|
248
|
-
`CREATE TABLE IF NOT EXISTS wc_store (
|
|
249
|
-
key TEXT PRIMARY KEY,
|
|
250
|
-
value TEXT NOT NULL
|
|
251
|
-
)`,
|
|
252
|
-
// Table 16: incoming_transactions (detected incoming transfers to monitored wallets, v27.1)
|
|
253
|
-
`CREATE TABLE IF NOT EXISTS incoming_transactions (
|
|
254
|
-
id TEXT PRIMARY KEY,
|
|
255
|
-
tx_hash TEXT NOT NULL,
|
|
256
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
257
|
-
from_address TEXT NOT NULL,
|
|
258
|
-
amount TEXT NOT NULL,
|
|
259
|
-
token_address TEXT,
|
|
260
|
-
chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
|
|
261
|
-
network TEXT NOT NULL,
|
|
262
|
-
status TEXT NOT NULL DEFAULT 'DETECTED' CHECK (status IN (${inList(INCOMING_TX_STATUSES)})),
|
|
263
|
-
block_number INTEGER,
|
|
264
|
-
detected_at INTEGER NOT NULL,
|
|
265
|
-
confirmed_at INTEGER,
|
|
266
|
-
is_suspicious INTEGER NOT NULL DEFAULT 0,
|
|
267
|
-
UNIQUE(tx_hash, wallet_id)
|
|
268
|
-
)`,
|
|
269
|
-
// Table 17: incoming_tx_cursors (per-wallet cursor for gap recovery, v27.1)
|
|
270
|
-
`CREATE TABLE IF NOT EXISTS incoming_tx_cursors (
|
|
271
|
-
wallet_id TEXT PRIMARY KEY REFERENCES wallets(id) ON DELETE CASCADE,
|
|
272
|
-
chain TEXT NOT NULL,
|
|
273
|
-
network TEXT NOT NULL,
|
|
274
|
-
last_signature TEXT,
|
|
275
|
-
last_block_number INTEGER,
|
|
276
|
-
updated_at INTEGER NOT NULL
|
|
277
|
-
)`,
|
|
278
|
-
// Table 18: defi_positions (DeFi position tracking, v29.2)
|
|
279
|
-
`CREATE TABLE IF NOT EXISTS defi_positions (
|
|
280
|
-
id TEXT PRIMARY KEY,
|
|
281
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
282
|
-
category TEXT NOT NULL CHECK(category IN (${inList(POSITION_CATEGORIES)})),
|
|
283
|
-
provider TEXT NOT NULL,
|
|
284
|
-
chain TEXT NOT NULL CHECK(chain IN (${inList(CHAIN_TYPES)})),
|
|
285
|
-
network TEXT CHECK(network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
286
|
-
asset_id TEXT,
|
|
287
|
-
amount TEXT NOT NULL,
|
|
288
|
-
amount_usd REAL,
|
|
289
|
-
metadata TEXT,
|
|
290
|
-
status TEXT NOT NULL DEFAULT 'ACTIVE' CHECK(status IN (${inList(POSITION_STATUSES)})),
|
|
291
|
-
opened_at INTEGER NOT NULL,
|
|
292
|
-
closed_at INTEGER,
|
|
293
|
-
last_synced_at INTEGER NOT NULL,
|
|
294
|
-
created_at INTEGER NOT NULL,
|
|
295
|
-
updated_at INTEGER NOT NULL
|
|
296
|
-
)`,
|
|
297
|
-
// Table 19: wallet_apps (Human Wallet Apps registry, v29.7, v29.10: sign_topic/notify_topic, v34: wallet_type, v35: subscription_token)
|
|
298
|
-
`CREATE TABLE IF NOT EXISTS wallet_apps (
|
|
299
|
-
id TEXT PRIMARY KEY,
|
|
300
|
-
name TEXT NOT NULL UNIQUE,
|
|
301
|
-
display_name TEXT NOT NULL,
|
|
302
|
-
wallet_type TEXT NOT NULL DEFAULT '',
|
|
303
|
-
signing_enabled INTEGER NOT NULL DEFAULT 1,
|
|
304
|
-
alerts_enabled INTEGER NOT NULL DEFAULT 1,
|
|
305
|
-
sign_topic TEXT,
|
|
306
|
-
notify_topic TEXT,
|
|
307
|
-
subscription_token TEXT,
|
|
308
|
-
created_at INTEGER NOT NULL,
|
|
309
|
-
updated_at INTEGER NOT NULL
|
|
310
|
-
)`,
|
|
311
|
-
// Table 20: webhooks (webhook outbound subscriptions, v37 OPS-04)
|
|
312
|
-
`CREATE TABLE IF NOT EXISTS webhooks (
|
|
313
|
-
id TEXT PRIMARY KEY,
|
|
314
|
-
url TEXT NOT NULL,
|
|
315
|
-
secret_hash TEXT NOT NULL,
|
|
316
|
-
secret_encrypted TEXT NOT NULL,
|
|
317
|
-
events TEXT NOT NULL DEFAULT '[]',
|
|
318
|
-
description TEXT,
|
|
319
|
-
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
|
|
320
|
-
created_at INTEGER NOT NULL,
|
|
321
|
-
updated_at INTEGER NOT NULL
|
|
322
|
-
)`,
|
|
323
|
-
// Table 21: webhook_logs (webhook delivery attempt history, v37 OPS-04)
|
|
324
|
-
`CREATE TABLE IF NOT EXISTS webhook_logs (
|
|
325
|
-
id TEXT PRIMARY KEY,
|
|
326
|
-
webhook_id TEXT NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
|
|
327
|
-
event_type TEXT NOT NULL,
|
|
328
|
-
status TEXT NOT NULL CHECK (status IN ('success', 'failed')),
|
|
329
|
-
http_status INTEGER,
|
|
330
|
-
attempt INTEGER NOT NULL DEFAULT 1,
|
|
331
|
-
error TEXT,
|
|
332
|
-
request_duration INTEGER,
|
|
333
|
-
created_at INTEGER NOT NULL
|
|
334
|
-
)`,
|
|
335
|
-
// Table 22: agent_identities (ERC-8004 agent identity tracking, v39)
|
|
336
|
-
`CREATE TABLE IF NOT EXISTS agent_identities (
|
|
337
|
-
id TEXT PRIMARY KEY,
|
|
338
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
339
|
-
chain_agent_id TEXT NOT NULL,
|
|
340
|
-
registry_address TEXT NOT NULL,
|
|
341
|
-
chain_id INTEGER NOT NULL,
|
|
342
|
-
agent_uri TEXT,
|
|
343
|
-
registration_file_url TEXT,
|
|
344
|
-
status TEXT NOT NULL DEFAULT 'PENDING'
|
|
345
|
-
CHECK (status IN ('PENDING', 'REGISTERED', 'WALLET_LINKED', 'DEREGISTERED')),
|
|
346
|
-
created_at INTEGER NOT NULL,
|
|
347
|
-
updated_at INTEGER NOT NULL
|
|
348
|
-
)`,
|
|
349
|
-
// Table 23: reputation_cache (ERC-8004 reputation score cache, v39)
|
|
350
|
-
`CREATE TABLE IF NOT EXISTS reputation_cache (
|
|
351
|
-
agent_id TEXT NOT NULL,
|
|
352
|
-
registry_address TEXT NOT NULL,
|
|
353
|
-
tag1 TEXT NOT NULL DEFAULT '',
|
|
354
|
-
tag2 TEXT NOT NULL DEFAULT '',
|
|
355
|
-
score INTEGER NOT NULL,
|
|
356
|
-
score_decimals INTEGER NOT NULL DEFAULT 0,
|
|
357
|
-
feedback_count INTEGER NOT NULL DEFAULT 0,
|
|
358
|
-
cached_at INTEGER NOT NULL,
|
|
359
|
-
PRIMARY KEY (agent_id, registry_address, tag1, tag2)
|
|
360
|
-
)`,
|
|
361
|
-
// Table 24: nft_metadata_cache (NFT metadata caching with TTL, v44)
|
|
362
|
-
`CREATE TABLE IF NOT EXISTS nft_metadata_cache (
|
|
363
|
-
id TEXT PRIMARY KEY,
|
|
364
|
-
contract_address TEXT NOT NULL,
|
|
365
|
-
token_id TEXT NOT NULL,
|
|
366
|
-
chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
|
|
367
|
-
network TEXT NOT NULL CHECK (network IN (${inList(NETWORK_TYPES)})),
|
|
368
|
-
metadata_json TEXT NOT NULL,
|
|
369
|
-
cached_at INTEGER NOT NULL,
|
|
370
|
-
expires_at INTEGER NOT NULL
|
|
371
|
-
)`,
|
|
372
|
-
// Table 25: userop_builds (UserOp Build/Sign API data, v45, network added v50)
|
|
373
|
-
`CREATE TABLE IF NOT EXISTS userop_builds (
|
|
374
|
-
id TEXT PRIMARY KEY,
|
|
375
|
-
wallet_id TEXT NOT NULL,
|
|
376
|
-
call_data TEXT NOT NULL,
|
|
377
|
-
sender TEXT NOT NULL,
|
|
378
|
-
nonce TEXT NOT NULL,
|
|
379
|
-
entry_point TEXT NOT NULL,
|
|
380
|
-
network TEXT,
|
|
381
|
-
created_at INTEGER NOT NULL,
|
|
382
|
-
expires_at INTEGER NOT NULL,
|
|
383
|
-
used INTEGER NOT NULL DEFAULT 0 CHECK (used IN (0, 1))
|
|
384
|
-
)`,
|
|
385
|
-
// Table 26: hyperliquid_orders (Hyperliquid DEX order history, v51)
|
|
386
|
-
`CREATE TABLE IF NOT EXISTS hyperliquid_orders (
|
|
387
|
-
id TEXT PRIMARY KEY,
|
|
388
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
389
|
-
sub_account_address TEXT,
|
|
390
|
-
oid INTEGER,
|
|
391
|
-
cloid TEXT,
|
|
392
|
-
transaction_id TEXT REFERENCES transactions(id),
|
|
393
|
-
market TEXT NOT NULL,
|
|
394
|
-
asset_index INTEGER NOT NULL,
|
|
395
|
-
side TEXT NOT NULL CHECK(side IN ('BUY', 'SELL')),
|
|
396
|
-
order_type TEXT NOT NULL CHECK(order_type IN ('MARKET', 'LIMIT', 'STOP_MARKET', 'STOP_LIMIT', 'TAKE_PROFIT')),
|
|
397
|
-
size TEXT NOT NULL,
|
|
398
|
-
price TEXT,
|
|
399
|
-
trigger_price TEXT,
|
|
400
|
-
tif TEXT CHECK(tif IN ('GTC', 'IOC', 'ALO')),
|
|
401
|
-
reduce_only INTEGER NOT NULL DEFAULT 0,
|
|
402
|
-
status TEXT NOT NULL CHECK(status IN ('PENDING', 'RESTING', 'FILLED', 'PARTIALLY_FILLED', 'CANCELLED', 'REJECTED', 'TRIGGERED')),
|
|
403
|
-
filled_size TEXT,
|
|
404
|
-
avg_fill_price TEXT,
|
|
405
|
-
is_spot INTEGER NOT NULL DEFAULT 0,
|
|
406
|
-
leverage INTEGER,
|
|
407
|
-
margin_mode TEXT CHECK(margin_mode IN ('CROSS', 'ISOLATED')),
|
|
408
|
-
response_data TEXT,
|
|
409
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
410
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
411
|
-
)`,
|
|
412
|
-
// Table 27: hyperliquid_sub_accounts (Hyperliquid Sub-account mapping, v52)
|
|
413
|
-
`CREATE TABLE IF NOT EXISTS hyperliquid_sub_accounts (
|
|
414
|
-
id TEXT PRIMARY KEY,
|
|
415
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
416
|
-
sub_account_address TEXT NOT NULL,
|
|
417
|
-
name TEXT,
|
|
418
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
419
|
-
UNIQUE(wallet_id, sub_account_address)
|
|
420
|
-
)`,
|
|
421
|
-
// Table 28: polymarket_orders (Polymarket CLOB order history, v53)
|
|
422
|
-
`CREATE TABLE IF NOT EXISTS polymarket_orders (
|
|
423
|
-
id TEXT PRIMARY KEY,
|
|
424
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
425
|
-
transaction_id TEXT REFERENCES transactions(id),
|
|
426
|
-
condition_id TEXT NOT NULL,
|
|
427
|
-
token_id TEXT NOT NULL,
|
|
428
|
-
market_slug TEXT,
|
|
429
|
-
outcome TEXT NOT NULL,
|
|
430
|
-
order_id TEXT,
|
|
431
|
-
side TEXT NOT NULL CHECK (side IN ('BUY', 'SELL')),
|
|
432
|
-
order_type TEXT NOT NULL CHECK (order_type IN ('GTC', 'GTD', 'FOK', 'IOC')),
|
|
433
|
-
price TEXT NOT NULL,
|
|
434
|
-
size TEXT NOT NULL,
|
|
435
|
-
status TEXT NOT NULL CHECK (status IN ('PENDING', 'LIVE', 'MATCHED', 'PARTIALLY_FILLED', 'CANCELLED', 'EXPIRED')),
|
|
436
|
-
filled_size TEXT,
|
|
437
|
-
avg_fill_price TEXT,
|
|
438
|
-
salt TEXT,
|
|
439
|
-
maker_amount TEXT,
|
|
440
|
-
taker_amount TEXT,
|
|
441
|
-
signature_type INTEGER NOT NULL DEFAULT 0,
|
|
442
|
-
fee_rate_bps INTEGER,
|
|
443
|
-
expiration INTEGER,
|
|
444
|
-
nonce TEXT,
|
|
445
|
-
is_neg_risk INTEGER NOT NULL DEFAULT 0,
|
|
446
|
-
response_data TEXT,
|
|
447
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
448
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
449
|
-
)`,
|
|
450
|
-
// Table 29: polymarket_positions (Polymarket position tracking, v54)
|
|
451
|
-
`CREATE TABLE IF NOT EXISTS polymarket_positions (
|
|
452
|
-
id TEXT PRIMARY KEY,
|
|
453
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
454
|
-
condition_id TEXT NOT NULL,
|
|
455
|
-
token_id TEXT NOT NULL,
|
|
456
|
-
market_slug TEXT,
|
|
457
|
-
outcome TEXT NOT NULL CHECK (outcome IN ('YES', 'NO')),
|
|
458
|
-
size TEXT NOT NULL DEFAULT '0',
|
|
459
|
-
avg_price TEXT,
|
|
460
|
-
realized_pnl TEXT DEFAULT '0',
|
|
461
|
-
market_resolved INTEGER NOT NULL DEFAULT 0,
|
|
462
|
-
winning_outcome TEXT,
|
|
463
|
-
is_neg_risk INTEGER NOT NULL DEFAULT 0,
|
|
464
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
465
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
466
|
-
UNIQUE(wallet_id, token_id)
|
|
467
|
-
)`,
|
|
468
|
-
// Table 30: polymarket_api_keys (Polymarket CLOB API credentials, v54)
|
|
469
|
-
`CREATE TABLE IF NOT EXISTS polymarket_api_keys (
|
|
470
|
-
id TEXT PRIMARY KEY,
|
|
471
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
472
|
-
api_key TEXT NOT NULL,
|
|
473
|
-
api_secret_encrypted TEXT NOT NULL,
|
|
474
|
-
api_passphrase_encrypted TEXT NOT NULL,
|
|
475
|
-
signature_type INTEGER NOT NULL DEFAULT 0,
|
|
476
|
-
proxy_address TEXT,
|
|
477
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
478
|
-
UNIQUE(wallet_id)
|
|
479
|
-
)`,
|
|
480
|
-
// Table 31: wallet_credentials (External Action credential vault, v55)
|
|
481
|
-
`CREATE TABLE IF NOT EXISTS wallet_credentials (
|
|
482
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
483
|
-
wallet_id TEXT,
|
|
484
|
-
type TEXT NOT NULL CHECK (type IN ('api-key','hmac-secret','rsa-private-key','session-token','custom')),
|
|
485
|
-
name TEXT NOT NULL,
|
|
486
|
-
encrypted_value BLOB NOT NULL,
|
|
487
|
-
iv BLOB NOT NULL,
|
|
488
|
-
auth_tag BLOB NOT NULL,
|
|
489
|
-
metadata TEXT NOT NULL DEFAULT '{}',
|
|
490
|
-
expires_at INTEGER,
|
|
491
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
492
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
493
|
-
FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE
|
|
494
|
-
)`,
|
|
495
|
-
];
|
|
496
|
-
}
|
|
497
|
-
// ---------------------------------------------------------------------------
|
|
498
|
-
// Index creation statements
|
|
18
|
+
import { getCreateTableStatements, getCreateIndexStatements, } from './schema-ddl.js';
|
|
19
|
+
import { migrations as v2to10 } from './migrations/v2-v10.js';
|
|
20
|
+
import { migrations as v11to20 } from './migrations/v11-v20.js';
|
|
21
|
+
import { migrations as v21to30 } from './migrations/v21-v30.js';
|
|
22
|
+
import { migrations as v31to40 } from './migrations/v31-v40.js';
|
|
23
|
+
import { migrations as v41to50 } from './migrations/v41-v50.js';
|
|
24
|
+
import { migrations as v51to59 } from './migrations/v51-v59.js';
|
|
25
|
+
// Re-export LATEST_SCHEMA_VERSION from schema-ddl
|
|
26
|
+
export { LATEST_SCHEMA_VERSION } from './schema-ddl.js';
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Global migration registry (assembled from submodules)
|
|
499
29
|
// ---------------------------------------------------------------------------
|
|
500
|
-
function getCreateIndexStatements() {
|
|
501
|
-
return [
|
|
502
|
-
// wallets indexes (renamed from agents in v3)
|
|
503
|
-
'CREATE UNIQUE INDEX IF NOT EXISTS idx_wallets_public_key ON wallets(public_key)',
|
|
504
|
-
'CREATE INDEX IF NOT EXISTS idx_wallets_status ON wallets(status)',
|
|
505
|
-
'CREATE INDEX IF NOT EXISTS idx_wallets_chain_environment ON wallets(chain, environment)',
|
|
506
|
-
'CREATE INDEX IF NOT EXISTS idx_wallets_owner_address ON wallets(owner_address)',
|
|
507
|
-
// sessions indexes (v26.4: idx_sessions_wallet_id removed, wallet_id moved to session_wallets)
|
|
508
|
-
'CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at)',
|
|
509
|
-
'CREATE INDEX IF NOT EXISTS idx_sessions_token_hash ON sessions(token_hash)',
|
|
510
|
-
// session_wallets indexes (v26.4)
|
|
511
|
-
'CREATE INDEX IF NOT EXISTS idx_session_wallets_session ON session_wallets(session_id)',
|
|
512
|
-
'CREATE INDEX IF NOT EXISTS idx_session_wallets_wallet ON session_wallets(wallet_id)',
|
|
513
|
-
// transactions indexes
|
|
514
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_wallet_status ON transactions(wallet_id, status)',
|
|
515
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_session_id ON transactions(session_id)',
|
|
516
|
-
'CREATE UNIQUE INDEX IF NOT EXISTS idx_transactions_tx_hash ON transactions(tx_hash)',
|
|
517
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_queued_at ON transactions(queued_at)',
|
|
518
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_created_at ON transactions(created_at)',
|
|
519
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_type ON transactions(type)',
|
|
520
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_contract_address ON transactions(contract_address)',
|
|
521
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_parent_id ON transactions(parent_id)',
|
|
522
|
-
// policies indexes
|
|
523
|
-
'CREATE INDEX IF NOT EXISTS idx_policies_wallet_enabled ON policies(wallet_id, enabled)',
|
|
524
|
-
'CREATE INDEX IF NOT EXISTS idx_policies_type ON policies(type)',
|
|
525
|
-
'CREATE INDEX IF NOT EXISTS idx_policies_network ON policies(network)',
|
|
526
|
-
// pending_approvals indexes
|
|
527
|
-
'CREATE INDEX IF NOT EXISTS idx_pending_approvals_tx_id ON pending_approvals(tx_id)',
|
|
528
|
-
'CREATE INDEX IF NOT EXISTS idx_pending_approvals_expires_at ON pending_approvals(expires_at)',
|
|
529
|
-
// audit_log indexes
|
|
530
|
-
'CREATE INDEX IF NOT EXISTS idx_audit_log_timestamp ON audit_log(timestamp)',
|
|
531
|
-
'CREATE INDEX IF NOT EXISTS idx_audit_log_event_type ON audit_log(event_type)',
|
|
532
|
-
'CREATE INDEX IF NOT EXISTS idx_audit_log_wallet_id ON audit_log(wallet_id)',
|
|
533
|
-
'CREATE INDEX IF NOT EXISTS idx_audit_log_severity ON audit_log(severity)',
|
|
534
|
-
'CREATE INDEX IF NOT EXISTS idx_audit_log_wallet_timestamp ON audit_log(wallet_id, timestamp)',
|
|
535
|
-
'CREATE INDEX IF NOT EXISTS idx_audit_log_tx_id ON audit_log(tx_id)',
|
|
536
|
-
// notification_logs indexes
|
|
537
|
-
'CREATE INDEX IF NOT EXISTS idx_notification_logs_event_type ON notification_logs(event_type)',
|
|
538
|
-
'CREATE INDEX IF NOT EXISTS idx_notification_logs_wallet_id ON notification_logs(wallet_id)',
|
|
539
|
-
'CREATE INDEX IF NOT EXISTS idx_notification_logs_status ON notification_logs(status)',
|
|
540
|
-
'CREATE INDEX IF NOT EXISTS idx_notification_logs_created_at ON notification_logs(created_at)',
|
|
541
|
-
// token_registry indexes
|
|
542
|
-
'CREATE UNIQUE INDEX IF NOT EXISTS idx_token_registry_network_address ON token_registry(network, address)',
|
|
543
|
-
'CREATE INDEX IF NOT EXISTS idx_token_registry_network ON token_registry(network)',
|
|
544
|
-
// settings indexes
|
|
545
|
-
'CREATE INDEX IF NOT EXISTS idx_settings_category ON settings(category)',
|
|
546
|
-
// telegram_users indexes
|
|
547
|
-
'CREATE INDEX IF NOT EXISTS idx_telegram_users_role ON telegram_users(role)',
|
|
548
|
-
// wc_sessions indexes
|
|
549
|
-
'CREATE INDEX IF NOT EXISTS idx_wc_sessions_topic ON wc_sessions(topic)',
|
|
550
|
-
// incoming_transactions indexes
|
|
551
|
-
'CREATE INDEX IF NOT EXISTS idx_incoming_tx_wallet_detected ON incoming_transactions(wallet_id, detected_at DESC)',
|
|
552
|
-
'CREATE INDEX IF NOT EXISTS idx_incoming_tx_detected_at ON incoming_transactions(detected_at)',
|
|
553
|
-
'CREATE INDEX IF NOT EXISTS idx_incoming_tx_chain_network ON incoming_transactions(chain, network)',
|
|
554
|
-
"CREATE INDEX IF NOT EXISTS idx_incoming_tx_status ON incoming_transactions(status) WHERE status = 'DETECTED'",
|
|
555
|
-
// v28.3: DeFi async tracking partial indexes on transactions
|
|
556
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_bridge_status ON transactions(bridge_status) WHERE bridge_status IS NOT NULL',
|
|
557
|
-
"CREATE INDEX IF NOT EXISTS idx_transactions_gas_waiting ON transactions(status) WHERE status = 'GAS_WAITING'",
|
|
558
|
-
// v29.2: defi_positions indexes
|
|
559
|
-
'CREATE INDEX IF NOT EXISTS idx_defi_positions_wallet_category ON defi_positions(wallet_id, category)',
|
|
560
|
-
'CREATE INDEX IF NOT EXISTS idx_defi_positions_wallet_provider ON defi_positions(wallet_id, provider)',
|
|
561
|
-
'CREATE INDEX IF NOT EXISTS idx_defi_positions_status ON defi_positions(status)',
|
|
562
|
-
'CREATE UNIQUE INDEX IF NOT EXISTS idx_defi_positions_unique ON defi_positions(wallet_id, provider, asset_id, category)',
|
|
563
|
-
// v34: wallet_apps.wallet_type index
|
|
564
|
-
'CREATE INDEX IF NOT EXISTS idx_wallet_apps_wallet_type ON wallet_apps(wallet_type)',
|
|
565
|
-
// v37: webhooks + webhook_logs indexes (OPS-04)
|
|
566
|
-
'CREATE INDEX IF NOT EXISTS idx_webhooks_enabled ON webhooks(enabled)',
|
|
567
|
-
'CREATE INDEX IF NOT EXISTS idx_webhook_logs_webhook_id ON webhook_logs(webhook_id)',
|
|
568
|
-
'CREATE INDEX IF NOT EXISTS idx_webhook_logs_event_type ON webhook_logs(event_type)',
|
|
569
|
-
'CREATE INDEX IF NOT EXISTS idx_webhook_logs_status ON webhook_logs(status)',
|
|
570
|
-
'CREATE INDEX IF NOT EXISTS idx_webhook_logs_created_at ON webhook_logs(created_at)',
|
|
571
|
-
// v39: agent_identities indexes (ERC-8004)
|
|
572
|
-
'CREATE INDEX IF NOT EXISTS idx_agent_identities_wallet ON agent_identities(wallet_id)',
|
|
573
|
-
'CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_identities_chain ON agent_identities(registry_address, chain_agent_id)',
|
|
574
|
-
// v44: nft_metadata_cache indexes
|
|
575
|
-
'CREATE UNIQUE INDEX IF NOT EXISTS idx_nft_cache_unique ON nft_metadata_cache(contract_address, token_id, chain, network)',
|
|
576
|
-
'CREATE INDEX IF NOT EXISTS idx_nft_cache_expires ON nft_metadata_cache(expires_at)',
|
|
577
|
-
// v45: userop_builds indexes
|
|
578
|
-
'CREATE INDEX IF NOT EXISTS idx_userop_builds_wallet_id ON userop_builds(wallet_id)',
|
|
579
|
-
'CREATE INDEX IF NOT EXISTS idx_userop_builds_expires ON userop_builds(expires_at)',
|
|
580
|
-
// v51: hyperliquid_orders indexes
|
|
581
|
-
'CREATE INDEX IF NOT EXISTS idx_hl_orders_wallet ON hyperliquid_orders(wallet_id)',
|
|
582
|
-
'CREATE INDEX IF NOT EXISTS idx_hl_orders_oid ON hyperliquid_orders(oid)',
|
|
583
|
-
'CREATE INDEX IF NOT EXISTS idx_hl_orders_market ON hyperliquid_orders(market)',
|
|
584
|
-
'CREATE INDEX IF NOT EXISTS idx_hl_orders_status ON hyperliquid_orders(status)',
|
|
585
|
-
'CREATE INDEX IF NOT EXISTS idx_hl_orders_created ON hyperliquid_orders(created_at)',
|
|
586
|
-
// v52: hyperliquid_sub_accounts index
|
|
587
|
-
'CREATE INDEX IF NOT EXISTS idx_hl_sub_wallet ON hyperliquid_sub_accounts(wallet_id)',
|
|
588
|
-
// v53: polymarket_orders indexes
|
|
589
|
-
'CREATE INDEX IF NOT EXISTS idx_pm_orders_wallet ON polymarket_orders(wallet_id)',
|
|
590
|
-
'CREATE INDEX IF NOT EXISTS idx_pm_orders_order_id ON polymarket_orders(order_id)',
|
|
591
|
-
'CREATE INDEX IF NOT EXISTS idx_pm_orders_condition ON polymarket_orders(condition_id)',
|
|
592
|
-
'CREATE INDEX IF NOT EXISTS idx_pm_orders_status ON polymarket_orders(status)',
|
|
593
|
-
'CREATE INDEX IF NOT EXISTS idx_pm_orders_created ON polymarket_orders(created_at)',
|
|
594
|
-
// v54: polymarket_positions indexes
|
|
595
|
-
'CREATE INDEX IF NOT EXISTS idx_pm_positions_wallet ON polymarket_positions(wallet_id)',
|
|
596
|
-
'CREATE INDEX IF NOT EXISTS idx_pm_positions_condition ON polymarket_positions(condition_id)',
|
|
597
|
-
'CREATE INDEX IF NOT EXISTS idx_pm_positions_resolved ON polymarket_positions(market_resolved)',
|
|
598
|
-
// v54: polymarket_api_keys indexes (UNIQUE on wallet_id is inline)
|
|
599
|
-
// v55: wallet_credentials indexes
|
|
600
|
-
'CREATE UNIQUE INDEX IF NOT EXISTS idx_wallet_credentials_wallet_name ON wallet_credentials(wallet_id, name)',
|
|
601
|
-
'CREATE INDEX IF NOT EXISTS idx_wallet_credentials_global_name ON wallet_credentials(name) WHERE wallet_id IS NULL',
|
|
602
|
-
'CREATE INDEX IF NOT EXISTS idx_wallet_credentials_wallet_id ON wallet_credentials(wallet_id)',
|
|
603
|
-
'CREATE INDEX IF NOT EXISTS idx_wallet_credentials_expires_at ON wallet_credentials(expires_at) WHERE expires_at IS NOT NULL',
|
|
604
|
-
// v56: transactions action tracking indexes
|
|
605
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_action_kind ON transactions(action_kind)',
|
|
606
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_venue ON transactions(venue) WHERE venue IS NOT NULL',
|
|
607
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_external_id ON transactions(external_id) WHERE external_id IS NOT NULL',
|
|
608
|
-
// v57: composite index for external action tracking queries
|
|
609
|
-
'CREATE INDEX IF NOT EXISTS idx_transactions_action_kind_bridge_status ON transactions(action_kind, bridge_status) WHERE bridge_status IS NOT NULL',
|
|
610
|
-
];
|
|
611
|
-
}
|
|
612
30
|
/**
|
|
613
|
-
*
|
|
31
|
+
* Complete migration registry. Built by spreading all 6 version-range arrays.
|
|
614
32
|
* Each migration's version must be unique and greater than 1.
|
|
615
33
|
*/
|
|
616
|
-
export const MIGRATIONS = [
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
description: 'Expand agents network CHECK to include EVM networks',
|
|
625
|
-
managesOwnTransaction: true,
|
|
626
|
-
up: (sqlite) => {
|
|
627
|
-
// Step 1: Begin transaction (foreign_keys already OFF via runner)
|
|
628
|
-
sqlite.exec('BEGIN');
|
|
629
|
-
try {
|
|
630
|
-
// Step 2: Create new agents table with expanded CHECK (uses SSoT arrays)
|
|
631
|
-
sqlite.exec(`CREATE TABLE agents_new (
|
|
632
|
-
id TEXT PRIMARY KEY,
|
|
633
|
-
name TEXT NOT NULL,
|
|
634
|
-
chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
|
|
635
|
-
network TEXT NOT NULL CHECK (network IN (${inList(NETWORK_TYPES_WITH_LEGACY)})),
|
|
636
|
-
public_key TEXT NOT NULL,
|
|
637
|
-
status TEXT NOT NULL DEFAULT 'CREATING' CHECK (status IN (${inList(WALLET_STATUSES)})),
|
|
638
|
-
owner_address TEXT,
|
|
639
|
-
owner_verified INTEGER NOT NULL DEFAULT 0 CHECK (owner_verified IN (0, 1)),
|
|
640
|
-
created_at INTEGER NOT NULL,
|
|
641
|
-
updated_at INTEGER NOT NULL,
|
|
642
|
-
suspended_at INTEGER,
|
|
643
|
-
suspension_reason TEXT
|
|
644
|
-
)`);
|
|
645
|
-
// Step 3: Copy existing data
|
|
646
|
-
sqlite.exec('INSERT INTO agents_new SELECT * FROM agents');
|
|
647
|
-
// Step 4: Drop old table
|
|
648
|
-
sqlite.exec('DROP TABLE agents');
|
|
649
|
-
// Step 5: Rename new table
|
|
650
|
-
sqlite.exec('ALTER TABLE agents_new RENAME TO agents');
|
|
651
|
-
// Step 6: Recreate indexes
|
|
652
|
-
sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_agents_public_key ON agents(public_key)');
|
|
653
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status)');
|
|
654
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_agents_chain_network ON agents(chain, network)');
|
|
655
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_agents_owner_address ON agents(owner_address)');
|
|
656
|
-
// Step 7: Commit transaction
|
|
657
|
-
sqlite.exec('COMMIT');
|
|
658
|
-
}
|
|
659
|
-
catch (err) {
|
|
660
|
-
sqlite.exec('ROLLBACK');
|
|
661
|
-
throw err;
|
|
662
|
-
}
|
|
663
|
-
// Step 8: Re-enable foreign keys to run integrity check
|
|
664
|
-
sqlite.pragma('foreign_keys = ON');
|
|
665
|
-
// Step 9: Verify FK integrity
|
|
666
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
667
|
-
if (fkErrors.length > 0) {
|
|
668
|
-
throw new Error(`FK integrity check failed after v2 migration: ${JSON.stringify(fkErrors)}`);
|
|
669
|
-
}
|
|
670
|
-
// Note: Runner will also set foreign_keys = ON after we return,
|
|
671
|
-
// but we set it here to run the integrity check with FK enabled.
|
|
672
|
-
},
|
|
673
|
-
});
|
|
674
|
-
// ---------------------------------------------------------------------------
|
|
675
|
-
// v3: Rename agents to wallets (table, FK columns, indexes, enum data)
|
|
676
|
-
// ---------------------------------------------------------------------------
|
|
677
|
-
// Renames agents table to wallets, agent_id columns to wallet_id in 5 tables,
|
|
678
|
-
// recreates all affected indexes, and updates AGENT_* enum data to WALLET_*.
|
|
679
|
-
// Requires PRAGMA foreign_keys=OFF for table recreation (managesOwnTransaction).
|
|
680
|
-
MIGRATIONS.push({
|
|
681
|
-
version: 3,
|
|
682
|
-
description: 'Rename agents to wallets (table, FK columns, indexes, enum data)',
|
|
683
|
-
managesOwnTransaction: true,
|
|
684
|
-
up: (sqlite) => {
|
|
685
|
-
sqlite.exec('BEGIN');
|
|
686
|
-
try {
|
|
687
|
-
// Step 1: Rename agents table to wallets
|
|
688
|
-
sqlite.exec('ALTER TABLE agents RENAME TO wallets');
|
|
689
|
-
// Step 1b: Drop old agents indexes (ALTER TABLE RENAME doesn't rename indexes)
|
|
690
|
-
sqlite.exec('DROP INDEX IF EXISTS idx_agents_public_key');
|
|
691
|
-
sqlite.exec('DROP INDEX IF EXISTS idx_agents_status');
|
|
692
|
-
sqlite.exec('DROP INDEX IF EXISTS idx_agents_chain_network');
|
|
693
|
-
sqlite.exec('DROP INDEX IF EXISTS idx_agents_owner_address');
|
|
694
|
-
// Step 2: Recreate sessions with wallet_id instead of agent_id
|
|
695
|
-
sqlite.exec(`CREATE TABLE sessions_new (
|
|
696
|
-
id TEXT PRIMARY KEY,
|
|
697
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
698
|
-
token_hash TEXT NOT NULL,
|
|
699
|
-
expires_at INTEGER NOT NULL,
|
|
700
|
-
constraints TEXT,
|
|
701
|
-
usage_stats TEXT,
|
|
702
|
-
revoked_at INTEGER,
|
|
703
|
-
renewal_count INTEGER NOT NULL DEFAULT 0,
|
|
704
|
-
max_renewals INTEGER NOT NULL DEFAULT 30,
|
|
705
|
-
last_renewed_at INTEGER,
|
|
706
|
-
absolute_expires_at INTEGER NOT NULL,
|
|
707
|
-
created_at INTEGER NOT NULL
|
|
708
|
-
)`);
|
|
709
|
-
sqlite.exec(`INSERT INTO sessions_new (id, wallet_id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at)
|
|
710
|
-
SELECT id, agent_id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at FROM sessions`);
|
|
711
|
-
sqlite.exec('DROP TABLE sessions');
|
|
712
|
-
sqlite.exec('ALTER TABLE sessions_new RENAME TO sessions');
|
|
713
|
-
// Step 3: Recreate transactions with wallet_id instead of agent_id
|
|
714
|
-
sqlite.exec(`CREATE TABLE transactions_new (
|
|
715
|
-
id TEXT PRIMARY KEY,
|
|
716
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
717
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
718
|
-
chain TEXT NOT NULL,
|
|
719
|
-
tx_hash TEXT,
|
|
720
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
721
|
-
amount TEXT,
|
|
722
|
-
to_address TEXT,
|
|
723
|
-
token_mint TEXT,
|
|
724
|
-
contract_address TEXT,
|
|
725
|
-
method_signature TEXT,
|
|
726
|
-
spender_address TEXT,
|
|
727
|
-
approved_amount TEXT,
|
|
728
|
-
parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
|
|
729
|
-
batch_index INTEGER,
|
|
730
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
731
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
732
|
-
queued_at INTEGER,
|
|
733
|
-
executed_at INTEGER,
|
|
734
|
-
created_at INTEGER NOT NULL,
|
|
735
|
-
reserved_amount TEXT,
|
|
736
|
-
error TEXT,
|
|
737
|
-
metadata TEXT
|
|
738
|
-
)`);
|
|
739
|
-
sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata)
|
|
740
|
-
SELECT id, agent_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata FROM transactions`);
|
|
741
|
-
sqlite.exec('DROP TABLE transactions');
|
|
742
|
-
sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
|
|
743
|
-
// Step 4: Recreate policies with wallet_id instead of agent_id
|
|
744
|
-
sqlite.exec(`CREATE TABLE policies_new (
|
|
745
|
-
id TEXT PRIMARY KEY,
|
|
746
|
-
wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
|
|
747
|
-
type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
|
|
748
|
-
rules TEXT NOT NULL,
|
|
749
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
750
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
751
|
-
created_at INTEGER NOT NULL,
|
|
752
|
-
updated_at INTEGER NOT NULL
|
|
753
|
-
)`);
|
|
754
|
-
sqlite.exec(`INSERT INTO policies_new (id, wallet_id, type, rules, priority, enabled, created_at, updated_at)
|
|
755
|
-
SELECT id, agent_id, type, rules, priority, enabled, created_at, updated_at FROM policies`);
|
|
756
|
-
sqlite.exec('DROP TABLE policies');
|
|
757
|
-
sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
|
|
758
|
-
// Step 5: Recreate audit_log with wallet_id instead of agent_id (no FK constraint)
|
|
759
|
-
sqlite.exec(`CREATE TABLE audit_log_new (
|
|
760
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
761
|
-
timestamp INTEGER NOT NULL,
|
|
762
|
-
event_type TEXT NOT NULL,
|
|
763
|
-
actor TEXT NOT NULL,
|
|
764
|
-
wallet_id TEXT,
|
|
765
|
-
session_id TEXT,
|
|
766
|
-
tx_id TEXT,
|
|
767
|
-
details TEXT NOT NULL,
|
|
768
|
-
severity TEXT NOT NULL DEFAULT 'info' CHECK (severity IN ('info', 'warning', 'critical')),
|
|
769
|
-
ip_address TEXT
|
|
770
|
-
)`);
|
|
771
|
-
sqlite.exec(`INSERT INTO audit_log_new (id, timestamp, event_type, actor, wallet_id, session_id, tx_id, details, severity, ip_address)
|
|
772
|
-
SELECT id, timestamp, event_type, actor, agent_id, session_id, tx_id, details, severity, ip_address FROM audit_log`);
|
|
773
|
-
sqlite.exec('DROP TABLE audit_log');
|
|
774
|
-
sqlite.exec('ALTER TABLE audit_log_new RENAME TO audit_log');
|
|
775
|
-
// Step 6: Recreate notification_logs with wallet_id instead of agent_id (no FK constraint)
|
|
776
|
-
sqlite.exec(`CREATE TABLE notification_logs_new (
|
|
777
|
-
id TEXT PRIMARY KEY,
|
|
778
|
-
event_type TEXT NOT NULL,
|
|
779
|
-
wallet_id TEXT,
|
|
780
|
-
channel TEXT NOT NULL,
|
|
781
|
-
status TEXT NOT NULL CHECK (status IN (${inList(NOTIFICATION_LOG_STATUSES)})),
|
|
782
|
-
error TEXT,
|
|
783
|
-
created_at INTEGER NOT NULL
|
|
784
|
-
)`);
|
|
785
|
-
sqlite.exec(`INSERT INTO notification_logs_new (id, event_type, wallet_id, channel, status, error, created_at)
|
|
786
|
-
SELECT id, event_type, agent_id, channel, status, error, created_at FROM notification_logs`);
|
|
787
|
-
sqlite.exec('DROP TABLE notification_logs');
|
|
788
|
-
sqlite.exec('ALTER TABLE notification_logs_new RENAME TO notification_logs');
|
|
789
|
-
// Step 7: Recreate all indexes with wallet naming
|
|
790
|
-
// wallets table indexes
|
|
791
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_wallets_public_key ON wallets(public_key)');
|
|
792
|
-
sqlite.exec('CREATE INDEX idx_wallets_status ON wallets(status)');
|
|
793
|
-
sqlite.exec('CREATE INDEX idx_wallets_chain_network ON wallets(chain, network)');
|
|
794
|
-
sqlite.exec('CREATE INDEX idx_wallets_owner_address ON wallets(owner_address)');
|
|
795
|
-
// sessions indexes (all recreated because table was dropped/recreated)
|
|
796
|
-
sqlite.exec('CREATE INDEX idx_sessions_wallet_id ON sessions(wallet_id)');
|
|
797
|
-
sqlite.exec('CREATE INDEX idx_sessions_expires_at ON sessions(expires_at)');
|
|
798
|
-
sqlite.exec('CREATE INDEX idx_sessions_token_hash ON sessions(token_hash)');
|
|
799
|
-
// transactions indexes (all recreated because table was dropped/recreated)
|
|
800
|
-
sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
|
|
801
|
-
sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
|
|
802
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
|
|
803
|
-
sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
|
|
804
|
-
sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
|
|
805
|
-
sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
|
|
806
|
-
sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
|
|
807
|
-
sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
|
|
808
|
-
// policies indexes (all recreated because table was dropped/recreated)
|
|
809
|
-
sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
|
|
810
|
-
sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
|
|
811
|
-
// audit_log indexes (all recreated because table was dropped/recreated)
|
|
812
|
-
sqlite.exec('CREATE INDEX idx_audit_log_timestamp ON audit_log(timestamp)');
|
|
813
|
-
sqlite.exec('CREATE INDEX idx_audit_log_event_type ON audit_log(event_type)');
|
|
814
|
-
sqlite.exec('CREATE INDEX idx_audit_log_wallet_id ON audit_log(wallet_id)');
|
|
815
|
-
sqlite.exec('CREATE INDEX idx_audit_log_severity ON audit_log(severity)');
|
|
816
|
-
sqlite.exec('CREATE INDEX idx_audit_log_wallet_timestamp ON audit_log(wallet_id, timestamp)');
|
|
817
|
-
// notification_logs indexes (all recreated because table was dropped/recreated)
|
|
818
|
-
sqlite.exec('CREATE INDEX idx_notification_logs_event_type ON notification_logs(event_type)');
|
|
819
|
-
sqlite.exec('CREATE INDEX idx_notification_logs_wallet_id ON notification_logs(wallet_id)');
|
|
820
|
-
sqlite.exec('CREATE INDEX idx_notification_logs_status ON notification_logs(status)');
|
|
821
|
-
sqlite.exec('CREATE INDEX idx_notification_logs_created_at ON notification_logs(created_at)');
|
|
822
|
-
// Step 8: Update audit_log.event_type AGENT_* values to WALLET_*
|
|
823
|
-
sqlite.exec("UPDATE audit_log SET event_type = 'WALLET_CREATED' WHERE event_type = 'AGENT_CREATED'");
|
|
824
|
-
sqlite.exec("UPDATE audit_log SET event_type = 'WALLET_ACTIVATED' WHERE event_type = 'AGENT_ACTIVATED'");
|
|
825
|
-
sqlite.exec("UPDATE audit_log SET event_type = 'WALLET_SUSPENDED' WHERE event_type = 'AGENT_SUSPENDED'");
|
|
826
|
-
sqlite.exec("UPDATE audit_log SET event_type = 'WALLET_TERMINATED' WHERE event_type = 'AGENT_TERMINATED'");
|
|
827
|
-
// Step 9: Update notification_logs.event_type AGENT_SUSPENDED to WALLET_SUSPENDED
|
|
828
|
-
sqlite.exec("UPDATE notification_logs SET event_type = 'WALLET_SUSPENDED' WHERE event_type = 'AGENT_SUSPENDED'");
|
|
829
|
-
sqlite.exec('COMMIT');
|
|
830
|
-
}
|
|
831
|
-
catch (err) {
|
|
832
|
-
sqlite.exec('ROLLBACK');
|
|
833
|
-
throw err;
|
|
834
|
-
}
|
|
835
|
-
// Re-enable FK and verify integrity
|
|
836
|
-
sqlite.pragma('foreign_keys = ON');
|
|
837
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
838
|
-
if (fkErrors.length > 0) {
|
|
839
|
-
throw new Error(`FK integrity check failed after v3 migration: ${JSON.stringify(fkErrors)}`);
|
|
840
|
-
}
|
|
841
|
-
},
|
|
842
|
-
});
|
|
843
|
-
// ---------------------------------------------------------------------------
|
|
844
|
-
// v4: Create token_registry table for EVM token management
|
|
845
|
-
// ---------------------------------------------------------------------------
|
|
846
|
-
MIGRATIONS.push({
|
|
847
|
-
version: 4,
|
|
848
|
-
description: 'Create token_registry table for EVM token management',
|
|
849
|
-
up: (sqlite) => {
|
|
850
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS token_registry (
|
|
851
|
-
id TEXT PRIMARY KEY,
|
|
852
|
-
network TEXT NOT NULL,
|
|
853
|
-
address TEXT NOT NULL,
|
|
854
|
-
symbol TEXT NOT NULL,
|
|
855
|
-
name TEXT NOT NULL,
|
|
856
|
-
decimals INTEGER NOT NULL,
|
|
857
|
-
source TEXT NOT NULL DEFAULT 'custom' CHECK (source IN ('builtin', 'custom')),
|
|
858
|
-
created_at INTEGER NOT NULL
|
|
859
|
-
)`);
|
|
860
|
-
sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_token_registry_network_address ON token_registry(network, address)');
|
|
861
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_token_registry_network ON token_registry(network)');
|
|
862
|
-
},
|
|
863
|
-
});
|
|
864
|
-
// ---------------------------------------------------------------------------
|
|
865
|
-
// v5: Create settings table for operational config DB storage
|
|
866
|
-
// ---------------------------------------------------------------------------
|
|
867
|
-
MIGRATIONS.push({
|
|
868
|
-
version: 5,
|
|
869
|
-
description: 'Create settings table for operational config DB storage',
|
|
870
|
-
up: (sqlite) => {
|
|
871
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS settings (
|
|
872
|
-
key TEXT PRIMARY KEY,
|
|
873
|
-
value TEXT NOT NULL,
|
|
874
|
-
encrypted INTEGER NOT NULL DEFAULT 0 CHECK (encrypted IN (0, 1)),
|
|
875
|
-
category TEXT NOT NULL,
|
|
876
|
-
updated_at INTEGER NOT NULL
|
|
877
|
-
)`);
|
|
878
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_settings_category ON settings(category)');
|
|
879
|
-
},
|
|
880
|
-
});
|
|
881
|
-
// ---------------------------------------------------------------------------
|
|
882
|
-
// v6a: Add network column to transactions with backfill from wallets
|
|
883
|
-
// ---------------------------------------------------------------------------
|
|
884
|
-
// Standard migration (managesOwnTransaction: false). Adds nullable network
|
|
885
|
-
// column to transactions and backfills from wallets.network via FK relationship.
|
|
886
|
-
// Must run BEFORE v6b which removes wallets.network.
|
|
887
|
-
MIGRATIONS.push({
|
|
888
|
-
version: 6,
|
|
889
|
-
description: 'Add network column to transactions with backfill from wallets',
|
|
890
|
-
managesOwnTransaction: false,
|
|
891
|
-
up: (sqlite) => {
|
|
892
|
-
// SQL 1: Add nullable network column
|
|
893
|
-
sqlite.exec('ALTER TABLE transactions ADD COLUMN network TEXT');
|
|
894
|
-
// SQL 2: Backfill from wallets.network via FK relationship
|
|
895
|
-
sqlite.exec(`UPDATE transactions SET network = (
|
|
896
|
-
SELECT w.network FROM wallets w WHERE w.id = transactions.wallet_id
|
|
897
|
-
)`);
|
|
898
|
-
},
|
|
899
|
-
});
|
|
900
|
-
// ---------------------------------------------------------------------------
|
|
901
|
-
// v6b: Replace wallets.network with environment + default_network (12-step)
|
|
902
|
-
// ---------------------------------------------------------------------------
|
|
903
|
-
// 12-step table recreation. Converts wallets.network to environment + default_network.
|
|
904
|
-
// Recreates FK dependent tables (sessions, transactions, policies, audit_log).
|
|
905
|
-
// Requires PRAGMA foreign_keys=OFF (handled by managesOwnTransaction).
|
|
906
|
-
// @see docs/69-db-migration-v6-design.md section 3
|
|
907
|
-
MIGRATIONS.push({
|
|
908
|
-
version: 7,
|
|
909
|
-
description: 'Replace wallets.network with environment + default_network (12-step recreation)',
|
|
910
|
-
managesOwnTransaction: true,
|
|
911
|
-
up: (sqlite) => {
|
|
912
|
-
// Step 1: Begin transaction
|
|
913
|
-
sqlite.exec('BEGIN');
|
|
914
|
-
try {
|
|
915
|
-
// Step 2: Create wallets_new with environment + default_network
|
|
916
|
-
sqlite.exec(`CREATE TABLE wallets_new (
|
|
917
|
-
id TEXT PRIMARY KEY,
|
|
918
|
-
name TEXT NOT NULL,
|
|
919
|
-
chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
|
|
920
|
-
environment TEXT NOT NULL CHECK (environment IN (${inList(ENVIRONMENT_TYPES)})),
|
|
921
|
-
default_network TEXT CHECK (default_network IS NULL OR default_network IN (${inList(NETWORK_TYPES_WITH_LEGACY)})),
|
|
922
|
-
public_key TEXT NOT NULL,
|
|
923
|
-
status TEXT NOT NULL DEFAULT 'CREATING' CHECK (status IN (${inList(WALLET_STATUSES)})),
|
|
924
|
-
owner_address TEXT,
|
|
925
|
-
owner_verified INTEGER NOT NULL DEFAULT 0 CHECK (owner_verified IN (0, 1)),
|
|
926
|
-
created_at INTEGER NOT NULL,
|
|
927
|
-
updated_at INTEGER NOT NULL,
|
|
928
|
-
suspended_at INTEGER,
|
|
929
|
-
suspension_reason TEXT
|
|
930
|
-
)`);
|
|
931
|
-
// Step 3: Data transformation INSERT with 13 CASE WHEN branches
|
|
932
|
-
// Maps network -> environment using deriveEnvironment() logic (docs/68 section 3.3)
|
|
933
|
-
// Preserves original network as default_network
|
|
934
|
-
sqlite.exec(`INSERT INTO wallets_new (
|
|
935
|
-
id, name, chain, environment, default_network,
|
|
936
|
-
public_key, status, owner_address, owner_verified,
|
|
937
|
-
created_at, updated_at, suspended_at, suspension_reason
|
|
938
|
-
)
|
|
939
|
-
SELECT
|
|
940
|
-
id, name, chain,
|
|
941
|
-
CASE
|
|
942
|
-
WHEN network = 'mainnet' THEN 'mainnet'
|
|
943
|
-
WHEN network = 'devnet' THEN 'testnet'
|
|
944
|
-
WHEN network = 'testnet' THEN 'testnet'
|
|
945
|
-
WHEN network = 'ethereum-mainnet' THEN 'mainnet'
|
|
946
|
-
WHEN network = 'polygon-mainnet' THEN 'mainnet'
|
|
947
|
-
WHEN network = 'arbitrum-mainnet' THEN 'mainnet'
|
|
948
|
-
WHEN network = 'optimism-mainnet' THEN 'mainnet'
|
|
949
|
-
WHEN network = 'base-mainnet' THEN 'mainnet'
|
|
950
|
-
WHEN network = 'ethereum-sepolia' THEN 'testnet'
|
|
951
|
-
WHEN network = 'polygon-amoy' THEN 'testnet'
|
|
952
|
-
WHEN network = 'arbitrum-sepolia' THEN 'testnet'
|
|
953
|
-
WHEN network = 'optimism-sepolia' THEN 'testnet'
|
|
954
|
-
WHEN network = 'base-sepolia' THEN 'testnet'
|
|
955
|
-
ELSE 'testnet'
|
|
956
|
-
END AS environment,
|
|
957
|
-
network AS default_network,
|
|
958
|
-
public_key, status, owner_address, owner_verified,
|
|
959
|
-
created_at, updated_at, suspended_at, suspension_reason
|
|
960
|
-
FROM wallets`);
|
|
961
|
-
// Step 4: Drop old wallets table
|
|
962
|
-
sqlite.exec('DROP TABLE wallets');
|
|
963
|
-
// Step 5: Rename new table
|
|
964
|
-
sqlite.exec('ALTER TABLE wallets_new RENAME TO wallets');
|
|
965
|
-
// Step 6: Recreate wallets indexes
|
|
966
|
-
sqlite.exec('DROP INDEX IF EXISTS idx_wallets_chain_network');
|
|
967
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_wallets_public_key ON wallets(public_key)');
|
|
968
|
-
sqlite.exec('CREATE INDEX idx_wallets_status ON wallets(status)');
|
|
969
|
-
sqlite.exec('CREATE INDEX idx_wallets_chain_environment ON wallets(chain, environment)');
|
|
970
|
-
sqlite.exec('CREATE INDEX idx_wallets_owner_address ON wallets(owner_address)');
|
|
971
|
-
// Step 7: Recreate sessions (FK reconnection, no schema change)
|
|
972
|
-
sqlite.exec(`CREATE TABLE sessions_new (
|
|
973
|
-
id TEXT PRIMARY KEY,
|
|
974
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
975
|
-
token_hash TEXT NOT NULL,
|
|
976
|
-
expires_at INTEGER NOT NULL,
|
|
977
|
-
constraints TEXT,
|
|
978
|
-
usage_stats TEXT,
|
|
979
|
-
revoked_at INTEGER,
|
|
980
|
-
renewal_count INTEGER NOT NULL DEFAULT 0,
|
|
981
|
-
max_renewals INTEGER NOT NULL DEFAULT 30,
|
|
982
|
-
last_renewed_at INTEGER,
|
|
983
|
-
absolute_expires_at INTEGER NOT NULL,
|
|
984
|
-
created_at INTEGER NOT NULL
|
|
985
|
-
)`);
|
|
986
|
-
sqlite.exec(`INSERT INTO sessions_new (id, wallet_id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at)
|
|
987
|
-
SELECT id, wallet_id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at FROM sessions`);
|
|
988
|
-
sqlite.exec('DROP TABLE sessions');
|
|
989
|
-
sqlite.exec('ALTER TABLE sessions_new RENAME TO sessions');
|
|
990
|
-
sqlite.exec('CREATE INDEX idx_sessions_wallet_id ON sessions(wallet_id)');
|
|
991
|
-
sqlite.exec('CREATE INDEX idx_sessions_expires_at ON sessions(expires_at)');
|
|
992
|
-
sqlite.exec('CREATE INDEX idx_sessions_token_hash ON sessions(token_hash)');
|
|
993
|
-
// Step 8: Recreate transactions (network column with CHECK, FK reconnection)
|
|
994
|
-
sqlite.exec(`CREATE TABLE transactions_new (
|
|
995
|
-
id TEXT PRIMARY KEY,
|
|
996
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
997
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
998
|
-
chain TEXT NOT NULL,
|
|
999
|
-
tx_hash TEXT,
|
|
1000
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
1001
|
-
amount TEXT,
|
|
1002
|
-
to_address TEXT,
|
|
1003
|
-
token_mint TEXT,
|
|
1004
|
-
contract_address TEXT,
|
|
1005
|
-
method_signature TEXT,
|
|
1006
|
-
spender_address TEXT,
|
|
1007
|
-
approved_amount TEXT,
|
|
1008
|
-
parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
|
|
1009
|
-
batch_index INTEGER,
|
|
1010
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
1011
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
1012
|
-
queued_at INTEGER,
|
|
1013
|
-
executed_at INTEGER,
|
|
1014
|
-
created_at INTEGER NOT NULL,
|
|
1015
|
-
reserved_amount TEXT,
|
|
1016
|
-
error TEXT,
|
|
1017
|
-
metadata TEXT,
|
|
1018
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)}))
|
|
1019
|
-
)`);
|
|
1020
|
-
sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network)
|
|
1021
|
-
SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network FROM transactions`);
|
|
1022
|
-
sqlite.exec('DROP TABLE transactions');
|
|
1023
|
-
sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
|
|
1024
|
-
sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
|
|
1025
|
-
sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
|
|
1026
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
|
|
1027
|
-
sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
|
|
1028
|
-
sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
|
|
1029
|
-
sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
|
|
1030
|
-
sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
|
|
1031
|
-
sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
|
|
1032
|
-
// Step 9: Recreate policies (FK reconnection, no schema change -- v8 adds network)
|
|
1033
|
-
sqlite.exec(`CREATE TABLE policies_new (
|
|
1034
|
-
id TEXT PRIMARY KEY,
|
|
1035
|
-
wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1036
|
-
type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
|
|
1037
|
-
rules TEXT NOT NULL,
|
|
1038
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
1039
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
1040
|
-
created_at INTEGER NOT NULL,
|
|
1041
|
-
updated_at INTEGER NOT NULL
|
|
1042
|
-
)`);
|
|
1043
|
-
sqlite.exec(`INSERT INTO policies_new (id, wallet_id, type, rules, priority, enabled, created_at, updated_at)
|
|
1044
|
-
SELECT id, wallet_id, type, rules, priority, enabled, created_at, updated_at FROM policies`);
|
|
1045
|
-
sqlite.exec('DROP TABLE policies');
|
|
1046
|
-
sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
|
|
1047
|
-
sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
|
|
1048
|
-
sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
|
|
1049
|
-
// Step 10: Recreate audit_log (consistency with v3 pattern)
|
|
1050
|
-
sqlite.exec(`CREATE TABLE audit_log_new (
|
|
1051
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1052
|
-
timestamp INTEGER NOT NULL,
|
|
1053
|
-
event_type TEXT NOT NULL,
|
|
1054
|
-
actor TEXT NOT NULL,
|
|
1055
|
-
wallet_id TEXT,
|
|
1056
|
-
session_id TEXT,
|
|
1057
|
-
tx_id TEXT,
|
|
1058
|
-
details TEXT NOT NULL,
|
|
1059
|
-
severity TEXT NOT NULL DEFAULT 'info' CHECK (severity IN ('info', 'warning', 'critical')),
|
|
1060
|
-
ip_address TEXT
|
|
1061
|
-
)`);
|
|
1062
|
-
sqlite.exec(`INSERT INTO audit_log_new (id, timestamp, event_type, actor, wallet_id, session_id, tx_id, details, severity, ip_address)
|
|
1063
|
-
SELECT id, timestamp, event_type, actor, wallet_id, session_id, tx_id, details, severity, ip_address FROM audit_log`);
|
|
1064
|
-
sqlite.exec('DROP TABLE audit_log');
|
|
1065
|
-
sqlite.exec('ALTER TABLE audit_log_new RENAME TO audit_log');
|
|
1066
|
-
sqlite.exec('CREATE INDEX idx_audit_log_timestamp ON audit_log(timestamp)');
|
|
1067
|
-
sqlite.exec('CREATE INDEX idx_audit_log_event_type ON audit_log(event_type)');
|
|
1068
|
-
sqlite.exec('CREATE INDEX idx_audit_log_wallet_id ON audit_log(wallet_id)');
|
|
1069
|
-
sqlite.exec('CREATE INDEX idx_audit_log_severity ON audit_log(severity)');
|
|
1070
|
-
sqlite.exec('CREATE INDEX idx_audit_log_wallet_timestamp ON audit_log(wallet_id, timestamp)');
|
|
1071
|
-
// Step 11: Commit transaction
|
|
1072
|
-
sqlite.exec('COMMIT');
|
|
1073
|
-
}
|
|
1074
|
-
catch (err) {
|
|
1075
|
-
sqlite.exec('ROLLBACK');
|
|
1076
|
-
throw err;
|
|
1077
|
-
}
|
|
1078
|
-
// Step 12: Re-enable foreign keys and verify integrity
|
|
1079
|
-
sqlite.pragma('foreign_keys = ON');
|
|
1080
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
1081
|
-
if (fkErrors.length > 0) {
|
|
1082
|
-
throw new Error(`FK integrity violation after v6b: ${JSON.stringify(fkErrors)}`);
|
|
1083
|
-
}
|
|
1084
|
-
},
|
|
1085
|
-
});
|
|
1086
|
-
// ---------------------------------------------------------------------------
|
|
1087
|
-
// v8: Add network column to policies (12-step recreation)
|
|
34
|
+
export const MIGRATIONS = [
|
|
35
|
+
...v2to10,
|
|
36
|
+
...v11to20,
|
|
37
|
+
...v21to30,
|
|
38
|
+
...v31to40,
|
|
39
|
+
...v41to50,
|
|
40
|
+
...v51to59,
|
|
41
|
+
];
|
|
1088
42
|
// ---------------------------------------------------------------------------
|
|
1089
|
-
//
|
|
1090
|
-
// Requires 12-step recreation because SQLite cannot ALTER CHECK constraints.
|
|
1091
|
-
// @see docs/71-policy-engine-network-extension-design.md section 6
|
|
1092
|
-
MIGRATIONS.push({
|
|
1093
|
-
version: 8,
|
|
1094
|
-
description: 'Add network column to policies and ALLOWED_NETWORKS type support',
|
|
1095
|
-
managesOwnTransaction: true,
|
|
1096
|
-
up: (sqlite) => {
|
|
1097
|
-
// Step 1: Begin transaction
|
|
1098
|
-
sqlite.exec('BEGIN');
|
|
1099
|
-
try {
|
|
1100
|
-
// Step 2: Create policies_new with network column + updated CHECK
|
|
1101
|
-
sqlite.exec(`CREATE TABLE policies_new (
|
|
1102
|
-
id TEXT PRIMARY KEY,
|
|
1103
|
-
wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1104
|
-
type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
|
|
1105
|
-
rules TEXT NOT NULL,
|
|
1106
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
1107
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
1108
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)})),
|
|
1109
|
-
created_at INTEGER NOT NULL,
|
|
1110
|
-
updated_at INTEGER NOT NULL
|
|
1111
|
-
)`);
|
|
1112
|
-
// Step 3: Copy existing policies with network=NULL
|
|
1113
|
-
sqlite.exec(`INSERT INTO policies_new (
|
|
1114
|
-
id, wallet_id, type, rules, priority, enabled, network, created_at, updated_at
|
|
1115
|
-
)
|
|
1116
|
-
SELECT
|
|
1117
|
-
id, wallet_id, type, rules, priority, enabled, NULL, created_at, updated_at
|
|
1118
|
-
FROM policies`);
|
|
1119
|
-
// Step 4: Drop old table
|
|
1120
|
-
sqlite.exec('DROP TABLE policies');
|
|
1121
|
-
// Step 5: Rename new table
|
|
1122
|
-
sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
|
|
1123
|
-
// Step 6: Recreate existing indexes
|
|
1124
|
-
sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
|
|
1125
|
-
sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
|
|
1126
|
-
// Step 7: Create new network index
|
|
1127
|
-
sqlite.exec('CREATE INDEX idx_policies_network ON policies(network)');
|
|
1128
|
-
// Step 8: Commit
|
|
1129
|
-
sqlite.exec('COMMIT');
|
|
1130
|
-
}
|
|
1131
|
-
catch (err) {
|
|
1132
|
-
sqlite.exec('ROLLBACK');
|
|
1133
|
-
throw err;
|
|
1134
|
-
}
|
|
1135
|
-
// Step 9: Re-enable foreign keys and verify integrity
|
|
1136
|
-
sqlite.pragma('foreign_keys = ON');
|
|
1137
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
1138
|
-
if (fkErrors.length > 0) {
|
|
1139
|
-
throw new Error(`FK integrity violation after v8: ${JSON.stringify(fkErrors)}`);
|
|
1140
|
-
}
|
|
1141
|
-
},
|
|
1142
|
-
});
|
|
1143
|
-
// ---------------------------------------------------------------------------
|
|
1144
|
-
// v9: Add SIGNED status and SIGN type to transactions CHECK constraints
|
|
1145
|
-
// ---------------------------------------------------------------------------
|
|
1146
|
-
// SSoT arrays TRANSACTION_STATUSES/TRANSACTION_TYPES에 새 값이 추가되었으므로
|
|
1147
|
-
// transactions 테이블의 CHECK 제약을 갱신해야 한다. SQLite는 ALTER CHECK 불가 -> 12-step 재생성.
|
|
1148
|
-
// 이 마이그레이션은 transactions 테이블만 재생성한다 (다른 테이블에 영향 없음).
|
|
1149
|
-
MIGRATIONS.push({
|
|
1150
|
-
version: 9,
|
|
1151
|
-
description: 'Add SIGNED status and SIGN type to transactions CHECK constraints',
|
|
1152
|
-
managesOwnTransaction: true,
|
|
1153
|
-
up: (sqlite) => {
|
|
1154
|
-
// Step 1: Begin transaction
|
|
1155
|
-
sqlite.exec('BEGIN');
|
|
1156
|
-
try {
|
|
1157
|
-
// Step 2: Create transactions_new with updated CHECK constraints
|
|
1158
|
-
sqlite.exec(`CREATE TABLE transactions_new (
|
|
1159
|
-
id TEXT PRIMARY KEY,
|
|
1160
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
1161
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
1162
|
-
chain TEXT NOT NULL,
|
|
1163
|
-
tx_hash TEXT,
|
|
1164
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
1165
|
-
amount TEXT,
|
|
1166
|
-
to_address TEXT,
|
|
1167
|
-
token_mint TEXT,
|
|
1168
|
-
contract_address TEXT,
|
|
1169
|
-
method_signature TEXT,
|
|
1170
|
-
spender_address TEXT,
|
|
1171
|
-
approved_amount TEXT,
|
|
1172
|
-
parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
|
|
1173
|
-
batch_index INTEGER,
|
|
1174
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
1175
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
1176
|
-
queued_at INTEGER,
|
|
1177
|
-
executed_at INTEGER,
|
|
1178
|
-
created_at INTEGER NOT NULL,
|
|
1179
|
-
reserved_amount TEXT,
|
|
1180
|
-
error TEXT,
|
|
1181
|
-
metadata TEXT,
|
|
1182
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)}))
|
|
1183
|
-
)`);
|
|
1184
|
-
// Step 3: Copy existing data
|
|
1185
|
-
sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network)
|
|
1186
|
-
SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network FROM transactions`);
|
|
1187
|
-
// Step 4: Drop old table
|
|
1188
|
-
sqlite.exec('DROP TABLE transactions');
|
|
1189
|
-
// Step 5: Rename new table
|
|
1190
|
-
sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
|
|
1191
|
-
// Step 6: Recreate all 8 existing indexes
|
|
1192
|
-
sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
|
|
1193
|
-
sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
|
|
1194
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
|
|
1195
|
-
sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
|
|
1196
|
-
sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
|
|
1197
|
-
sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
|
|
1198
|
-
sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
|
|
1199
|
-
sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
|
|
1200
|
-
// Step 7: Commit
|
|
1201
|
-
sqlite.exec('COMMIT');
|
|
1202
|
-
}
|
|
1203
|
-
catch (err) {
|
|
1204
|
-
sqlite.exec('ROLLBACK');
|
|
1205
|
-
throw err;
|
|
1206
|
-
}
|
|
1207
|
-
// Step 8: Re-enable foreign keys and verify integrity
|
|
1208
|
-
sqlite.pragma('foreign_keys = ON');
|
|
1209
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
1210
|
-
if (fkErrors.length > 0) {
|
|
1211
|
-
throw new Error(`FK integrity violation after v9: ${JSON.stringify(fkErrors)}`);
|
|
1212
|
-
}
|
|
1213
|
-
},
|
|
1214
|
-
});
|
|
43
|
+
// Migration runner
|
|
1215
44
|
// ---------------------------------------------------------------------------
|
|
1216
|
-
// v10: Add message column to notification_logs
|
|
1217
|
-
// ---------------------------------------------------------------------------
|
|
1218
|
-
// Simple ALTER TABLE ADD COLUMN -- no CHECK constraint changes, no table recreation needed.
|
|
1219
|
-
MIGRATIONS.push({
|
|
1220
|
-
version: 10,
|
|
1221
|
-
description: 'Add message column to notification_logs',
|
|
1222
|
-
up: (sqlite) => {
|
|
1223
|
-
sqlite.exec('ALTER TABLE notification_logs ADD COLUMN message TEXT');
|
|
1224
|
-
},
|
|
1225
|
-
});
|
|
1226
|
-
// ---------------------------------------------------------------------------
|
|
1227
|
-
// v11: Create api_keys table for Action Provider API key storage
|
|
1228
|
-
// ---------------------------------------------------------------------------
|
|
1229
|
-
MIGRATIONS.push({
|
|
1230
|
-
version: 11,
|
|
1231
|
-
description: 'Add api_keys table for Action Provider API key storage',
|
|
1232
|
-
up: (sqlite) => {
|
|
1233
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS api_keys (
|
|
1234
|
-
provider_name TEXT PRIMARY KEY,
|
|
1235
|
-
encrypted_key TEXT NOT NULL,
|
|
1236
|
-
created_at INTEGER NOT NULL,
|
|
1237
|
-
updated_at INTEGER NOT NULL
|
|
1238
|
-
)`);
|
|
1239
|
-
},
|
|
1240
|
-
});
|
|
1241
|
-
// ---------------------------------------------------------------------------
|
|
1242
|
-
// v12: Add X402_PAYMENT to transactions and X402_ALLOWED_DOMAINS to policies CHECK constraints
|
|
1243
|
-
// ---------------------------------------------------------------------------
|
|
1244
|
-
// x402 결제 지원을 위해 transactions.type에 X402_PAYMENT,
|
|
1245
|
-
// policies.type에 X402_ALLOWED_DOMAINS가 CHECK 제약에 포함되어야 한다.
|
|
1246
|
-
// SQLite는 ALTER CHECK 불가 -> transactions와 policies 모두 12-step 재생성.
|
|
1247
|
-
MIGRATIONS.push({
|
|
1248
|
-
version: 12,
|
|
1249
|
-
description: 'Add X402_PAYMENT to transactions and X402_ALLOWED_DOMAINS to policies CHECK constraints',
|
|
1250
|
-
managesOwnTransaction: true,
|
|
1251
|
-
up: (sqlite) => {
|
|
1252
|
-
sqlite.exec('BEGIN');
|
|
1253
|
-
try {
|
|
1254
|
-
// ── Part 1: transactions 테이블 재생성 ──
|
|
1255
|
-
// v9과 동일 DDL. TRANSACTION_TYPES SSoT에 X402_PAYMENT이 이미 포함됨.
|
|
1256
|
-
sqlite.exec(`CREATE TABLE transactions_new (
|
|
1257
|
-
id TEXT PRIMARY KEY,
|
|
1258
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
1259
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
1260
|
-
chain TEXT NOT NULL,
|
|
1261
|
-
tx_hash TEXT,
|
|
1262
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
1263
|
-
amount TEXT,
|
|
1264
|
-
to_address TEXT,
|
|
1265
|
-
token_mint TEXT,
|
|
1266
|
-
contract_address TEXT,
|
|
1267
|
-
method_signature TEXT,
|
|
1268
|
-
spender_address TEXT,
|
|
1269
|
-
approved_amount TEXT,
|
|
1270
|
-
parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
|
|
1271
|
-
batch_index INTEGER,
|
|
1272
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
1273
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
1274
|
-
queued_at INTEGER,
|
|
1275
|
-
executed_at INTEGER,
|
|
1276
|
-
created_at INTEGER NOT NULL,
|
|
1277
|
-
reserved_amount TEXT,
|
|
1278
|
-
error TEXT,
|
|
1279
|
-
metadata TEXT,
|
|
1280
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)}))
|
|
1281
|
-
)`);
|
|
1282
|
-
sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network)
|
|
1283
|
-
SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, error, metadata, network FROM transactions`);
|
|
1284
|
-
sqlite.exec('DROP TABLE transactions');
|
|
1285
|
-
sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
|
|
1286
|
-
// Recreate all 8 indexes (same as v9)
|
|
1287
|
-
sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
|
|
1288
|
-
sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
|
|
1289
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
|
|
1290
|
-
sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
|
|
1291
|
-
sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
|
|
1292
|
-
sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
|
|
1293
|
-
sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
|
|
1294
|
-
sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
|
|
1295
|
-
// ── Part 2: policies 테이블 재생성 ──
|
|
1296
|
-
// v8과 동일 DDL. POLICY_TYPES SSoT에 X402_ALLOWED_DOMAINS가 이미 포함됨.
|
|
1297
|
-
sqlite.exec(`CREATE TABLE policies_new (
|
|
1298
|
-
id TEXT PRIMARY KEY,
|
|
1299
|
-
wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1300
|
-
type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
|
|
1301
|
-
rules TEXT NOT NULL,
|
|
1302
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
1303
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
1304
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)})),
|
|
1305
|
-
created_at INTEGER NOT NULL,
|
|
1306
|
-
updated_at INTEGER NOT NULL
|
|
1307
|
-
)`);
|
|
1308
|
-
sqlite.exec(`INSERT INTO policies_new (id, wallet_id, type, rules, priority, enabled, network, created_at, updated_at)
|
|
1309
|
-
SELECT id, wallet_id, type, rules, priority, enabled, network, created_at, updated_at FROM policies`);
|
|
1310
|
-
sqlite.exec('DROP TABLE policies');
|
|
1311
|
-
sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
|
|
1312
|
-
// Recreate 3 indexes (same as v8)
|
|
1313
|
-
sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
|
|
1314
|
-
sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
|
|
1315
|
-
sqlite.exec('CREATE INDEX idx_policies_network ON policies(network)');
|
|
1316
|
-
sqlite.exec('COMMIT');
|
|
1317
|
-
}
|
|
1318
|
-
catch (err) {
|
|
1319
|
-
sqlite.exec('ROLLBACK');
|
|
1320
|
-
throw err;
|
|
1321
|
-
}
|
|
1322
|
-
// Re-enable foreign keys and verify integrity
|
|
1323
|
-
sqlite.pragma('foreign_keys = ON');
|
|
1324
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
1325
|
-
if (fkErrors.length > 0) {
|
|
1326
|
-
throw new Error(`FK integrity violation after v12: ${JSON.stringify(fkErrors)}`);
|
|
1327
|
-
}
|
|
1328
|
-
},
|
|
1329
|
-
});
|
|
1330
|
-
// ---------------------------------------------------------------------------
|
|
1331
|
-
// v13: Add amount_usd and reserved_amount_usd columns to transactions
|
|
1332
|
-
// ---------------------------------------------------------------------------
|
|
1333
|
-
// Simple ALTER TABLE ADD COLUMN -- no CHECK constraint changes, no table recreation needed.
|
|
1334
|
-
// These nullable REAL columns store USD-denominated amounts for cumulative spending limit evaluation.
|
|
1335
|
-
// amount_usd: confirmed USD amount (persists after CONFIRMED), reserved_amount_usd: cleared on release.
|
|
1336
|
-
MIGRATIONS.push({
|
|
1337
|
-
version: 13,
|
|
1338
|
-
description: 'Add amount_usd and reserved_amount_usd columns to transactions',
|
|
1339
|
-
up: (sqlite) => {
|
|
1340
|
-
sqlite.exec('ALTER TABLE transactions ADD COLUMN amount_usd REAL');
|
|
1341
|
-
sqlite.exec('ALTER TABLE transactions ADD COLUMN reserved_amount_usd REAL');
|
|
1342
|
-
},
|
|
1343
|
-
});
|
|
1344
|
-
// ---------------------------------------------------------------------------
|
|
1345
|
-
// v14: Migrate kill_switch_state values: NORMAL->ACTIVE, ACTIVATED->SUSPENDED
|
|
1346
|
-
// ---------------------------------------------------------------------------
|
|
1347
|
-
// Kill Switch 3-state machine migration.
|
|
1348
|
-
// Old 2-state values (NORMAL, ACTIVATED, RECOVERING) are converted to
|
|
1349
|
-
// new 3-state values (ACTIVE, SUSPENDED, LOCKED).
|
|
1350
|
-
// Simple UPDATE on key_value_store -- no schema changes, no table recreation.
|
|
1351
|
-
MIGRATIONS.push({
|
|
1352
|
-
version: 14,
|
|
1353
|
-
description: 'Migrate kill_switch_state values: NORMAL->ACTIVE, ACTIVATED->SUSPENDED',
|
|
1354
|
-
up: (sqlite) => {
|
|
1355
|
-
const now = Math.floor(Date.now() / 1000);
|
|
1356
|
-
// NORMAL -> ACTIVE
|
|
1357
|
-
sqlite
|
|
1358
|
-
.prepare("UPDATE key_value_store SET value = 'ACTIVE', updated_at = ? WHERE key = 'kill_switch_state' AND value = 'NORMAL'")
|
|
1359
|
-
.run(now);
|
|
1360
|
-
// ACTIVATED -> SUSPENDED
|
|
1361
|
-
sqlite
|
|
1362
|
-
.prepare("UPDATE key_value_store SET value = 'SUSPENDED', updated_at = ? WHERE key = 'kill_switch_state' AND value = 'ACTIVATED'")
|
|
1363
|
-
.run(now);
|
|
1364
|
-
// RECOVERING -> ACTIVE (v0.10 design removed RECOVERING state)
|
|
1365
|
-
sqlite
|
|
1366
|
-
.prepare("UPDATE key_value_store SET value = 'ACTIVE', updated_at = ? WHERE key = 'kill_switch_state' AND value = 'RECOVERING'")
|
|
1367
|
-
.run(now);
|
|
1368
|
-
},
|
|
1369
|
-
});
|
|
1370
|
-
// ---------------------------------------------------------------------------
|
|
1371
|
-
// v15: Create telegram_users table for Telegram Bot user management
|
|
1372
|
-
// ---------------------------------------------------------------------------
|
|
1373
|
-
MIGRATIONS.push({
|
|
1374
|
-
version: 15,
|
|
1375
|
-
description: 'Create telegram_users table for Telegram Bot user management',
|
|
1376
|
-
up: (sqlite) => {
|
|
1377
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS telegram_users (
|
|
1378
|
-
chat_id INTEGER PRIMARY KEY,
|
|
1379
|
-
username TEXT,
|
|
1380
|
-
role TEXT NOT NULL DEFAULT 'PENDING' CHECK (role IN ('PENDING', 'ADMIN', 'READONLY')),
|
|
1381
|
-
registered_at INTEGER NOT NULL,
|
|
1382
|
-
approved_at INTEGER
|
|
1383
|
-
)`);
|
|
1384
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_telegram_users_role ON telegram_users(role)');
|
|
1385
|
-
},
|
|
1386
|
-
});
|
|
1387
|
-
// ---------------------------------------------------------------------------
|
|
1388
|
-
// v16: Add WC infra: wc_sessions table, wc_store table, pending_approvals.approval_channel
|
|
1389
|
-
// ---------------------------------------------------------------------------
|
|
1390
|
-
MIGRATIONS.push({
|
|
1391
|
-
version: 16,
|
|
1392
|
-
description: 'Add WC infra: wc_sessions table, wc_store table, pending_approvals.approval_channel',
|
|
1393
|
-
up: (sqlite) => {
|
|
1394
|
-
// 1. pending_approvals.approval_channel 추가
|
|
1395
|
-
sqlite.exec("ALTER TABLE pending_approvals ADD COLUMN approval_channel TEXT DEFAULT 'rest_api'");
|
|
1396
|
-
// 2. wc_sessions 테이블 생성
|
|
1397
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS wc_sessions (
|
|
1398
|
-
wallet_id TEXT PRIMARY KEY REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1399
|
-
topic TEXT NOT NULL UNIQUE,
|
|
1400
|
-
peer_meta TEXT,
|
|
1401
|
-
chain_id TEXT NOT NULL,
|
|
1402
|
-
owner_address TEXT NOT NULL,
|
|
1403
|
-
namespaces TEXT,
|
|
1404
|
-
expiry INTEGER NOT NULL,
|
|
1405
|
-
created_at INTEGER NOT NULL
|
|
1406
|
-
)`);
|
|
1407
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_wc_sessions_topic ON wc_sessions(topic)');
|
|
1408
|
-
// 3. wc_store 테이블 생성 (IKeyValueStorage용)
|
|
1409
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS wc_store (
|
|
1410
|
-
key TEXT PRIMARY KEY,
|
|
1411
|
-
value TEXT NOT NULL
|
|
1412
|
-
)`);
|
|
1413
|
-
},
|
|
1414
|
-
});
|
|
1415
|
-
// ---------------------------------------------------------------------------
|
|
1416
|
-
// Migration v17: Add source column to sessions table
|
|
1417
|
-
// ---------------------------------------------------------------------------
|
|
1418
|
-
MIGRATIONS.push({
|
|
1419
|
-
version: 17,
|
|
1420
|
-
description: 'Add source column to sessions table (api/mcp)',
|
|
1421
|
-
up: (sqlite) => {
|
|
1422
|
-
sqlite.exec("ALTER TABLE sessions ADD COLUMN source TEXT NOT NULL DEFAULT 'api'");
|
|
1423
|
-
},
|
|
1424
|
-
});
|
|
1425
|
-
// ---------------------------------------------------------------------------
|
|
1426
|
-
// Migration v18: Add owner_approval_method column to wallets table
|
|
1427
|
-
// ---------------------------------------------------------------------------
|
|
1428
|
-
// Simple ALTER TABLE ADD COLUMN -- nullable, no CHECK via ALTER (SQLite limitation).
|
|
1429
|
-
// CHECK is enforced at application level via Zod ApprovalMethodSchema.
|
|
1430
|
-
// Fresh DB DDL includes CHECK constraint directly.
|
|
1431
|
-
MIGRATIONS.push({
|
|
1432
|
-
version: 18,
|
|
1433
|
-
description: 'Add owner_approval_method column to wallets table',
|
|
1434
|
-
up: (sqlite) => {
|
|
1435
|
-
sqlite.exec('ALTER TABLE wallets ADD COLUMN owner_approval_method TEXT');
|
|
1436
|
-
},
|
|
1437
|
-
});
|
|
1438
|
-
// ---------------------------------------------------------------------------
|
|
1439
|
-
// Migration v19: Create session_wallets junction table, migrate sessions.wallet_id, drop wallet_id column
|
|
1440
|
-
// ---------------------------------------------------------------------------
|
|
1441
|
-
// 12-step table recreation for sessions (wallet_id column removal).
|
|
1442
|
-
// Creates session_wallets junction table, migrates existing 1:1 data as is_default=1,
|
|
1443
|
-
// then recreates sessions without wallet_id and reconnects FK-dependent transactions.
|
|
1444
|
-
// wallet_id IS NULL sessions are skipped (no crash).
|
|
1445
|
-
MIGRATIONS.push({
|
|
1446
|
-
version: 19,
|
|
1447
|
-
description: 'Create session_wallets junction table, migrate sessions.wallet_id, drop wallet_id column',
|
|
1448
|
-
managesOwnTransaction: true,
|
|
1449
|
-
up: (sqlite) => {
|
|
1450
|
-
sqlite.exec('BEGIN');
|
|
1451
|
-
try {
|
|
1452
|
-
// Step 1: Create session_wallets table
|
|
1453
|
-
sqlite.exec(`CREATE TABLE session_wallets (
|
|
1454
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
1455
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1456
|
-
is_default INTEGER NOT NULL DEFAULT 0,
|
|
1457
|
-
created_at INTEGER NOT NULL,
|
|
1458
|
-
PRIMARY KEY (session_id, wallet_id)
|
|
1459
|
-
)`);
|
|
1460
|
-
sqlite.exec('CREATE INDEX idx_session_wallets_session ON session_wallets(session_id)');
|
|
1461
|
-
sqlite.exec('CREATE INDEX idx_session_wallets_wallet ON session_wallets(wallet_id)');
|
|
1462
|
-
// Step 2: Migrate existing sessions.wallet_id -> session_wallets (is_default = 1)
|
|
1463
|
-
// wallet_id가 NULL인 비정상 세션은 스킵 (WHERE wallet_id IS NOT NULL)
|
|
1464
|
-
sqlite.exec(`INSERT INTO session_wallets (session_id, wallet_id, is_default, created_at)
|
|
1465
|
-
SELECT id, wallet_id, 1, CAST(strftime('%s', 'now') AS INTEGER)
|
|
1466
|
-
FROM sessions
|
|
1467
|
-
WHERE wallet_id IS NOT NULL`);
|
|
1468
|
-
// Step 3: Recreate sessions table without wallet_id column (12-step)
|
|
1469
|
-
sqlite.exec(`CREATE TABLE sessions_new (
|
|
1470
|
-
id TEXT PRIMARY KEY,
|
|
1471
|
-
token_hash TEXT NOT NULL,
|
|
1472
|
-
expires_at INTEGER NOT NULL,
|
|
1473
|
-
constraints TEXT,
|
|
1474
|
-
usage_stats TEXT,
|
|
1475
|
-
revoked_at INTEGER,
|
|
1476
|
-
renewal_count INTEGER NOT NULL DEFAULT 0,
|
|
1477
|
-
max_renewals INTEGER NOT NULL DEFAULT 30,
|
|
1478
|
-
last_renewed_at INTEGER,
|
|
1479
|
-
absolute_expires_at INTEGER NOT NULL,
|
|
1480
|
-
created_at INTEGER NOT NULL,
|
|
1481
|
-
source TEXT NOT NULL DEFAULT 'api'
|
|
1482
|
-
)`);
|
|
1483
|
-
// Step 4: Copy data (excluding wallet_id)
|
|
1484
|
-
sqlite.exec(`INSERT INTO sessions_new (id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at, source)
|
|
1485
|
-
SELECT id, token_hash, expires_at, constraints, usage_stats, revoked_at, renewal_count, max_renewals, last_renewed_at, absolute_expires_at, created_at, source
|
|
1486
|
-
FROM sessions`);
|
|
1487
|
-
// Step 5: Drop old sessions table
|
|
1488
|
-
sqlite.exec('DROP TABLE sessions');
|
|
1489
|
-
// Step 6: Rename new table
|
|
1490
|
-
sqlite.exec('ALTER TABLE sessions_new RENAME TO sessions');
|
|
1491
|
-
// Step 7: Recreate sessions indexes (without wallet_id index)
|
|
1492
|
-
sqlite.exec('CREATE INDEX idx_sessions_expires_at ON sessions(expires_at)');
|
|
1493
|
-
sqlite.exec('CREATE INDEX idx_sessions_token_hash ON sessions(token_hash)');
|
|
1494
|
-
// Step 8: Recreate transactions table to fix FK reference to sessions
|
|
1495
|
-
// (sessions was dropped+renamed, need FK reconnection)
|
|
1496
|
-
// transactions references sessions(id) ON DELETE SET NULL
|
|
1497
|
-
sqlite.exec(`CREATE TABLE transactions_new (
|
|
1498
|
-
id TEXT PRIMARY KEY,
|
|
1499
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
1500
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
1501
|
-
chain TEXT NOT NULL,
|
|
1502
|
-
tx_hash TEXT,
|
|
1503
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
1504
|
-
amount TEXT,
|
|
1505
|
-
to_address TEXT,
|
|
1506
|
-
token_mint TEXT,
|
|
1507
|
-
contract_address TEXT,
|
|
1508
|
-
method_signature TEXT,
|
|
1509
|
-
spender_address TEXT,
|
|
1510
|
-
approved_amount TEXT,
|
|
1511
|
-
parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
|
|
1512
|
-
batch_index INTEGER,
|
|
1513
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
1514
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
1515
|
-
queued_at INTEGER,
|
|
1516
|
-
executed_at INTEGER,
|
|
1517
|
-
created_at INTEGER NOT NULL,
|
|
1518
|
-
reserved_amount TEXT,
|
|
1519
|
-
amount_usd REAL,
|
|
1520
|
-
reserved_amount_usd REAL,
|
|
1521
|
-
error TEXT,
|
|
1522
|
-
metadata TEXT,
|
|
1523
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)}))
|
|
1524
|
-
)`);
|
|
1525
|
-
sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, amount_usd, reserved_amount_usd, error, metadata, network)
|
|
1526
|
-
SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, amount_usd, reserved_amount_usd, error, metadata, network FROM transactions`);
|
|
1527
|
-
sqlite.exec('DROP TABLE transactions');
|
|
1528
|
-
sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
|
|
1529
|
-
// Recreate transactions indexes
|
|
1530
|
-
sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
|
|
1531
|
-
sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
|
|
1532
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
|
|
1533
|
-
sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
|
|
1534
|
-
sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
|
|
1535
|
-
sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
|
|
1536
|
-
sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
|
|
1537
|
-
sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
|
|
1538
|
-
sqlite.exec('COMMIT');
|
|
1539
|
-
}
|
|
1540
|
-
catch (err) {
|
|
1541
|
-
sqlite.exec('ROLLBACK');
|
|
1542
|
-
throw err;
|
|
1543
|
-
}
|
|
1544
|
-
// Re-enable foreign keys and verify integrity
|
|
1545
|
-
sqlite.pragma('foreign_keys = ON');
|
|
1546
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
1547
|
-
if (fkErrors.length > 0) {
|
|
1548
|
-
throw new Error(`FK integrity violation after v19: ${JSON.stringify(fkErrors)}`);
|
|
1549
|
-
}
|
|
1550
|
-
},
|
|
1551
|
-
});
|
|
1552
|
-
// ---------------------------------------------------------------------------
|
|
1553
|
-
// Migration v20: Add token_issued_count to sessions (v26.5)
|
|
1554
|
-
// ---------------------------------------------------------------------------
|
|
1555
|
-
MIGRATIONS.push({
|
|
1556
|
-
version: 20,
|
|
1557
|
-
description: 'Add token_issued_count column to sessions table',
|
|
1558
|
-
up: (sqlite) => {
|
|
1559
|
-
sqlite.exec('ALTER TABLE sessions ADD COLUMN token_issued_count INTEGER NOT NULL DEFAULT 1');
|
|
1560
|
-
},
|
|
1561
|
-
});
|
|
1562
|
-
// ---------------------------------------------------------------------------
|
|
1563
|
-
// Migration v21: Add incoming transaction monitoring tables and wallet opt-in column
|
|
1564
|
-
// ---------------------------------------------------------------------------
|
|
1565
|
-
// Creates incoming_transactions (13 columns + UNIQUE constraint), incoming_tx_cursors (6 columns),
|
|
1566
|
-
// and adds wallets.monitor_incoming opt-in column.
|
|
1567
|
-
// Simple ALTER TABLE + CREATE TABLE -- no CHECK constraint changes on existing tables,
|
|
1568
|
-
// no table recreation needed.
|
|
1569
|
-
MIGRATIONS.push({
|
|
1570
|
-
version: 21,
|
|
1571
|
-
description: 'Add incoming transaction monitoring tables and wallet opt-in column',
|
|
1572
|
-
up: (sqlite) => {
|
|
1573
|
-
// 1. wallets.monitor_incoming opt-in column
|
|
1574
|
-
sqlite.exec('ALTER TABLE wallets ADD COLUMN monitor_incoming INTEGER NOT NULL DEFAULT 0');
|
|
1575
|
-
// 2. incoming_transactions table (13 columns + UNIQUE constraint)
|
|
1576
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS incoming_transactions (
|
|
1577
|
-
id TEXT PRIMARY KEY,
|
|
1578
|
-
tx_hash TEXT NOT NULL,
|
|
1579
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1580
|
-
from_address TEXT NOT NULL,
|
|
1581
|
-
amount TEXT NOT NULL,
|
|
1582
|
-
token_address TEXT,
|
|
1583
|
-
chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
|
|
1584
|
-
network TEXT NOT NULL,
|
|
1585
|
-
status TEXT NOT NULL DEFAULT 'DETECTED' CHECK (status IN (${inList(INCOMING_TX_STATUSES)})),
|
|
1586
|
-
block_number INTEGER,
|
|
1587
|
-
detected_at INTEGER NOT NULL,
|
|
1588
|
-
confirmed_at INTEGER,
|
|
1589
|
-
is_suspicious INTEGER NOT NULL DEFAULT 0,
|
|
1590
|
-
UNIQUE(tx_hash, wallet_id)
|
|
1591
|
-
)`);
|
|
1592
|
-
// 3. incoming_tx_cursors table (6 columns)
|
|
1593
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS incoming_tx_cursors (
|
|
1594
|
-
wallet_id TEXT PRIMARY KEY REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1595
|
-
chain TEXT NOT NULL,
|
|
1596
|
-
network TEXT NOT NULL,
|
|
1597
|
-
last_signature TEXT,
|
|
1598
|
-
last_block_number INTEGER,
|
|
1599
|
-
updated_at INTEGER NOT NULL
|
|
1600
|
-
)`);
|
|
1601
|
-
// 4. Indexes on incoming_transactions
|
|
1602
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_incoming_tx_wallet_detected ON incoming_transactions(wallet_id, detected_at DESC)');
|
|
1603
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_incoming_tx_detected_at ON incoming_transactions(detected_at)');
|
|
1604
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_incoming_tx_chain_network ON incoming_transactions(chain, network)');
|
|
1605
|
-
sqlite.exec("CREATE INDEX IF NOT EXISTS idx_incoming_tx_status ON incoming_transactions(status) WHERE status = 'DETECTED'");
|
|
1606
|
-
},
|
|
1607
|
-
});
|
|
1608
|
-
// ---------------------------------------------------------------------------
|
|
1609
|
-
// Migration v22: Add asset_id column to token_registry with CAIP-19 backfill
|
|
1610
|
-
// ---------------------------------------------------------------------------
|
|
1611
|
-
// Simple ALTER TABLE ADD COLUMN + application-level backfill.
|
|
1612
|
-
// Generates CAIP-19 asset_id for each known-network token using tokenAssetId().
|
|
1613
|
-
// Unknown networks are skipped (asset_id remains NULL).
|
|
1614
|
-
MIGRATIONS.push({
|
|
1615
|
-
version: 22,
|
|
1616
|
-
description: 'Add asset_id column to token_registry with CAIP-19 backfill',
|
|
1617
|
-
managesOwnTransaction: false,
|
|
1618
|
-
up: (sqlite) => {
|
|
1619
|
-
// Step 1: Add nullable column (skip if already exists from fresh DDL)
|
|
1620
|
-
const columns = sqlite
|
|
1621
|
-
.prepare("PRAGMA table_info('token_registry')")
|
|
1622
|
-
.all();
|
|
1623
|
-
const hasAssetId = columns.some((c) => c.name === 'asset_id');
|
|
1624
|
-
if (!hasAssetId) {
|
|
1625
|
-
sqlite.exec('ALTER TABLE token_registry ADD COLUMN asset_id TEXT');
|
|
1626
|
-
}
|
|
1627
|
-
// Step 2: Application-level backfill using tokenAssetId()
|
|
1628
|
-
const rows = sqlite
|
|
1629
|
-
.prepare('SELECT id, network, address FROM token_registry')
|
|
1630
|
-
.all();
|
|
1631
|
-
const updateStmt = sqlite.prepare('UPDATE token_registry SET asset_id = ? WHERE id = ?');
|
|
1632
|
-
for (const row of rows) {
|
|
1633
|
-
// Normalize legacy Solana network names (mainnet -> solana-mainnet, etc.)
|
|
1634
|
-
const normalizedNetwork = LEGACY_NETWORK_NORMALIZE[row.network] ?? row.network;
|
|
1635
|
-
// Guard: only backfill for known networks (Pitfall 4)
|
|
1636
|
-
if (!(normalizedNetwork in NETWORK_TO_CAIP2))
|
|
1637
|
-
continue;
|
|
1638
|
-
try {
|
|
1639
|
-
const assetId = tokenAssetId(normalizedNetwork, row.address);
|
|
1640
|
-
updateStmt.run(assetId, row.id);
|
|
1641
|
-
}
|
|
1642
|
-
catch {
|
|
1643
|
-
// Skip on error -- rows with unknown networks get asset_id = NULL
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
},
|
|
1647
|
-
});
|
|
1648
|
-
// ---------------------------------------------------------------------------
|
|
1649
|
-
// Migration v23: DeFi async tracking — bridge_status + bridge_metadata + GAS_WAITING
|
|
1650
|
-
// ---------------------------------------------------------------------------
|
|
1651
|
-
// 12-step table recreation required because:
|
|
1652
|
-
// 1. TRANSACTION_STATUSES now includes GAS_WAITING (11 entries), must update status CHECK
|
|
1653
|
-
// 2. New bridge_status column with 6-value CHECK constraint
|
|
1654
|
-
// 3. New bridge_metadata TEXT column
|
|
1655
|
-
// 4. 2 new partial indexes (idx_transactions_bridge_status, idx_transactions_gas_waiting)
|
|
1656
|
-
// @see internal/objectives/m28-00-defi-basic-protocol-design.md (DEFI-04 ASNC-01)
|
|
1657
|
-
MIGRATIONS.push({
|
|
1658
|
-
version: 23,
|
|
1659
|
-
description: 'DeFi async tracking: bridge_status + bridge_metadata + GAS_WAITING state',
|
|
1660
|
-
managesOwnTransaction: true,
|
|
1661
|
-
up: (sqlite) => {
|
|
1662
|
-
sqlite.exec('BEGIN');
|
|
1663
|
-
try {
|
|
1664
|
-
// Step 1: Create transactions_new with updated CHECK + new columns
|
|
1665
|
-
sqlite.exec(`CREATE TABLE transactions_new (
|
|
1666
|
-
id TEXT PRIMARY KEY,
|
|
1667
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
1668
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
1669
|
-
chain TEXT NOT NULL,
|
|
1670
|
-
tx_hash TEXT,
|
|
1671
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
1672
|
-
amount TEXT,
|
|
1673
|
-
to_address TEXT,
|
|
1674
|
-
token_mint TEXT,
|
|
1675
|
-
contract_address TEXT,
|
|
1676
|
-
method_signature TEXT,
|
|
1677
|
-
spender_address TEXT,
|
|
1678
|
-
approved_amount TEXT,
|
|
1679
|
-
parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
|
|
1680
|
-
batch_index INTEGER,
|
|
1681
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
1682
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
1683
|
-
queued_at INTEGER,
|
|
1684
|
-
executed_at INTEGER,
|
|
1685
|
-
created_at INTEGER NOT NULL,
|
|
1686
|
-
reserved_amount TEXT,
|
|
1687
|
-
amount_usd REAL,
|
|
1688
|
-
reserved_amount_usd REAL,
|
|
1689
|
-
error TEXT,
|
|
1690
|
-
metadata TEXT,
|
|
1691
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)})),
|
|
1692
|
-
bridge_status TEXT CHECK (bridge_status IS NULL OR bridge_status IN ('PENDING', 'COMPLETED', 'FAILED', 'BRIDGE_MONITORING', 'TIMEOUT', 'REFUNDED', 'PARTIALLY_FILLED', 'FILLED', 'CANCELED', 'SETTLED', 'EXPIRED')),
|
|
1693
|
-
bridge_metadata TEXT
|
|
1694
|
-
)`);
|
|
1695
|
-
// Step 2: Copy existing data (bridge_status and bridge_metadata default to NULL)
|
|
1696
|
-
sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, amount_usd, reserved_amount_usd, error, metadata, network)
|
|
1697
|
-
SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, amount_usd, reserved_amount_usd, error, metadata, network FROM transactions`);
|
|
1698
|
-
// Step 3: Drop old table
|
|
1699
|
-
sqlite.exec('DROP TABLE transactions');
|
|
1700
|
-
// Step 4: Rename new table
|
|
1701
|
-
sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
|
|
1702
|
-
// Step 5: Recreate all 8 existing indexes
|
|
1703
|
-
sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
|
|
1704
|
-
sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
|
|
1705
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
|
|
1706
|
-
sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
|
|
1707
|
-
sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
|
|
1708
|
-
sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
|
|
1709
|
-
sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
|
|
1710
|
-
sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
|
|
1711
|
-
// Step 6: Create 2 new partial indexes
|
|
1712
|
-
sqlite.exec('CREATE INDEX idx_transactions_bridge_status ON transactions(bridge_status) WHERE bridge_status IS NOT NULL');
|
|
1713
|
-
sqlite.exec("CREATE INDEX idx_transactions_gas_waiting ON transactions(status) WHERE status = 'GAS_WAITING'");
|
|
1714
|
-
// Step 7: Commit
|
|
1715
|
-
sqlite.exec('COMMIT');
|
|
1716
|
-
}
|
|
1717
|
-
catch (err) {
|
|
1718
|
-
sqlite.exec('ROLLBACK');
|
|
1719
|
-
throw err;
|
|
1720
|
-
}
|
|
1721
|
-
// Step 8: Re-enable foreign keys and verify integrity
|
|
1722
|
-
sqlite.pragma('foreign_keys = ON');
|
|
1723
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
1724
|
-
if (fkErrors.length > 0) {
|
|
1725
|
-
throw new Error(`FK integrity violation after v23: ${JSON.stringify(fkErrors)}`);
|
|
1726
|
-
}
|
|
1727
|
-
},
|
|
1728
|
-
});
|
|
1729
|
-
// ---------------------------------------------------------------------------
|
|
1730
|
-
// Migration v24: Add wallet_type column to wallets table for preset auto-setup
|
|
1731
|
-
// ---------------------------------------------------------------------------
|
|
1732
|
-
// Simple ALTER TABLE ADD COLUMN -- nullable TEXT, no CHECK constraint (validated at app level via Zod).
|
|
1733
|
-
// Supports wallet preset auto-setup (v28.8): stores the preset type identifier.
|
|
1734
|
-
MIGRATIONS.push({
|
|
1735
|
-
version: 24,
|
|
1736
|
-
description: 'Add wallet_type column to wallets table for preset auto-setup',
|
|
1737
|
-
up: (sqlite) => {
|
|
1738
|
-
sqlite.exec('ALTER TABLE wallets ADD COLUMN wallet_type TEXT');
|
|
1739
|
-
},
|
|
1740
|
-
});
|
|
1741
|
-
// ---------------------------------------------------------------------------
|
|
1742
|
-
// v29.2 Migration 25: Add defi_positions table for DeFi position tracking
|
|
1743
|
-
// ---------------------------------------------------------------------------
|
|
1744
|
-
MIGRATIONS.push({
|
|
1745
|
-
version: 25,
|
|
1746
|
-
description: 'Add defi_positions table for DeFi position tracking',
|
|
1747
|
-
up: (sqlite) => {
|
|
1748
|
-
sqlite.exec(`
|
|
1749
|
-
CREATE TABLE IF NOT EXISTS defi_positions (
|
|
1750
|
-
id TEXT PRIMARY KEY,
|
|
1751
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1752
|
-
category TEXT NOT NULL CHECK(category IN (${inList(POSITION_CATEGORIES)})),
|
|
1753
|
-
provider TEXT NOT NULL,
|
|
1754
|
-
chain TEXT NOT NULL CHECK(chain IN (${inList(CHAIN_TYPES)})),
|
|
1755
|
-
network TEXT CHECK(network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)})),
|
|
1756
|
-
asset_id TEXT,
|
|
1757
|
-
amount TEXT NOT NULL,
|
|
1758
|
-
amount_usd REAL,
|
|
1759
|
-
metadata TEXT,
|
|
1760
|
-
status TEXT NOT NULL DEFAULT 'ACTIVE' CHECK(status IN (${inList(POSITION_STATUSES)})),
|
|
1761
|
-
opened_at INTEGER NOT NULL,
|
|
1762
|
-
closed_at INTEGER,
|
|
1763
|
-
last_synced_at INTEGER NOT NULL,
|
|
1764
|
-
created_at INTEGER NOT NULL,
|
|
1765
|
-
updated_at INTEGER NOT NULL
|
|
1766
|
-
)
|
|
1767
|
-
`);
|
|
1768
|
-
// Indexes for the new table
|
|
1769
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_wallet_category ON defi_positions(wallet_id, category)');
|
|
1770
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_wallet_provider ON defi_positions(wallet_id, provider)');
|
|
1771
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_status ON defi_positions(status)');
|
|
1772
|
-
sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_defi_positions_unique ON defi_positions(wallet_id, provider, asset_id, category)');
|
|
1773
|
-
},
|
|
1774
|
-
});
|
|
1775
|
-
// ---------------------------------------------------------------------------
|
|
1776
|
-
// v26: Add LENDING_LTV_LIMIT and LENDING_ASSET_WHITELIST to policies table CHECK constraint
|
|
1777
|
-
// ---------------------------------------------------------------------------
|
|
1778
|
-
// POLICY_TYPES SSoT array now includes LENDING_LTV_LIMIT and LENDING_ASSET_WHITELIST (14 total).
|
|
1779
|
-
// SQLite cannot ALTER CHECK constraints, so we recreate the policies table (12-step pattern).
|
|
1780
|
-
MIGRATIONS.push({
|
|
1781
|
-
version: 26,
|
|
1782
|
-
description: 'Add lending policy types to policies table CHECK constraint',
|
|
1783
|
-
managesOwnTransaction: true,
|
|
1784
|
-
up: (sqlite) => {
|
|
1785
|
-
// Step 1: Begin transaction (foreign_keys already OFF via runner)
|
|
1786
|
-
sqlite.exec('BEGIN');
|
|
1787
|
-
try {
|
|
1788
|
-
// Step 2: Create policies_new with updated CHECK (uses SSoT POLICY_TYPES array)
|
|
1789
|
-
sqlite.exec(`CREATE TABLE policies_new (
|
|
1790
|
-
id TEXT PRIMARY KEY,
|
|
1791
|
-
wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1792
|
-
type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
|
|
1793
|
-
rules TEXT NOT NULL,
|
|
1794
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
1795
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
1796
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES_WITH_LEGACY)})),
|
|
1797
|
-
created_at INTEGER NOT NULL,
|
|
1798
|
-
updated_at INTEGER NOT NULL
|
|
1799
|
-
)`);
|
|
1800
|
-
// Step 3: Copy existing policies
|
|
1801
|
-
sqlite.exec('INSERT INTO policies_new SELECT * FROM policies');
|
|
1802
|
-
// Step 4: Drop old table
|
|
1803
|
-
sqlite.exec('DROP TABLE policies');
|
|
1804
|
-
// Step 5: Rename new table
|
|
1805
|
-
sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
|
|
1806
|
-
// Step 6: Recreate indexes
|
|
1807
|
-
sqlite.exec('CREATE INDEX idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
|
|
1808
|
-
sqlite.exec('CREATE INDEX idx_policies_type ON policies(type)');
|
|
1809
|
-
sqlite.exec('CREATE INDEX idx_policies_network ON policies(network)');
|
|
1810
|
-
// Step 7: Commit
|
|
1811
|
-
sqlite.exec('COMMIT');
|
|
1812
|
-
}
|
|
1813
|
-
catch (err) {
|
|
1814
|
-
sqlite.exec('ROLLBACK');
|
|
1815
|
-
throw err;
|
|
1816
|
-
}
|
|
1817
|
-
// Step 8: Re-enable foreign keys and verify integrity
|
|
1818
|
-
sqlite.pragma('foreign_keys = ON');
|
|
1819
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
1820
|
-
if (fkErrors.length > 0) {
|
|
1821
|
-
throw new Error(`FK integrity violation after v26: ${JSON.stringify(fkErrors)}`);
|
|
1822
|
-
}
|
|
1823
|
-
},
|
|
1824
|
-
});
|
|
1825
|
-
// ---------------------------------------------------------------------------
|
|
1826
|
-
// Migration v27: Remove is_default from session_wallets, default_network from wallets
|
|
1827
|
-
// v29.3: Default wallet/default network concept removed
|
|
1828
|
-
// ---------------------------------------------------------------------------
|
|
1829
|
-
MIGRATIONS.push({
|
|
1830
|
-
version: 27,
|
|
1831
|
-
description: 'Remove is_default from session_wallets and default_network from wallets (v29.3)',
|
|
1832
|
-
managesOwnTransaction: true,
|
|
1833
|
-
up: (sqlite) => {
|
|
1834
|
-
sqlite.exec('BEGIN');
|
|
1835
|
-
try {
|
|
1836
|
-
// ── Part 1: Remove is_default from session_wallets ──
|
|
1837
|
-
// Step 2: Create session_wallets_new without is_default
|
|
1838
|
-
sqlite.exec(`CREATE TABLE session_wallets_new (
|
|
1839
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
1840
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
1841
|
-
created_at INTEGER NOT NULL,
|
|
1842
|
-
PRIMARY KEY (session_id, wallet_id)
|
|
1843
|
-
)`);
|
|
1844
|
-
// Step 3: Copy data (is_default column discarded, no data loss)
|
|
1845
|
-
sqlite.exec('INSERT INTO session_wallets_new (session_id, wallet_id, created_at) SELECT session_id, wallet_id, created_at FROM session_wallets');
|
|
1846
|
-
// Step 4: Drop old table
|
|
1847
|
-
sqlite.exec('DROP TABLE session_wallets');
|
|
1848
|
-
// Step 5: Rename
|
|
1849
|
-
sqlite.exec('ALTER TABLE session_wallets_new RENAME TO session_wallets');
|
|
1850
|
-
// Step 6: Recreate indexes
|
|
1851
|
-
sqlite.exec('CREATE INDEX idx_session_wallets_session ON session_wallets(session_id)');
|
|
1852
|
-
sqlite.exec('CREATE INDEX idx_session_wallets_wallet ON session_wallets(wallet_id)');
|
|
1853
|
-
// ── Part 2: Remove default_network from wallets ──
|
|
1854
|
-
// Step 7: Create wallets_new without default_network
|
|
1855
|
-
sqlite.exec(`CREATE TABLE wallets_new (
|
|
1856
|
-
id TEXT PRIMARY KEY,
|
|
1857
|
-
name TEXT NOT NULL,
|
|
1858
|
-
chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
|
|
1859
|
-
environment TEXT NOT NULL CHECK (environment IN (${inList(ENVIRONMENT_TYPES)})),
|
|
1860
|
-
public_key TEXT NOT NULL,
|
|
1861
|
-
status TEXT NOT NULL DEFAULT 'CREATING' CHECK (status IN (${inList(WALLET_STATUSES)})),
|
|
1862
|
-
owner_address TEXT,
|
|
1863
|
-
owner_verified INTEGER NOT NULL DEFAULT 0 CHECK (owner_verified IN (0, 1)),
|
|
1864
|
-
created_at INTEGER NOT NULL,
|
|
1865
|
-
updated_at INTEGER NOT NULL,
|
|
1866
|
-
suspended_at INTEGER,
|
|
1867
|
-
suspension_reason TEXT,
|
|
1868
|
-
monitor_incoming INTEGER NOT NULL DEFAULT 0,
|
|
1869
|
-
owner_approval_method TEXT CHECK (owner_approval_method IS NULL OR owner_approval_method IN ('sdk_ntfy', 'sdk_telegram', 'walletconnect', 'telegram_bot', 'rest')),
|
|
1870
|
-
wallet_type TEXT
|
|
1871
|
-
)`);
|
|
1872
|
-
// Step 8: Copy data (default_network intentionally excluded)
|
|
1873
|
-
sqlite.exec('INSERT INTO wallets_new (id, name, chain, environment, public_key, status, owner_address, owner_verified, created_at, updated_at, suspended_at, suspension_reason, monitor_incoming, owner_approval_method, wallet_type) SELECT id, name, chain, environment, public_key, status, owner_address, owner_verified, created_at, updated_at, suspended_at, suspension_reason, monitor_incoming, owner_approval_method, wallet_type FROM wallets');
|
|
1874
|
-
// Step 9: Drop old table
|
|
1875
|
-
sqlite.exec('DROP TABLE wallets');
|
|
1876
|
-
// Step 10: Rename
|
|
1877
|
-
sqlite.exec('ALTER TABLE wallets_new RENAME TO wallets');
|
|
1878
|
-
// Step 11: Recreate indexes
|
|
1879
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_wallets_public_key ON wallets(public_key)');
|
|
1880
|
-
sqlite.exec('CREATE INDEX idx_wallets_status ON wallets(status)');
|
|
1881
|
-
sqlite.exec('CREATE INDEX idx_wallets_chain_environment ON wallets(chain, environment)');
|
|
1882
|
-
sqlite.exec('CREATE INDEX idx_wallets_owner_address ON wallets(owner_address)');
|
|
1883
|
-
// Step 12: Commit
|
|
1884
|
-
sqlite.exec('COMMIT');
|
|
1885
|
-
}
|
|
1886
|
-
catch (err) {
|
|
1887
|
-
sqlite.exec('ROLLBACK');
|
|
1888
|
-
throw err;
|
|
1889
|
-
}
|
|
1890
|
-
// Re-enable foreign keys and verify integrity
|
|
1891
|
-
sqlite.pragma('foreign_keys = ON');
|
|
1892
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
1893
|
-
if (fkErrors.length > 0) {
|
|
1894
|
-
throw new Error(`FK integrity violation after v27: ${JSON.stringify(fkErrors)}`);
|
|
1895
|
-
}
|
|
1896
|
-
},
|
|
1897
|
-
});
|
|
1898
|
-
// ---------------------------------------------------------------------------
|
|
1899
|
-
// v28: Migrate api_keys to settings table and drop api_keys (v29.5 #214)
|
|
1900
|
-
// ---------------------------------------------------------------------------
|
|
1901
|
-
MIGRATIONS.push({
|
|
1902
|
-
version: 28,
|
|
1903
|
-
description: 'Migrate api_keys to settings table and drop api_keys (v29.5 #214)',
|
|
1904
|
-
managesOwnTransaction: true,
|
|
1905
|
-
up: (sqlite) => {
|
|
1906
|
-
sqlite.exec('BEGIN');
|
|
1907
|
-
try {
|
|
1908
|
-
// 1. Check if api_keys table exists (may not exist on very old DBs that skipped v11)
|
|
1909
|
-
const tableExists = sqlite
|
|
1910
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='api_keys'")
|
|
1911
|
-
.get();
|
|
1912
|
-
if (tableExists) {
|
|
1913
|
-
// 2. Read all api_keys rows
|
|
1914
|
-
const rows = sqlite
|
|
1915
|
-
.prepare('SELECT provider_name, encrypted_key, updated_at FROM api_keys')
|
|
1916
|
-
.all();
|
|
1917
|
-
// 3. For each row, insert into settings with key = 'actions.{provider_name}_api_key'
|
|
1918
|
-
// Only insert if the setting doesn't already exist (avoid overwriting manual settings)
|
|
1919
|
-
// encrypted_key values use the SAME encryption format as settings.value
|
|
1920
|
-
// (both use encryptSettingValue from settings-crypto.ts), so copy directly.
|
|
1921
|
-
const insertStmt = sqlite.prepare(`INSERT OR IGNORE INTO settings (key, value, encrypted, category, updated_at)
|
|
1922
|
-
VALUES (?, ?, 1, 'actions', ?)`);
|
|
1923
|
-
for (const row of rows) {
|
|
1924
|
-
const settingKey = `actions.${row.provider_name}_api_key`;
|
|
1925
|
-
insertStmt.run(settingKey, row.encrypted_key, row.updated_at);
|
|
1926
|
-
}
|
|
1927
|
-
// 4. Drop api_keys table
|
|
1928
|
-
sqlite.exec('DROP TABLE IF EXISTS api_keys');
|
|
1929
|
-
}
|
|
1930
|
-
sqlite.exec('COMMIT');
|
|
1931
|
-
}
|
|
1932
|
-
catch (err) {
|
|
1933
|
-
sqlite.exec('ROLLBACK');
|
|
1934
|
-
throw err;
|
|
1935
|
-
}
|
|
1936
|
-
},
|
|
1937
|
-
});
|
|
1938
|
-
// ---------------------------------------------------------------------------
|
|
1939
|
-
// v29: Rename Solana network IDs to solana-{network} format (v29.5 #211)
|
|
1940
|
-
// ---------------------------------------------------------------------------
|
|
1941
|
-
// Converts mainnet->solana-mainnet, devnet->solana-devnet, testnet->solana-testnet
|
|
1942
|
-
// in all tables with Solana network references.
|
|
1943
|
-
// Tables with CHECK constraints (transactions, policies, defi_positions) require
|
|
1944
|
-
// 12-step table recreation. Tables without CHECK (incoming_transactions,
|
|
1945
|
-
// incoming_tx_cursors, token_registry) use simple UPDATE.
|
|
1946
|
-
MIGRATIONS.push({
|
|
1947
|
-
version: 29,
|
|
1948
|
-
description: 'Rename Solana network IDs to solana-{network} format (v29.5 #211)',
|
|
1949
|
-
managesOwnTransaction: true,
|
|
1950
|
-
up: (sqlite) => {
|
|
1951
|
-
sqlite.exec('BEGIN');
|
|
1952
|
-
try {
|
|
1953
|
-
// Helper: Solana network CASE WHEN clause for SELECT
|
|
1954
|
-
const solanaCase = (col) => `CASE WHEN chain = 'solana' AND ${col} = 'mainnet' THEN 'solana-mainnet'` +
|
|
1955
|
-
` WHEN chain = 'solana' AND ${col} = 'devnet' THEN 'solana-devnet'` +
|
|
1956
|
-
` WHEN chain = 'solana' AND ${col} = 'testnet' THEN 'solana-testnet'` +
|
|
1957
|
-
` ELSE ${col} END`;
|
|
1958
|
-
// ── 1. transactions: 12-step recreation (has CHECK on network) ──
|
|
1959
|
-
sqlite.exec(`CREATE TABLE transactions_new (
|
|
1960
|
-
id TEXT PRIMARY KEY,
|
|
1961
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
1962
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
1963
|
-
chain TEXT NOT NULL,
|
|
1964
|
-
tx_hash TEXT,
|
|
1965
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
1966
|
-
amount TEXT,
|
|
1967
|
-
to_address TEXT,
|
|
1968
|
-
token_mint TEXT,
|
|
1969
|
-
contract_address TEXT,
|
|
1970
|
-
method_signature TEXT,
|
|
1971
|
-
spender_address TEXT,
|
|
1972
|
-
approved_amount TEXT,
|
|
1973
|
-
parent_id TEXT REFERENCES transactions(id) ON DELETE CASCADE,
|
|
1974
|
-
batch_index INTEGER,
|
|
1975
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
1976
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
1977
|
-
queued_at INTEGER,
|
|
1978
|
-
executed_at INTEGER,
|
|
1979
|
-
created_at INTEGER NOT NULL,
|
|
1980
|
-
reserved_amount TEXT,
|
|
1981
|
-
amount_usd REAL,
|
|
1982
|
-
reserved_amount_usd REAL,
|
|
1983
|
-
error TEXT,
|
|
1984
|
-
metadata TEXT,
|
|
1985
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
1986
|
-
bridge_status TEXT CHECK (bridge_status IS NULL OR bridge_status IN ('PENDING', 'COMPLETED', 'FAILED', 'BRIDGE_MONITORING', 'TIMEOUT', 'REFUNDED', 'PARTIALLY_FILLED', 'FILLED', 'CANCELED', 'SETTLED', 'EXPIRED')),
|
|
1987
|
-
bridge_metadata TEXT
|
|
1988
|
-
)`);
|
|
1989
|
-
sqlite.exec(`INSERT INTO transactions_new
|
|
1990
|
-
SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address,
|
|
1991
|
-
token_mint, contract_address, method_signature, spender_address,
|
|
1992
|
-
approved_amount, parent_id, batch_index, status, tier, queued_at,
|
|
1993
|
-
executed_at, created_at, reserved_amount, amount_usd, reserved_amount_usd,
|
|
1994
|
-
error, metadata, ${solanaCase('network')}, bridge_status, bridge_metadata
|
|
1995
|
-
FROM transactions`);
|
|
1996
|
-
sqlite.exec('DROP TABLE transactions');
|
|
1997
|
-
sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
|
|
1998
|
-
// Recreate transactions indexes
|
|
1999
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_wallet_status ON transactions(wallet_id, status)');
|
|
2000
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_session_id ON transactions(session_id)');
|
|
2001
|
-
sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_transactions_tx_hash ON transactions(tx_hash)');
|
|
2002
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_queued_at ON transactions(queued_at)');
|
|
2003
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_created_at ON transactions(created_at)');
|
|
2004
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_type ON transactions(type)');
|
|
2005
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_contract_address ON transactions(contract_address)');
|
|
2006
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_parent_id ON transactions(parent_id)');
|
|
2007
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_bridge_status ON transactions(bridge_status) WHERE bridge_status IS NOT NULL');
|
|
2008
|
-
sqlite.exec("CREATE INDEX IF NOT EXISTS idx_transactions_gas_waiting ON transactions(status) WHERE status = 'GAS_WAITING'");
|
|
2009
|
-
// ── 2. policies: 12-step recreation (has CHECK on network) ──
|
|
2010
|
-
sqlite.exec(`CREATE TABLE policies_new (
|
|
2011
|
-
id TEXT PRIMARY KEY,
|
|
2012
|
-
wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
|
|
2013
|
-
type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
|
|
2014
|
-
rules TEXT NOT NULL,
|
|
2015
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
2016
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
2017
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
2018
|
-
created_at INTEGER NOT NULL,
|
|
2019
|
-
updated_at INTEGER NOT NULL
|
|
2020
|
-
)`);
|
|
2021
|
-
// policies doesn't have a chain column, so we check if network is one of the bare Solana names
|
|
2022
|
-
sqlite.exec(`INSERT INTO policies_new
|
|
2023
|
-
SELECT id, wallet_id, type, rules, priority, enabled,
|
|
2024
|
-
CASE WHEN network = 'mainnet' THEN 'solana-mainnet'
|
|
2025
|
-
WHEN network = 'devnet' THEN 'solana-devnet'
|
|
2026
|
-
WHEN network = 'testnet' THEN 'solana-testnet'
|
|
2027
|
-
ELSE network END,
|
|
2028
|
-
created_at, updated_at
|
|
2029
|
-
FROM policies`);
|
|
2030
|
-
sqlite.exec('DROP TABLE policies');
|
|
2031
|
-
sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
|
|
2032
|
-
// Recreate policies indexes
|
|
2033
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
|
|
2034
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_policies_type ON policies(type)');
|
|
2035
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_policies_network ON policies(network)');
|
|
2036
|
-
// ── 3. defi_positions: 12-step recreation (has CHECK on network) ──
|
|
2037
|
-
sqlite.exec(`CREATE TABLE defi_positions_new (
|
|
2038
|
-
id TEXT PRIMARY KEY,
|
|
2039
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
2040
|
-
category TEXT NOT NULL CHECK(category IN (${inList(POSITION_CATEGORIES)})),
|
|
2041
|
-
provider TEXT NOT NULL,
|
|
2042
|
-
chain TEXT NOT NULL CHECK(chain IN (${inList(CHAIN_TYPES)})),
|
|
2043
|
-
network TEXT CHECK(network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
2044
|
-
asset_id TEXT,
|
|
2045
|
-
amount TEXT NOT NULL,
|
|
2046
|
-
amount_usd REAL,
|
|
2047
|
-
metadata TEXT,
|
|
2048
|
-
status TEXT NOT NULL DEFAULT 'ACTIVE' CHECK(status IN (${inList(POSITION_STATUSES)})),
|
|
2049
|
-
opened_at INTEGER NOT NULL,
|
|
2050
|
-
closed_at INTEGER,
|
|
2051
|
-
last_synced_at INTEGER NOT NULL,
|
|
2052
|
-
created_at INTEGER NOT NULL,
|
|
2053
|
-
updated_at INTEGER NOT NULL
|
|
2054
|
-
)`);
|
|
2055
|
-
sqlite.exec(`INSERT INTO defi_positions_new
|
|
2056
|
-
SELECT id, wallet_id, category, provider, chain, ${solanaCase('network')},
|
|
2057
|
-
asset_id, amount, amount_usd, metadata, status, opened_at, closed_at,
|
|
2058
|
-
last_synced_at, created_at, updated_at
|
|
2059
|
-
FROM defi_positions`);
|
|
2060
|
-
sqlite.exec('DROP TABLE defi_positions');
|
|
2061
|
-
sqlite.exec('ALTER TABLE defi_positions_new RENAME TO defi_positions');
|
|
2062
|
-
// Recreate defi_positions indexes
|
|
2063
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_wallet_category ON defi_positions(wallet_id, category)');
|
|
2064
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_wallet_provider ON defi_positions(wallet_id, provider)');
|
|
2065
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_status ON defi_positions(status)');
|
|
2066
|
-
sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_defi_positions_unique ON defi_positions(wallet_id, provider, asset_id, category)');
|
|
2067
|
-
// ── 4. incoming_transactions: UPDATE only (no CHECK on network) ──
|
|
2068
|
-
sqlite.exec(`UPDATE incoming_transactions SET network = 'solana-mainnet' WHERE chain = 'solana' AND network = 'mainnet'`);
|
|
2069
|
-
sqlite.exec(`UPDATE incoming_transactions SET network = 'solana-devnet' WHERE chain = 'solana' AND network = 'devnet'`);
|
|
2070
|
-
sqlite.exec(`UPDATE incoming_transactions SET network = 'solana-testnet' WHERE chain = 'solana' AND network = 'testnet'`);
|
|
2071
|
-
// ── 5. incoming_tx_cursors: UPDATE only (no CHECK on network) ──
|
|
2072
|
-
sqlite.exec(`UPDATE incoming_tx_cursors SET network = 'solana-mainnet' WHERE chain = 'solana' AND network = 'mainnet'`);
|
|
2073
|
-
sqlite.exec(`UPDATE incoming_tx_cursors SET network = 'solana-devnet' WHERE chain = 'solana' AND network = 'devnet'`);
|
|
2074
|
-
sqlite.exec(`UPDATE incoming_tx_cursors SET network = 'solana-testnet' WHERE chain = 'solana' AND network = 'testnet'`);
|
|
2075
|
-
// ── 6. token_registry: UPDATE only (no CHECK on network, no chain column) ──
|
|
2076
|
-
sqlite.exec(`UPDATE token_registry SET network = 'solana-mainnet' WHERE network = 'mainnet'`);
|
|
2077
|
-
sqlite.exec(`UPDATE token_registry SET network = 'solana-devnet' WHERE network = 'devnet'`);
|
|
2078
|
-
sqlite.exec(`UPDATE token_registry SET network = 'solana-testnet' WHERE network = 'testnet'`);
|
|
2079
|
-
sqlite.exec('COMMIT');
|
|
2080
|
-
}
|
|
2081
|
-
catch (err) {
|
|
2082
|
-
sqlite.exec('ROLLBACK');
|
|
2083
|
-
throw err;
|
|
2084
|
-
}
|
|
2085
|
-
// Re-enable foreign keys and verify integrity
|
|
2086
|
-
sqlite.pragma('foreign_keys = ON');
|
|
2087
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
2088
|
-
if (fkErrors.length > 0) {
|
|
2089
|
-
throw new Error(`FK integrity check failed after v29 migration: ${JSON.stringify(fkErrors)}`);
|
|
2090
|
-
}
|
|
2091
|
-
},
|
|
2092
|
-
});
|
|
2093
|
-
// ---------------------------------------------------------------------------
|
|
2094
|
-
// Migration v30: Add MATURED status to defi_positions CHECK constraint (v29.6)
|
|
2095
|
-
// ---------------------------------------------------------------------------
|
|
2096
|
-
MIGRATIONS.push({
|
|
2097
|
-
version: 30,
|
|
2098
|
-
description: 'Add MATURED position status to defi_positions CHECK constraint (v29.6 Yield)',
|
|
2099
|
-
managesOwnTransaction: true,
|
|
2100
|
-
up: (sqlite) => {
|
|
2101
|
-
sqlite.exec('BEGIN');
|
|
2102
|
-
try {
|
|
2103
|
-
// 12-step table recreation for defi_positions (status CHECK constraint update)
|
|
2104
|
-
sqlite.exec(`CREATE TABLE defi_positions_new (
|
|
2105
|
-
id TEXT PRIMARY KEY,
|
|
2106
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
2107
|
-
category TEXT NOT NULL CHECK(category IN (${inList(POSITION_CATEGORIES)})),
|
|
2108
|
-
provider TEXT NOT NULL,
|
|
2109
|
-
chain TEXT NOT NULL CHECK(chain IN (${inList(CHAIN_TYPES)})),
|
|
2110
|
-
network TEXT CHECK(network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
2111
|
-
asset_id TEXT,
|
|
2112
|
-
amount TEXT NOT NULL,
|
|
2113
|
-
amount_usd REAL,
|
|
2114
|
-
metadata TEXT,
|
|
2115
|
-
status TEXT NOT NULL DEFAULT 'ACTIVE' CHECK(status IN (${inList(POSITION_STATUSES)})),
|
|
2116
|
-
opened_at INTEGER NOT NULL,
|
|
2117
|
-
closed_at INTEGER,
|
|
2118
|
-
last_synced_at INTEGER NOT NULL,
|
|
2119
|
-
created_at INTEGER NOT NULL,
|
|
2120
|
-
updated_at INTEGER NOT NULL
|
|
2121
|
-
)`);
|
|
2122
|
-
sqlite.exec(`INSERT INTO defi_positions_new
|
|
2123
|
-
SELECT id, wallet_id, category, provider, chain, network,
|
|
2124
|
-
asset_id, amount, amount_usd, metadata, status, opened_at, closed_at,
|
|
2125
|
-
last_synced_at, created_at, updated_at
|
|
2126
|
-
FROM defi_positions`);
|
|
2127
|
-
sqlite.exec('DROP TABLE defi_positions');
|
|
2128
|
-
sqlite.exec('ALTER TABLE defi_positions_new RENAME TO defi_positions');
|
|
2129
|
-
// Recreate indexes
|
|
2130
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_wallet_category ON defi_positions(wallet_id, category)');
|
|
2131
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_wallet_provider ON defi_positions(wallet_id, provider)');
|
|
2132
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_defi_positions_status ON defi_positions(status)');
|
|
2133
|
-
sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_defi_positions_unique ON defi_positions(wallet_id, provider, asset_id, category)');
|
|
2134
|
-
sqlite.exec('COMMIT');
|
|
2135
|
-
}
|
|
2136
|
-
catch (err) {
|
|
2137
|
-
sqlite.exec('ROLLBACK');
|
|
2138
|
-
throw err;
|
|
2139
|
-
}
|
|
2140
|
-
// Re-enable foreign keys and verify integrity
|
|
2141
|
-
sqlite.pragma('foreign_keys = ON');
|
|
2142
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
2143
|
-
if (fkErrors.length > 0) {
|
|
2144
|
-
throw new Error(`FK integrity check failed after v30 migration: ${JSON.stringify(fkErrors)}`);
|
|
2145
|
-
}
|
|
2146
|
-
},
|
|
2147
|
-
});
|
|
2148
|
-
// ---------------------------------------------------------------------------
|
|
2149
|
-
// Migration v31: Create wallet_apps table for Human Wallet Apps registry (v29.7)
|
|
2150
|
-
// ---------------------------------------------------------------------------
|
|
2151
|
-
MIGRATIONS.push({
|
|
2152
|
-
version: 31,
|
|
2153
|
-
description: 'Create wallet_apps table for Human Wallet Apps registry',
|
|
2154
|
-
up: (sqlite) => {
|
|
2155
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS wallet_apps (
|
|
2156
|
-
id TEXT PRIMARY KEY,
|
|
2157
|
-
name TEXT NOT NULL UNIQUE,
|
|
2158
|
-
display_name TEXT NOT NULL,
|
|
2159
|
-
signing_enabled INTEGER NOT NULL DEFAULT 1,
|
|
2160
|
-
alerts_enabled INTEGER NOT NULL DEFAULT 1,
|
|
2161
|
-
created_at INTEGER NOT NULL,
|
|
2162
|
-
updated_at INTEGER NOT NULL
|
|
2163
|
-
)`);
|
|
2164
|
-
},
|
|
2165
|
-
});
|
|
2166
|
-
// v32: Change sessions DDL default max_renewals from 30 to 0 (unlimited).
|
|
2167
|
-
// This is a no-op migration: existing rows keep their values, and the Drizzle
|
|
2168
|
-
// schema (.default(0)) already handles the app-level default for new inserts.
|
|
2169
|
-
// The DDL default change only affects fresh databases via getCreateTableStatements().
|
|
2170
|
-
MIGRATIONS.push({
|
|
2171
|
-
version: 32,
|
|
2172
|
-
description: 'Session progressive security: default max_renewals 30 -> 0 (unlimited)',
|
|
2173
|
-
up: (_sqlite) => {
|
|
2174
|
-
// No DDL needed -- SQLite cannot ALTER TABLE ... ALTER COLUMN DEFAULT.
|
|
2175
|
-
// Fresh databases use getCreateTableStatements() which already has DEFAULT 0.
|
|
2176
|
-
// Existing sessions retain their max_renewals values unchanged.
|
|
2177
|
-
},
|
|
2178
|
-
});
|
|
2179
|
-
// ---------------------------------------------------------------------------
|
|
2180
|
-
// Migration v33: Add sign_topic and notify_topic to wallet_apps for per-wallet ntfy topic routing (v29.10)
|
|
2181
|
-
// ---------------------------------------------------------------------------
|
|
2182
|
-
MIGRATIONS.push({
|
|
2183
|
-
version: 33,
|
|
2184
|
-
description: 'Add sign_topic and notify_topic columns to wallet_apps for per-wallet ntfy topic routing',
|
|
2185
|
-
up: (sqlite) => {
|
|
2186
|
-
const cols = sqlite.prepare("PRAGMA table_info('wallet_apps')").all().map(c => c.name);
|
|
2187
|
-
if (!cols.includes('sign_topic'))
|
|
2188
|
-
sqlite.exec(`ALTER TABLE wallet_apps ADD COLUMN sign_topic TEXT`);
|
|
2189
|
-
if (!cols.includes('notify_topic'))
|
|
2190
|
-
sqlite.exec(`ALTER TABLE wallet_apps ADD COLUMN notify_topic TEXT`);
|
|
2191
|
-
// Backfill existing rows with prefix+appName defaults
|
|
2192
|
-
const prefix = 'waiaas-sign';
|
|
2193
|
-
const notifyPrefix = 'waiaas-notify';
|
|
2194
|
-
const rows = sqlite.prepare('SELECT id, name FROM wallet_apps').all();
|
|
2195
|
-
const stmt = sqlite.prepare('UPDATE wallet_apps SET sign_topic = ?, notify_topic = ? WHERE id = ?');
|
|
2196
|
-
for (const row of rows) {
|
|
2197
|
-
stmt.run(`${prefix}-${row.name}`, `${notifyPrefix}-${row.name}`, row.id);
|
|
2198
|
-
}
|
|
2199
|
-
},
|
|
2200
|
-
});
|
|
2201
|
-
// ---------------------------------------------------------------------------
|
|
2202
|
-
// v34: Add wallet_type column to wallet_apps (multi-device per wallet type)
|
|
2203
|
-
// ---------------------------------------------------------------------------
|
|
2204
|
-
MIGRATIONS.push({
|
|
2205
|
-
version: 34,
|
|
2206
|
-
description: 'Add wallet_type column to wallet_apps for multi-device per wallet type',
|
|
2207
|
-
up: (sqlite) => {
|
|
2208
|
-
const cols = sqlite.prepare("PRAGMA table_info('wallet_apps')").all().map(c => c.name);
|
|
2209
|
-
if (!cols.includes('wallet_type')) {
|
|
2210
|
-
sqlite.exec(`ALTER TABLE wallet_apps ADD COLUMN wallet_type TEXT NOT NULL DEFAULT ''`);
|
|
2211
|
-
// Backfill: set wallet_type = name for existing rows
|
|
2212
|
-
sqlite.exec(`UPDATE wallet_apps SET wallet_type = name WHERE wallet_type = ''`);
|
|
2213
|
-
}
|
|
2214
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_wallet_apps_wallet_type ON wallet_apps(wallet_type)');
|
|
2215
|
-
},
|
|
2216
|
-
});
|
|
2217
|
-
// ---------------------------------------------------------------------------
|
|
2218
|
-
// v35: Add subscription_token column to wallet_apps (token-based ntfy topic routing)
|
|
2219
|
-
// ---------------------------------------------------------------------------
|
|
2220
|
-
MIGRATIONS.push({
|
|
2221
|
-
version: 35,
|
|
2222
|
-
description: 'Add subscription_token column to wallet_apps for token-based ntfy topic routing',
|
|
2223
|
-
up: (sqlite) => {
|
|
2224
|
-
const cols = sqlite.prepare("PRAGMA table_info('wallet_apps')").all().map(c => c.name);
|
|
2225
|
-
if (!cols.includes('subscription_token')) {
|
|
2226
|
-
sqlite.exec(`ALTER TABLE wallet_apps ADD COLUMN subscription_token TEXT`);
|
|
2227
|
-
}
|
|
2228
|
-
},
|
|
2229
|
-
});
|
|
2230
|
-
// ---------------------------------------------------------------------------
|
|
2231
|
-
// v36: Add idx_audit_log_tx_id index for audit log tx_id filter queries (OPS-02)
|
|
2232
|
-
// ---------------------------------------------------------------------------
|
|
2233
|
-
MIGRATIONS.push({
|
|
2234
|
-
version: 36,
|
|
2235
|
-
description: 'Add idx_audit_log_tx_id index for audit log tx_id filter queries',
|
|
2236
|
-
up: (sqlite) => {
|
|
2237
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_audit_log_tx_id ON audit_log(tx_id)');
|
|
2238
|
-
},
|
|
2239
|
-
});
|
|
2240
|
-
// ---------------------------------------------------------------------------
|
|
2241
|
-
// v37: Create webhooks + webhook_logs tables for webhook outbound (OPS-04)
|
|
2242
|
-
// ---------------------------------------------------------------------------
|
|
2243
|
-
MIGRATIONS.push({
|
|
2244
|
-
version: 37,
|
|
2245
|
-
description: 'Create webhooks and webhook_logs tables for webhook outbound (OPS-04)',
|
|
2246
|
-
up: (sqlite) => {
|
|
2247
|
-
// Enable foreign keys for CASCADE support
|
|
2248
|
-
sqlite.exec('PRAGMA foreign_keys = ON');
|
|
2249
|
-
// Table 20: webhooks -- webhook subscription registry
|
|
2250
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS webhooks (
|
|
2251
|
-
id TEXT PRIMARY KEY,
|
|
2252
|
-
url TEXT NOT NULL,
|
|
2253
|
-
secret_hash TEXT NOT NULL,
|
|
2254
|
-
secret_encrypted TEXT NOT NULL,
|
|
2255
|
-
events TEXT NOT NULL DEFAULT '[]',
|
|
2256
|
-
description TEXT,
|
|
2257
|
-
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
|
|
2258
|
-
created_at INTEGER NOT NULL,
|
|
2259
|
-
updated_at INTEGER NOT NULL
|
|
2260
|
-
)`);
|
|
2261
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_webhooks_enabled ON webhooks(enabled)');
|
|
2262
|
-
// Table 21: webhook_logs -- webhook delivery attempt history
|
|
2263
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS webhook_logs (
|
|
2264
|
-
id TEXT PRIMARY KEY,
|
|
2265
|
-
webhook_id TEXT NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
|
|
2266
|
-
event_type TEXT NOT NULL,
|
|
2267
|
-
status TEXT NOT NULL CHECK (status IN ('success', 'failed')),
|
|
2268
|
-
http_status INTEGER,
|
|
2269
|
-
attempt INTEGER NOT NULL DEFAULT 1,
|
|
2270
|
-
error TEXT,
|
|
2271
|
-
request_duration INTEGER,
|
|
2272
|
-
created_at INTEGER NOT NULL
|
|
2273
|
-
)`);
|
|
2274
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_webhook_logs_webhook_id ON webhook_logs(webhook_id)');
|
|
2275
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_webhook_logs_event_type ON webhook_logs(event_type)');
|
|
2276
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_webhook_logs_status ON webhook_logs(status)');
|
|
2277
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_webhook_logs_created_at ON webhook_logs(created_at)');
|
|
2278
|
-
},
|
|
2279
|
-
});
|
|
2280
|
-
// ---------------------------------------------------------------------------
|
|
2281
|
-
// v38: Add smart account columns to wallets table (ERC-4337 Account Abstraction)
|
|
2282
|
-
// ---------------------------------------------------------------------------
|
|
2283
|
-
MIGRATIONS.push({
|
|
2284
|
-
version: 38,
|
|
2285
|
-
description: 'Add smart account columns to wallets table (account_type, signer_key, deployed, entry_point)',
|
|
2286
|
-
up: (sqlite) => {
|
|
2287
|
-
sqlite.exec(`ALTER TABLE wallets ADD COLUMN account_type TEXT NOT NULL DEFAULT 'eoa'`);
|
|
2288
|
-
sqlite.exec(`ALTER TABLE wallets ADD COLUMN signer_key TEXT`);
|
|
2289
|
-
sqlite.exec(`ALTER TABLE wallets ADD COLUMN deployed INTEGER NOT NULL DEFAULT 1`);
|
|
2290
|
-
sqlite.exec(`ALTER TABLE wallets ADD COLUMN entry_point TEXT`);
|
|
2291
|
-
},
|
|
2292
|
-
});
|
|
2293
|
-
// v39: ERC-8004 Trustless Agents Foundation
|
|
2294
|
-
// Creates agent_identities + reputation_cache tables, adds pending_approvals.approval_type,
|
|
2295
|
-
// recreates policies table with REPUTATION_THRESHOLD in CHECK constraint.
|
|
2296
|
-
MIGRATIONS.push({
|
|
2297
|
-
version: 39,
|
|
2298
|
-
description: 'ERC-8004: agent_identities + reputation_cache + pending_approvals.approval_type + policies CHECK update',
|
|
2299
|
-
managesOwnTransaction: true,
|
|
2300
|
-
up: (sqlite) => {
|
|
2301
|
-
sqlite.exec('BEGIN');
|
|
2302
|
-
// Step 1: Create agent_identities table
|
|
2303
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS agent_identities (
|
|
2304
|
-
id TEXT PRIMARY KEY,
|
|
2305
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE CASCADE,
|
|
2306
|
-
chain_agent_id TEXT NOT NULL,
|
|
2307
|
-
registry_address TEXT NOT NULL,
|
|
2308
|
-
chain_id INTEGER NOT NULL,
|
|
2309
|
-
agent_uri TEXT,
|
|
2310
|
-
registration_file_url TEXT,
|
|
2311
|
-
status TEXT NOT NULL DEFAULT 'PENDING'
|
|
2312
|
-
CHECK (status IN ('PENDING', 'REGISTERED', 'WALLET_LINKED', 'DEREGISTERED')),
|
|
2313
|
-
created_at INTEGER NOT NULL,
|
|
2314
|
-
updated_at INTEGER NOT NULL
|
|
2315
|
-
)`);
|
|
2316
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_agent_identities_wallet ON agent_identities(wallet_id)');
|
|
2317
|
-
sqlite.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_identities_chain ON agent_identities(registry_address, chain_agent_id)');
|
|
2318
|
-
// Step 2: Create reputation_cache table
|
|
2319
|
-
sqlite.exec(`CREATE TABLE IF NOT EXISTS reputation_cache (
|
|
2320
|
-
agent_id TEXT NOT NULL,
|
|
2321
|
-
registry_address TEXT NOT NULL,
|
|
2322
|
-
tag1 TEXT NOT NULL DEFAULT '',
|
|
2323
|
-
tag2 TEXT NOT NULL DEFAULT '',
|
|
2324
|
-
score INTEGER NOT NULL,
|
|
2325
|
-
score_decimals INTEGER NOT NULL DEFAULT 0,
|
|
2326
|
-
feedback_count INTEGER NOT NULL DEFAULT 0,
|
|
2327
|
-
cached_at INTEGER NOT NULL,
|
|
2328
|
-
PRIMARY KEY (agent_id, registry_address, tag1, tag2)
|
|
2329
|
-
)`);
|
|
2330
|
-
// Step 3: Add approval_type to pending_approvals
|
|
2331
|
-
sqlite.exec("ALTER TABLE pending_approvals ADD COLUMN approval_type TEXT NOT NULL DEFAULT 'SIWE' CHECK (approval_type IN ('SIWE', 'EIP712'))");
|
|
2332
|
-
// Step 4: Recreate policies table with REPUTATION_THRESHOLD in CHECK constraint
|
|
2333
|
-
// Uses same pattern as v11, v20, v27, v33 (INSERT → DROP → RENAME)
|
|
2334
|
-
sqlite.exec(`CREATE TABLE policies_new (
|
|
2335
|
-
id TEXT PRIMARY KEY,
|
|
2336
|
-
wallet_id TEXT REFERENCES wallets(id) ON DELETE CASCADE,
|
|
2337
|
-
type TEXT NOT NULL CHECK (type IN (${inList(POLICY_TYPES)})),
|
|
2338
|
-
rules TEXT NOT NULL,
|
|
2339
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
2340
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
2341
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
2342
|
-
created_at INTEGER NOT NULL,
|
|
2343
|
-
updated_at INTEGER NOT NULL
|
|
2344
|
-
)`);
|
|
2345
|
-
sqlite.exec('INSERT INTO policies_new SELECT * FROM policies');
|
|
2346
|
-
sqlite.exec('DROP TABLE policies');
|
|
2347
|
-
sqlite.exec('ALTER TABLE policies_new RENAME TO policies');
|
|
2348
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_policies_wallet_enabled ON policies(wallet_id, enabled)');
|
|
2349
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_policies_type ON policies(type)');
|
|
2350
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_policies_network ON policies(network)');
|
|
2351
|
-
sqlite.exec('COMMIT');
|
|
2352
|
-
},
|
|
2353
|
-
});
|
|
2354
|
-
// ---------------------------------------------------------------------------
|
|
2355
|
-
// v40: Add typed_data_json column to pending_approvals for EIP-712 approval flow
|
|
2356
|
-
// ---------------------------------------------------------------------------
|
|
2357
|
-
MIGRATIONS.push({
|
|
2358
|
-
version: 40,
|
|
2359
|
-
description: 'ERC-8004: pending_approvals.typed_data_json for EIP-712 approval payloads',
|
|
2360
|
-
up: (sqlite) => {
|
|
2361
|
-
sqlite.exec("ALTER TABLE pending_approvals ADD COLUMN typed_data_json TEXT");
|
|
2362
|
-
},
|
|
2363
|
-
});
|
|
2364
|
-
// ---------------------------------------------------------------------------
|
|
2365
|
-
// v41: Smart Account per-wallet provider columns (v30.9)
|
|
2366
|
-
// ---------------------------------------------------------------------------
|
|
2367
|
-
MIGRATIONS.push({
|
|
2368
|
-
version: 41,
|
|
2369
|
-
description: 'Smart Account per-wallet provider: aa_provider, aa_provider_api_key_encrypted, aa_bundler_url, aa_paymaster_url',
|
|
2370
|
-
up: (sqlite) => {
|
|
2371
|
-
// Skip if columns already exist (e.g. fresh DDL via pushSchema includes them)
|
|
2372
|
-
const columns = sqlite
|
|
2373
|
-
.prepare("PRAGMA table_info('wallets')")
|
|
2374
|
-
.all();
|
|
2375
|
-
const has = (name) => columns.some((c) => c.name === name);
|
|
2376
|
-
if (!has('aa_provider')) {
|
|
2377
|
-
sqlite.exec("ALTER TABLE wallets ADD COLUMN aa_provider TEXT CHECK (aa_provider IS NULL OR aa_provider IN ('pimlico', 'alchemy', 'custom'))");
|
|
2378
|
-
}
|
|
2379
|
-
if (!has('aa_provider_api_key_encrypted')) {
|
|
2380
|
-
sqlite.exec("ALTER TABLE wallets ADD COLUMN aa_provider_api_key_encrypted TEXT");
|
|
2381
|
-
}
|
|
2382
|
-
if (!has('aa_bundler_url')) {
|
|
2383
|
-
sqlite.exec("ALTER TABLE wallets ADD COLUMN aa_bundler_url TEXT");
|
|
2384
|
-
}
|
|
2385
|
-
if (!has('aa_paymaster_url')) {
|
|
2386
|
-
sqlite.exec("ALTER TABLE wallets ADD COLUMN aa_paymaster_url TEXT");
|
|
2387
|
-
}
|
|
2388
|
-
},
|
|
2389
|
-
});
|
|
2390
|
-
// ---------------------------------------------------------------------------
|
|
2391
|
-
// v42: Seed all 10 action provider _enabled defaults to true
|
|
2392
|
-
// ---------------------------------------------------------------------------
|
|
2393
|
-
MIGRATIONS.push({
|
|
2394
|
-
version: 42,
|
|
2395
|
-
description: 'Seed all 10 action provider _enabled defaults to true (INSERT OR IGNORE preserves existing)',
|
|
2396
|
-
up: (sqlite) => {
|
|
2397
|
-
const keys = [
|
|
2398
|
-
'actions.jupiter_swap_enabled',
|
|
2399
|
-
'actions.zerox_swap_enabled',
|
|
2400
|
-
'actions.lifi_enabled',
|
|
2401
|
-
'actions.lido_staking_enabled',
|
|
2402
|
-
'actions.jito_staking_enabled',
|
|
2403
|
-
'actions.aave_v3_enabled',
|
|
2404
|
-
'actions.kamino_enabled',
|
|
2405
|
-
'actions.pendle_yield_enabled',
|
|
2406
|
-
'actions.drift_enabled',
|
|
2407
|
-
'actions.erc8004_agent_enabled',
|
|
2408
|
-
];
|
|
2409
|
-
const now = Math.floor(Date.now() / 1000);
|
|
2410
|
-
const stmt = sqlite.prepare("INSERT OR IGNORE INTO settings (key, value, encrypted, category, updated_at) VALUES (?, 'true', 0, 'actions', ?)");
|
|
2411
|
-
for (const key of keys) {
|
|
2412
|
-
stmt.run(key, now);
|
|
2413
|
-
}
|
|
2414
|
-
},
|
|
2415
|
-
});
|
|
2416
|
-
// ---------------------------------------------------------------------------
|
|
2417
|
-
// #252: Paymaster Policy ID column
|
|
2418
|
-
// ---------------------------------------------------------------------------
|
|
2419
|
-
MIGRATIONS.push({
|
|
2420
|
-
version: 43,
|
|
2421
|
-
description: 'Add aa_paymaster_policy_id column to wallets for paymaster context (sponsorshipPolicyId)',
|
|
2422
|
-
up: (sqlite) => {
|
|
2423
|
-
const cols = sqlite.pragma('table_info(wallets)');
|
|
2424
|
-
const has = (n) => cols.some((c) => c.name === n);
|
|
2425
|
-
if (!has('aa_paymaster_policy_id')) {
|
|
2426
|
-
sqlite.exec('ALTER TABLE wallets ADD COLUMN aa_paymaster_policy_id TEXT');
|
|
2427
|
-
}
|
|
2428
|
-
},
|
|
2429
|
-
});
|
|
2430
|
-
// v44: Create nft_metadata_cache table for NFT metadata caching (24h TTL)
|
|
2431
|
-
MIGRATIONS.push({
|
|
2432
|
-
version: 44,
|
|
2433
|
-
description: 'Create nft_metadata_cache table for NFT metadata caching (24h TTL)',
|
|
2434
|
-
up: (sqlite) => {
|
|
2435
|
-
sqlite.exec(`
|
|
2436
|
-
CREATE TABLE IF NOT EXISTS nft_metadata_cache (
|
|
2437
|
-
id TEXT PRIMARY KEY,
|
|
2438
|
-
contract_address TEXT NOT NULL,
|
|
2439
|
-
token_id TEXT NOT NULL,
|
|
2440
|
-
chain TEXT NOT NULL CHECK (chain IN (${inList(CHAIN_TYPES)})),
|
|
2441
|
-
network TEXT NOT NULL CHECK (network IN (${inList(NETWORK_TYPES)})),
|
|
2442
|
-
metadata_json TEXT NOT NULL,
|
|
2443
|
-
cached_at INTEGER NOT NULL,
|
|
2444
|
-
expires_at INTEGER NOT NULL
|
|
2445
|
-
)
|
|
2446
|
-
`);
|
|
2447
|
-
sqlite.exec(`
|
|
2448
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_nft_cache_unique
|
|
2449
|
-
ON nft_metadata_cache (contract_address, token_id, chain, network)
|
|
2450
|
-
`);
|
|
2451
|
-
sqlite.exec(`
|
|
2452
|
-
CREATE INDEX IF NOT EXISTS idx_nft_cache_expires
|
|
2453
|
-
ON nft_metadata_cache (expires_at)
|
|
2454
|
-
`);
|
|
2455
|
-
},
|
|
2456
|
-
});
|
|
2457
|
-
// v45: Create userop_builds table for UserOp Build/Sign API (v31.2)
|
|
2458
|
-
MIGRATIONS.push({
|
|
2459
|
-
version: 45,
|
|
2460
|
-
description: 'Create userop_builds table for UserOp Build/Sign API',
|
|
2461
|
-
up: (sqlite) => {
|
|
2462
|
-
sqlite.exec(`
|
|
2463
|
-
CREATE TABLE IF NOT EXISTS userop_builds (
|
|
2464
|
-
id TEXT PRIMARY KEY,
|
|
2465
|
-
wallet_id TEXT NOT NULL,
|
|
2466
|
-
call_data TEXT NOT NULL,
|
|
2467
|
-
sender TEXT NOT NULL,
|
|
2468
|
-
nonce TEXT NOT NULL,
|
|
2469
|
-
entry_point TEXT NOT NULL,
|
|
2470
|
-
created_at INTEGER NOT NULL,
|
|
2471
|
-
expires_at INTEGER NOT NULL,
|
|
2472
|
-
used INTEGER NOT NULL DEFAULT 0 CHECK (used IN (0, 1))
|
|
2473
|
-
)
|
|
2474
|
-
`);
|
|
2475
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_userop_builds_wallet_id ON userop_builds(wallet_id)');
|
|
2476
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_userop_builds_expires ON userop_builds(expires_at)');
|
|
2477
|
-
},
|
|
2478
|
-
});
|
|
2479
|
-
// ── v46: Backfill CONTRACT_CALL amount from metadata (#260) ──────────
|
|
2480
|
-
MIGRATIONS.push({
|
|
2481
|
-
version: 46,
|
|
2482
|
-
description: 'Backfill CONTRACT_CALL amount from metadata.originalRequest.value',
|
|
2483
|
-
up: (sqlite) => {
|
|
2484
|
-
sqlite.exec(`
|
|
2485
|
-
UPDATE transactions
|
|
2486
|
-
SET amount = json_extract(metadata, '$.originalRequest.value')
|
|
2487
|
-
WHERE type = 'CONTRACT_CALL'
|
|
2488
|
-
AND amount IS NULL
|
|
2489
|
-
AND json_extract(metadata, '$.originalRequest.value') IS NOT NULL
|
|
2490
|
-
`);
|
|
2491
|
-
},
|
|
2492
|
-
});
|
|
2493
|
-
// ── v47: Add factory_address column to wallets (#256) ────────────────
|
|
2494
|
-
MIGRATIONS.push({
|
|
2495
|
-
version: 47,
|
|
2496
|
-
description: 'Add factory_address column to wallets for multichain Smart Account factory tracking',
|
|
2497
|
-
up: (sqlite) => {
|
|
2498
|
-
// Add nullable factory_address column
|
|
2499
|
-
sqlite.exec(`ALTER TABLE wallets ADD COLUMN factory_address TEXT`);
|
|
2500
|
-
// Backfill: existing smart accounts used the Solady factory
|
|
2501
|
-
sqlite.exec(`
|
|
2502
|
-
UPDATE wallets
|
|
2503
|
-
SET factory_address = '0x5d82735936c6Cd5DE57cC3c1A799f6B2E6F933Df'
|
|
2504
|
-
WHERE account_type = 'smart' AND factory_address IS NULL
|
|
2505
|
-
`);
|
|
2506
|
-
},
|
|
2507
|
-
});
|
|
2508
|
-
// ── v48: Purge mock defi_positions data from Kamino/Drift (#263/#269) ──
|
|
2509
|
-
MIGRATIONS.push({
|
|
2510
|
-
version: 48,
|
|
2511
|
-
description: 'Purge mock defi_positions data from Kamino/Drift (#263)',
|
|
2512
|
-
up: (sqlite) => {
|
|
2513
|
-
sqlite.exec(`DELETE FROM defi_positions WHERE provider IN ('kamino', 'drift_perp')`);
|
|
2514
|
-
},
|
|
2515
|
-
});
|
|
2516
|
-
// ── v49: Fix bugged smart account wallets — convert to EOA (#272) ─────
|
|
2517
|
-
MIGRATIONS.push({
|
|
2518
|
-
version: 49,
|
|
2519
|
-
description: 'Convert bugged smart account wallets (missing signerKey) to EOA (#272)',
|
|
2520
|
-
up: (sqlite) => {
|
|
2521
|
-
// Smart account wallets created while smartAccountService was not injected
|
|
2522
|
-
// have accountType='smart' but signerKey=NULL (signer_key column).
|
|
2523
|
-
// Their publicKey is already the EOA address, so convert them to EOA type.
|
|
2524
|
-
sqlite.exec(`
|
|
2525
|
-
UPDATE wallets
|
|
2526
|
-
SET account_type = 'eoa',
|
|
2527
|
-
deployed = 1,
|
|
2528
|
-
entry_point = NULL,
|
|
2529
|
-
factory_address = NULL
|
|
2530
|
-
WHERE account_type = 'smart' AND signer_key IS NULL
|
|
2531
|
-
`);
|
|
2532
|
-
},
|
|
2533
|
-
});
|
|
2534
|
-
// ── v50: Add network column to userop_builds (#279) ──────────────────
|
|
2535
|
-
MIGRATIONS.push({
|
|
2536
|
-
version: 50,
|
|
2537
|
-
description: 'Add network column to userop_builds for Sign route RPC resolve (#279)',
|
|
2538
|
-
up: (sqlite) => {
|
|
2539
|
-
const cols = sqlite.prepare("PRAGMA table_info('userop_builds')").all();
|
|
2540
|
-
if (!cols.some((c) => c.name === 'network')) {
|
|
2541
|
-
sqlite.exec(`ALTER TABLE userop_builds ADD COLUMN network TEXT`);
|
|
2542
|
-
}
|
|
2543
|
-
},
|
|
2544
|
-
});
|
|
2545
|
-
// ── v51: Hyperliquid order history table ─────────────────────────────
|
|
2546
|
-
MIGRATIONS.push({
|
|
2547
|
-
version: 51,
|
|
2548
|
-
description: 'Create hyperliquid_orders table for Hyperliquid DEX integration',
|
|
2549
|
-
up: (sqlite) => {
|
|
2550
|
-
// Idempotent check
|
|
2551
|
-
const tables = sqlite
|
|
2552
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='hyperliquid_orders'")
|
|
2553
|
-
.all();
|
|
2554
|
-
if (tables.length > 0)
|
|
2555
|
-
return;
|
|
2556
|
-
sqlite.exec(`
|
|
2557
|
-
CREATE TABLE hyperliquid_orders (
|
|
2558
|
-
id TEXT PRIMARY KEY,
|
|
2559
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
2560
|
-
sub_account_address TEXT,
|
|
2561
|
-
oid INTEGER,
|
|
2562
|
-
cloid TEXT,
|
|
2563
|
-
transaction_id TEXT REFERENCES transactions(id),
|
|
2564
|
-
market TEXT NOT NULL,
|
|
2565
|
-
asset_index INTEGER NOT NULL,
|
|
2566
|
-
side TEXT NOT NULL CHECK(side IN ('BUY', 'SELL')),
|
|
2567
|
-
order_type TEXT NOT NULL CHECK(order_type IN ('MARKET', 'LIMIT', 'STOP_MARKET', 'STOP_LIMIT', 'TAKE_PROFIT')),
|
|
2568
|
-
size TEXT NOT NULL,
|
|
2569
|
-
price TEXT,
|
|
2570
|
-
trigger_price TEXT,
|
|
2571
|
-
tif TEXT CHECK(tif IN ('GTC', 'IOC', 'ALO')),
|
|
2572
|
-
reduce_only INTEGER NOT NULL DEFAULT 0,
|
|
2573
|
-
status TEXT NOT NULL CHECK(status IN ('PENDING', 'RESTING', 'FILLED', 'PARTIALLY_FILLED', 'CANCELLED', 'REJECTED', 'TRIGGERED')),
|
|
2574
|
-
filled_size TEXT,
|
|
2575
|
-
avg_fill_price TEXT,
|
|
2576
|
-
is_spot INTEGER NOT NULL DEFAULT 0,
|
|
2577
|
-
leverage INTEGER,
|
|
2578
|
-
margin_mode TEXT CHECK(margin_mode IN ('CROSS', 'ISOLATED')),
|
|
2579
|
-
response_data TEXT,
|
|
2580
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
2581
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
2582
|
-
);
|
|
2583
|
-
CREATE INDEX idx_hl_orders_wallet ON hyperliquid_orders(wallet_id);
|
|
2584
|
-
CREATE INDEX idx_hl_orders_oid ON hyperliquid_orders(oid);
|
|
2585
|
-
CREATE INDEX idx_hl_orders_market ON hyperliquid_orders(market);
|
|
2586
|
-
CREATE INDEX idx_hl_orders_status ON hyperliquid_orders(status);
|
|
2587
|
-
CREATE INDEX idx_hl_orders_created ON hyperliquid_orders(created_at);
|
|
2588
|
-
`);
|
|
2589
|
-
},
|
|
2590
|
-
});
|
|
2591
|
-
// ── v52: Hyperliquid sub-accounts table ──────────────────────────────
|
|
2592
|
-
MIGRATIONS.push({
|
|
2593
|
-
version: 52,
|
|
2594
|
-
description: 'Create hyperliquid_sub_accounts table for Hyperliquid Sub-account management',
|
|
2595
|
-
up: (sqlite) => {
|
|
2596
|
-
// Idempotent check
|
|
2597
|
-
const tables = sqlite
|
|
2598
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='hyperliquid_sub_accounts'")
|
|
2599
|
-
.all();
|
|
2600
|
-
if (tables.length > 0)
|
|
2601
|
-
return;
|
|
2602
|
-
sqlite.exec(`
|
|
2603
|
-
CREATE TABLE hyperliquid_sub_accounts (
|
|
2604
|
-
id TEXT PRIMARY KEY,
|
|
2605
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
2606
|
-
sub_account_address TEXT NOT NULL,
|
|
2607
|
-
name TEXT,
|
|
2608
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
2609
|
-
UNIQUE(wallet_id, sub_account_address)
|
|
2610
|
-
);
|
|
2611
|
-
CREATE INDEX idx_hl_sub_wallet ON hyperliquid_sub_accounts(wallet_id);
|
|
2612
|
-
`);
|
|
2613
|
-
},
|
|
2614
|
-
});
|
|
2615
|
-
// ---------------------------------------------------------------------------
|
|
2616
|
-
// v53: polymarket_orders table
|
|
2617
|
-
// ---------------------------------------------------------------------------
|
|
2618
|
-
MIGRATIONS.push({
|
|
2619
|
-
version: 53,
|
|
2620
|
-
description: 'Create polymarket_orders table for Polymarket CLOB order tracking',
|
|
2621
|
-
up: (sqlite) => {
|
|
2622
|
-
// Idempotent check
|
|
2623
|
-
const tables = sqlite
|
|
2624
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='polymarket_orders'")
|
|
2625
|
-
.all();
|
|
2626
|
-
if (tables.length > 0)
|
|
2627
|
-
return;
|
|
2628
|
-
sqlite.exec(`
|
|
2629
|
-
CREATE TABLE polymarket_orders (
|
|
2630
|
-
id TEXT PRIMARY KEY,
|
|
2631
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
2632
|
-
transaction_id TEXT REFERENCES transactions(id),
|
|
2633
|
-
condition_id TEXT NOT NULL,
|
|
2634
|
-
token_id TEXT NOT NULL,
|
|
2635
|
-
market_slug TEXT,
|
|
2636
|
-
outcome TEXT NOT NULL,
|
|
2637
|
-
order_id TEXT,
|
|
2638
|
-
side TEXT NOT NULL CHECK (side IN ('BUY', 'SELL')),
|
|
2639
|
-
order_type TEXT NOT NULL CHECK (order_type IN ('GTC', 'GTD', 'FOK', 'IOC')),
|
|
2640
|
-
price TEXT NOT NULL,
|
|
2641
|
-
size TEXT NOT NULL,
|
|
2642
|
-
status TEXT NOT NULL CHECK (status IN ('PENDING', 'LIVE', 'MATCHED', 'PARTIALLY_FILLED', 'CANCELLED', 'EXPIRED')),
|
|
2643
|
-
filled_size TEXT,
|
|
2644
|
-
avg_fill_price TEXT,
|
|
2645
|
-
salt TEXT,
|
|
2646
|
-
maker_amount TEXT,
|
|
2647
|
-
taker_amount TEXT,
|
|
2648
|
-
signature_type INTEGER NOT NULL DEFAULT 0,
|
|
2649
|
-
fee_rate_bps INTEGER,
|
|
2650
|
-
expiration INTEGER,
|
|
2651
|
-
nonce TEXT,
|
|
2652
|
-
is_neg_risk INTEGER NOT NULL DEFAULT 0,
|
|
2653
|
-
response_data TEXT,
|
|
2654
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
2655
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
2656
|
-
);
|
|
2657
|
-
CREATE INDEX idx_pm_orders_wallet ON polymarket_orders(wallet_id);
|
|
2658
|
-
CREATE INDEX idx_pm_orders_order_id ON polymarket_orders(order_id);
|
|
2659
|
-
CREATE INDEX idx_pm_orders_condition ON polymarket_orders(condition_id);
|
|
2660
|
-
CREATE INDEX idx_pm_orders_status ON polymarket_orders(status);
|
|
2661
|
-
CREATE INDEX idx_pm_orders_created ON polymarket_orders(created_at);
|
|
2662
|
-
`);
|
|
2663
|
-
},
|
|
2664
|
-
});
|
|
2665
|
-
// ---------------------------------------------------------------------------
|
|
2666
|
-
// v54: polymarket_positions + polymarket_api_keys tables
|
|
2667
|
-
// ---------------------------------------------------------------------------
|
|
2668
|
-
MIGRATIONS.push({
|
|
2669
|
-
version: 54,
|
|
2670
|
-
description: 'Create polymarket_positions and polymarket_api_keys tables',
|
|
2671
|
-
up: (sqlite) => {
|
|
2672
|
-
// polymarket_positions
|
|
2673
|
-
const posTables = sqlite
|
|
2674
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='polymarket_positions'")
|
|
2675
|
-
.all();
|
|
2676
|
-
if (posTables.length === 0) {
|
|
2677
|
-
sqlite.exec(`
|
|
2678
|
-
CREATE TABLE polymarket_positions (
|
|
2679
|
-
id TEXT PRIMARY KEY,
|
|
2680
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
2681
|
-
condition_id TEXT NOT NULL,
|
|
2682
|
-
token_id TEXT NOT NULL,
|
|
2683
|
-
market_slug TEXT,
|
|
2684
|
-
outcome TEXT NOT NULL CHECK (outcome IN ('YES', 'NO')),
|
|
2685
|
-
size TEXT NOT NULL DEFAULT '0',
|
|
2686
|
-
avg_price TEXT,
|
|
2687
|
-
realized_pnl TEXT DEFAULT '0',
|
|
2688
|
-
market_resolved INTEGER NOT NULL DEFAULT 0,
|
|
2689
|
-
winning_outcome TEXT,
|
|
2690
|
-
is_neg_risk INTEGER NOT NULL DEFAULT 0,
|
|
2691
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
2692
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
2693
|
-
UNIQUE(wallet_id, token_id)
|
|
2694
|
-
);
|
|
2695
|
-
CREATE INDEX idx_pm_positions_wallet ON polymarket_positions(wallet_id);
|
|
2696
|
-
CREATE INDEX idx_pm_positions_condition ON polymarket_positions(condition_id);
|
|
2697
|
-
CREATE INDEX idx_pm_positions_resolved ON polymarket_positions(market_resolved);
|
|
2698
|
-
`);
|
|
2699
|
-
}
|
|
2700
|
-
// polymarket_api_keys
|
|
2701
|
-
const keyTables = sqlite
|
|
2702
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='polymarket_api_keys'")
|
|
2703
|
-
.all();
|
|
2704
|
-
if (keyTables.length === 0) {
|
|
2705
|
-
sqlite.exec(`
|
|
2706
|
-
CREATE TABLE polymarket_api_keys (
|
|
2707
|
-
id TEXT PRIMARY KEY,
|
|
2708
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id),
|
|
2709
|
-
api_key TEXT NOT NULL,
|
|
2710
|
-
api_secret_encrypted TEXT NOT NULL,
|
|
2711
|
-
api_passphrase_encrypted TEXT NOT NULL,
|
|
2712
|
-
signature_type INTEGER NOT NULL DEFAULT 0,
|
|
2713
|
-
proxy_address TEXT,
|
|
2714
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
2715
|
-
UNIQUE(wallet_id)
|
|
2716
|
-
);
|
|
2717
|
-
`);
|
|
2718
|
-
}
|
|
2719
|
-
},
|
|
2720
|
-
});
|
|
2721
|
-
// ---------------------------------------------------------------------------
|
|
2722
|
-
// v55: wallet_credentials table for External Action credential vault
|
|
2723
|
-
// ---------------------------------------------------------------------------
|
|
2724
|
-
MIGRATIONS.push({
|
|
2725
|
-
version: 55,
|
|
2726
|
-
description: 'Create wallet_credentials table for External Action credential vault',
|
|
2727
|
-
up: (sqlite) => {
|
|
2728
|
-
// Idempotent check
|
|
2729
|
-
const tables = sqlite
|
|
2730
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='wallet_credentials'")
|
|
2731
|
-
.all();
|
|
2732
|
-
if (tables.length > 0)
|
|
2733
|
-
return;
|
|
2734
|
-
sqlite.exec(`
|
|
2735
|
-
CREATE TABLE wallet_credentials (
|
|
2736
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
2737
|
-
wallet_id TEXT,
|
|
2738
|
-
type TEXT NOT NULL CHECK (type IN ('api-key','hmac-secret','rsa-private-key','session-token','custom')),
|
|
2739
|
-
name TEXT NOT NULL,
|
|
2740
|
-
encrypted_value BLOB NOT NULL,
|
|
2741
|
-
iv BLOB NOT NULL,
|
|
2742
|
-
auth_tag BLOB NOT NULL,
|
|
2743
|
-
metadata TEXT NOT NULL DEFAULT '{}',
|
|
2744
|
-
expires_at INTEGER,
|
|
2745
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
2746
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
2747
|
-
FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE
|
|
2748
|
-
);
|
|
2749
|
-
CREATE UNIQUE INDEX idx_wallet_credentials_wallet_name ON wallet_credentials(wallet_id, name);
|
|
2750
|
-
CREATE INDEX idx_wallet_credentials_global_name ON wallet_credentials(name) WHERE wallet_id IS NULL;
|
|
2751
|
-
CREATE INDEX idx_wallet_credentials_wallet_id ON wallet_credentials(wallet_id);
|
|
2752
|
-
CREATE INDEX idx_wallet_credentials_expires_at ON wallet_credentials(expires_at) WHERE expires_at IS NOT NULL;
|
|
2753
|
-
`);
|
|
2754
|
-
},
|
|
2755
|
-
});
|
|
2756
|
-
// ---------------------------------------------------------------------------
|
|
2757
|
-
// v56: transactions table action tracking columns
|
|
2758
|
-
// ---------------------------------------------------------------------------
|
|
2759
|
-
MIGRATIONS.push({
|
|
2760
|
-
version: 56,
|
|
2761
|
-
description: 'Add action_kind, venue, operation, external_id columns to transactions',
|
|
2762
|
-
up: (sqlite) => {
|
|
2763
|
-
// Check if columns already exist (pushSchema may have already added them)
|
|
2764
|
-
const cols = sqlite.prepare('PRAGMA table_info(transactions)').all()
|
|
2765
|
-
.map(c => c.name);
|
|
2766
|
-
if (!cols.includes('action_kind')) {
|
|
2767
|
-
sqlite.exec("ALTER TABLE transactions ADD COLUMN action_kind TEXT NOT NULL DEFAULT 'contractCall'");
|
|
2768
|
-
}
|
|
2769
|
-
if (!cols.includes('venue')) {
|
|
2770
|
-
sqlite.exec('ALTER TABLE transactions ADD COLUMN venue TEXT');
|
|
2771
|
-
}
|
|
2772
|
-
if (!cols.includes('operation')) {
|
|
2773
|
-
sqlite.exec('ALTER TABLE transactions ADD COLUMN operation TEXT');
|
|
2774
|
-
}
|
|
2775
|
-
if (!cols.includes('external_id')) {
|
|
2776
|
-
sqlite.exec('ALTER TABLE transactions ADD COLUMN external_id TEXT');
|
|
2777
|
-
}
|
|
2778
|
-
// Create indexes (idempotent with IF NOT EXISTS)
|
|
2779
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_action_kind ON transactions(action_kind)');
|
|
2780
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_venue ON transactions(venue) WHERE venue IS NOT NULL');
|
|
2781
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_external_id ON transactions(external_id) WHERE external_id IS NOT NULL');
|
|
2782
|
-
},
|
|
2783
|
-
});
|
|
2784
|
-
// ---------------------------------------------------------------------------
|
|
2785
|
-
// v57: composite index for external action tracking queries
|
|
2786
|
-
// ---------------------------------------------------------------------------
|
|
2787
|
-
MIGRATIONS.push({
|
|
2788
|
-
version: 57,
|
|
2789
|
-
description: 'Add composite index idx_transactions_action_kind_bridge_status',
|
|
2790
|
-
up: (sqlite) => {
|
|
2791
|
-
sqlite.exec('CREATE INDEX IF NOT EXISTS idx_transactions_action_kind_bridge_status ON transactions(action_kind, bridge_status) WHERE bridge_status IS NOT NULL');
|
|
2792
|
-
},
|
|
2793
|
-
});
|
|
2794
|
-
// ---------------------------------------------------------------------------
|
|
2795
|
-
// v58: Update transactions type CHECK constraint to include CONTRACT_DEPLOY
|
|
2796
|
-
// ---------------------------------------------------------------------------
|
|
2797
|
-
MIGRATIONS.push({
|
|
2798
|
-
version: 58,
|
|
2799
|
-
description: 'Add CONTRACT_DEPLOY to transactions type CHECK constraint (12-step table recreation)',
|
|
2800
|
-
managesOwnTransaction: true,
|
|
2801
|
-
up: (sqlite) => {
|
|
2802
|
-
sqlite.exec('BEGIN');
|
|
2803
|
-
try {
|
|
2804
|
-
// Step 1: Create transactions_new with updated CHECK constraints (CONTRACT_DEPLOY in TRANSACTION_TYPES)
|
|
2805
|
-
sqlite.exec(`CREATE TABLE transactions_new (
|
|
2806
|
-
id TEXT PRIMARY KEY,
|
|
2807
|
-
wallet_id TEXT NOT NULL REFERENCES wallets(id) ON DELETE RESTRICT,
|
|
2808
|
-
session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
2809
|
-
chain TEXT NOT NULL,
|
|
2810
|
-
tx_hash TEXT,
|
|
2811
|
-
type TEXT NOT NULL CHECK (type IN (${inList(TRANSACTION_TYPES)})),
|
|
2812
|
-
amount TEXT,
|
|
2813
|
-
to_address TEXT,
|
|
2814
|
-
token_mint TEXT,
|
|
2815
|
-
contract_address TEXT,
|
|
2816
|
-
method_signature TEXT,
|
|
2817
|
-
spender_address TEXT,
|
|
2818
|
-
approved_amount TEXT,
|
|
2819
|
-
parent_id TEXT REFERENCES transactions_new(id) ON DELETE CASCADE,
|
|
2820
|
-
batch_index INTEGER,
|
|
2821
|
-
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN (${inList(TRANSACTION_STATUSES)})),
|
|
2822
|
-
tier TEXT CHECK (tier IS NULL OR tier IN (${inList(POLICY_TIERS)})),
|
|
2823
|
-
queued_at INTEGER,
|
|
2824
|
-
executed_at INTEGER,
|
|
2825
|
-
created_at INTEGER NOT NULL,
|
|
2826
|
-
reserved_amount TEXT,
|
|
2827
|
-
amount_usd REAL,
|
|
2828
|
-
reserved_amount_usd REAL,
|
|
2829
|
-
error TEXT,
|
|
2830
|
-
metadata TEXT,
|
|
2831
|
-
network TEXT CHECK (network IS NULL OR network IN (${inList(NETWORK_TYPES)})),
|
|
2832
|
-
bridge_status TEXT CHECK (bridge_status IS NULL OR bridge_status IN ('PENDING', 'COMPLETED', 'FAILED', 'BRIDGE_MONITORING', 'TIMEOUT', 'REFUNDED', 'PARTIALLY_FILLED', 'FILLED', 'CANCELED', 'SETTLED', 'EXPIRED')),
|
|
2833
|
-
bridge_metadata TEXT,
|
|
2834
|
-
action_kind TEXT NOT NULL DEFAULT 'contractCall',
|
|
2835
|
-
venue TEXT,
|
|
2836
|
-
operation TEXT,
|
|
2837
|
-
external_id TEXT
|
|
2838
|
-
)`);
|
|
2839
|
-
// Step 2: Copy existing data
|
|
2840
|
-
sqlite.exec(`INSERT INTO transactions_new (id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, amount_usd, reserved_amount_usd, error, metadata, network, bridge_status, bridge_metadata, action_kind, venue, operation, external_id)
|
|
2841
|
-
SELECT id, wallet_id, session_id, chain, tx_hash, type, amount, to_address, token_mint, contract_address, method_signature, spender_address, approved_amount, parent_id, batch_index, status, tier, queued_at, executed_at, created_at, reserved_amount, amount_usd, reserved_amount_usd, error, metadata, network, bridge_status, bridge_metadata, action_kind, venue, operation, external_id FROM transactions`);
|
|
2842
|
-
// Step 3: Drop old table
|
|
2843
|
-
sqlite.exec('DROP TABLE transactions');
|
|
2844
|
-
// Step 4: Rename new table
|
|
2845
|
-
sqlite.exec('ALTER TABLE transactions_new RENAME TO transactions');
|
|
2846
|
-
// Step 5: Recreate all 14 indexes
|
|
2847
|
-
sqlite.exec('CREATE INDEX idx_transactions_wallet_status ON transactions(wallet_id, status)');
|
|
2848
|
-
sqlite.exec('CREATE INDEX idx_transactions_session_id ON transactions(session_id)');
|
|
2849
|
-
sqlite.exec('CREATE UNIQUE INDEX idx_transactions_tx_hash ON transactions(tx_hash)');
|
|
2850
|
-
sqlite.exec('CREATE INDEX idx_transactions_queued_at ON transactions(queued_at)');
|
|
2851
|
-
sqlite.exec('CREATE INDEX idx_transactions_created_at ON transactions(created_at)');
|
|
2852
|
-
sqlite.exec('CREATE INDEX idx_transactions_type ON transactions(type)');
|
|
2853
|
-
sqlite.exec('CREATE INDEX idx_transactions_contract_address ON transactions(contract_address)');
|
|
2854
|
-
sqlite.exec('CREATE INDEX idx_transactions_parent_id ON transactions(parent_id)');
|
|
2855
|
-
sqlite.exec("CREATE INDEX idx_transactions_bridge_status ON transactions(bridge_status) WHERE bridge_status IS NOT NULL");
|
|
2856
|
-
sqlite.exec("CREATE INDEX idx_transactions_gas_waiting ON transactions(status) WHERE status = 'GAS_WAITING'");
|
|
2857
|
-
sqlite.exec('CREATE INDEX idx_transactions_action_kind ON transactions(action_kind)');
|
|
2858
|
-
sqlite.exec("CREATE INDEX idx_transactions_venue ON transactions(venue) WHERE venue IS NOT NULL");
|
|
2859
|
-
sqlite.exec("CREATE INDEX idx_transactions_external_id ON transactions(external_id) WHERE external_id IS NOT NULL");
|
|
2860
|
-
sqlite.exec('CREATE INDEX idx_transactions_action_kind_bridge_status ON transactions(action_kind, bridge_status) WHERE bridge_status IS NOT NULL');
|
|
2861
|
-
// Step 6: Commit
|
|
2862
|
-
sqlite.exec('COMMIT');
|
|
2863
|
-
}
|
|
2864
|
-
catch (err) {
|
|
2865
|
-
sqlite.exec('ROLLBACK');
|
|
2866
|
-
throw err;
|
|
2867
|
-
}
|
|
2868
|
-
// Step 7: Re-enable foreign keys and verify integrity
|
|
2869
|
-
sqlite.pragma('foreign_keys = ON');
|
|
2870
|
-
const fkErrors = sqlite.pragma('foreign_key_check');
|
|
2871
|
-
if (fkErrors.length > 0) {
|
|
2872
|
-
throw new Error(`FK integrity violation after v58: ${JSON.stringify(fkErrors)}`);
|
|
2873
|
-
}
|
|
2874
|
-
},
|
|
2875
|
-
});
|
|
2876
45
|
/**
|
|
2877
46
|
* Run incremental migrations against the database.
|
|
2878
47
|
*
|