@openora/core 0.4.1-canary.116 → 0.4.1-canary.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/admin-console/contract/index.d.ts +12 -0
- package/dist/admin-console/contract/index.d.ts.map +1 -1
- package/dist/admin-console/plugin.d.ts +17 -0
- package/dist/admin-console/plugin.d.ts.map +1 -1
- package/dist/admin-console/router/index.d.ts +6 -0
- package/dist/admin-console/router/index.d.ts.map +1 -1
- package/dist/admin-console/service/backoffice.service.d.ts +6 -2
- package/dist/admin-console/service/backoffice.service.d.ts.map +1 -1
- package/dist/analytics/plugin.d.ts +17 -0
- package/dist/analytics/plugin.d.ts.map +1 -1
- package/dist/audit/__tests__/audit.service.int.test.js +223 -4
- package/dist/audit/__tests__/audit.service.int.test.js.map +1 -1
- package/dist/audit/contract/index.d.ts +86 -0
- package/dist/audit/contract/index.d.ts.map +1 -1
- package/dist/audit/contract/index.js +23 -1
- package/dist/audit/contract/index.js.map +1 -1
- package/dist/audit/plugin.d.ts +17 -0
- package/dist/audit/plugin.d.ts.map +1 -1
- package/dist/audit/plugin.js +36 -9
- package/dist/audit/plugin.js.map +1 -1
- package/dist/audit/router/index.d.ts +44 -0
- package/dist/audit/router/index.d.ts.map +1 -1
- package/dist/audit/router/index.js +1 -0
- package/dist/audit/router/index.js.map +1 -1
- package/dist/audit/service/audit.service.d.ts +29 -6
- package/dist/audit/service/audit.service.d.ts.map +1 -1
- package/dist/audit/service/audit.service.js +51 -1
- package/dist/audit/service/audit.service.js.map +1 -1
- package/dist/casino/gaming/__tests__/gaming.service.int.test.js +33 -2
- package/dist/casino/gaming/__tests__/gaming.service.int.test.js.map +1 -1
- package/dist/casino/gaming/plugin.d.ts +17 -0
- package/dist/casino/gaming/plugin.d.ts.map +1 -1
- package/dist/casino/gaming/plugin.js +2 -2
- package/dist/casino/gaming/plugin.js.map +1 -1
- package/dist/casino/gaming/router/index.d.ts.map +1 -1
- package/dist/casino/gaming/router/index.js +2 -1
- package/dist/casino/gaming/router/index.js.map +1 -1
- package/dist/casino/gaming/service/gaming.service.d.ts +16 -7
- package/dist/casino/gaming/service/gaming.service.d.ts.map +1 -1
- package/dist/casino/gaming/service/gaming.service.js +11 -1
- package/dist/casino/gaming/service/gaming.service.js.map +1 -1
- package/dist/casino/lobby/plugin.d.ts +17 -0
- package/dist/casino/lobby/plugin.d.ts.map +1 -1
- package/dist/casino/lobby/service/lobby.service.d.ts +3 -1
- package/dist/casino/lobby/service/lobby.service.d.ts.map +1 -1
- package/dist/cms/plugin.d.ts +17 -0
- package/dist/cms/plugin.d.ts.map +1 -1
- package/dist/cms/service/cms.service.d.ts +6 -2
- package/dist/cms/service/cms.service.d.ts.map +1 -1
- package/dist/compliance/__tests__/compliance.service.int.test.js +4 -85
- package/dist/compliance/__tests__/compliance.service.int.test.js.map +1 -1
- package/dist/compliance/__tests__/kyc-admin.router.int.test.js +22 -16
- package/dist/compliance/__tests__/kyc-admin.router.int.test.js.map +1 -1
- package/dist/compliance/__tests__/kyc-webhook.router.int.test.js +25 -21
- package/dist/compliance/__tests__/kyc-webhook.router.int.test.js.map +1 -1
- package/dist/compliance/__tests__/kyc.service.int.test.js +130 -8
- package/dist/compliance/__tests__/kyc.service.int.test.js.map +1 -1
- package/dist/compliance/__tests__/limits.contract.test.js +41 -1
- package/dist/compliance/__tests__/limits.contract.test.js.map +1 -1
- package/dist/compliance/__tests__/rg-eval.test.js +28 -1
- package/dist/compliance/__tests__/rg-eval.test.js.map +1 -1
- package/dist/compliance/__tests__/rg-monitoring.service.int.test.js +279 -38
- package/dist/compliance/__tests__/rg-monitoring.service.int.test.js.map +1 -1
- package/dist/compliance/__tests__/rg-self-service.int.test.d.ts +2 -0
- package/dist/compliance/__tests__/rg-self-service.int.test.d.ts.map +1 -0
- package/dist/compliance/__tests__/rg-self-service.int.test.js +539 -0
- package/dist/compliance/__tests__/rg-self-service.int.test.js.map +1 -0
- package/dist/compliance/__tests__/rg.contract.test.js +53 -1
- package/dist/compliance/__tests__/rg.contract.test.js.map +1 -1
- package/dist/compliance/__tests__/rg.router.int.test.js +175 -10
- package/dist/compliance/__tests__/rg.router.int.test.js.map +1 -1
- package/dist/compliance/__tests__/rg.service.int.test.js +400 -34
- package/dist/compliance/__tests__/rg.service.int.test.js.map +1 -1
- package/dist/compliance/adapters/rg-limit-gate.d.ts +17 -0
- package/dist/compliance/adapters/rg-limit-gate.d.ts.map +1 -0
- package/dist/compliance/adapters/rg-limit-gate.js +107 -0
- package/dist/compliance/adapters/rg-limit-gate.js.map +1 -0
- package/dist/compliance/contract/index.d.ts +1046 -221
- package/dist/compliance/contract/index.d.ts.map +1 -1
- package/dist/compliance/contract/index.js +45 -7
- package/dist/compliance/contract/index.js.map +1 -1
- package/dist/compliance/contract/limits.d.ts +52 -0
- package/dist/compliance/contract/limits.d.ts.map +1 -1
- package/dist/compliance/contract/limits.js +27 -7
- package/dist/compliance/contract/limits.js.map +1 -1
- package/dist/compliance/contract/rg.d.ts +265 -0
- package/dist/compliance/contract/rg.d.ts.map +1 -1
- package/dist/compliance/contract/rg.js +41 -14
- package/dist/compliance/contract/rg.js.map +1 -1
- package/dist/compliance/drizzle/migrations/0005_reflective_magma.sql +13 -0
- package/dist/compliance/drizzle/migrations/0006_backfill_session_limit_currency.sql +15 -0
- package/dist/compliance/drizzle/migrations/0007_romantic_toxin.sql +6 -0
- package/dist/compliance/drizzle/migrations/meta/0005_snapshot.json +754 -0
- package/dist/compliance/drizzle/migrations/meta/0006_snapshot.json +754 -0
- package/dist/compliance/drizzle/migrations/meta/0007_snapshot.json +785 -0
- package/dist/compliance/drizzle/migrations/meta/_journal.json +21 -0
- package/dist/compliance/plugin.d.ts +18 -1
- package/dist/compliance/plugin.d.ts.map +1 -1
- package/dist/compliance/plugin.js +43 -8
- package/dist/compliance/plugin.js.map +1 -1
- package/dist/compliance/router/index.d.ts +643 -90
- package/dist/compliance/router/index.d.ts.map +1 -1
- package/dist/compliance/router/index.js +31 -15
- package/dist/compliance/router/index.js.map +1 -1
- package/dist/compliance/schema/index.d.ts +156 -0
- package/dist/compliance/schema/index.d.ts.map +1 -1
- package/dist/compliance/schema/index.js +20 -5
- package/dist/compliance/schema/index.js.map +1 -1
- package/dist/compliance/service/compliance.service.d.ts +12 -30
- package/dist/compliance/service/compliance.service.d.ts.map +1 -1
- package/dist/compliance/service/compliance.service.js +3 -40
- package/dist/compliance/service/compliance.service.js.map +1 -1
- package/dist/compliance/service/kyc.service.d.ts +53 -9
- package/dist/compliance/service/kyc.service.d.ts.map +1 -1
- package/dist/compliance/service/kyc.service.js +355 -101
- package/dist/compliance/service/kyc.service.js.map +1 -1
- package/dist/compliance/service/rg-eval.d.ts +7 -2
- package/dist/compliance/service/rg-eval.d.ts.map +1 -1
- package/dist/compliance/service/rg-eval.js +10 -2
- package/dist/compliance/service/rg-eval.js.map +1 -1
- package/dist/compliance/service/rg-monitoring.service.d.ts +24 -6
- package/dist/compliance/service/rg-monitoring.service.d.ts.map +1 -1
- package/dist/compliance/service/rg-monitoring.service.js +97 -20
- package/dist/compliance/service/rg-monitoring.service.js.map +1 -1
- package/dist/compliance/service/rg-self-service.service.d.ts +84 -0
- package/dist/compliance/service/rg-self-service.service.d.ts.map +1 -0
- package/dist/compliance/service/rg-self-service.service.js +449 -0
- package/dist/compliance/service/rg-self-service.service.js.map +1 -0
- package/dist/compliance/service/rg.service.d.ts +89 -10
- package/dist/compliance/service/rg.service.d.ts.map +1 -1
- package/dist/compliance/service/rg.service.js +201 -37
- package/dist/compliance/service/rg.service.js.map +1 -1
- package/dist/contracts/adapters/audit.d.ts +1 -1
- package/dist/contracts/adapters/audit.d.ts.map +1 -1
- package/dist/contracts/adapters/audit.js.map +1 -1
- package/dist/contracts/adapters/exchange-rate-provider.d.ts +14 -0
- package/dist/contracts/adapters/exchange-rate-provider.d.ts.map +1 -0
- package/dist/contracts/adapters/exchange-rate-provider.js +5 -0
- package/dist/contracts/adapters/exchange-rate-provider.js.map +1 -0
- package/dist/contracts/adapters/exchange-rate-reader.d.ts +16 -0
- package/dist/contracts/adapters/exchange-rate-reader.d.ts.map +1 -0
- package/dist/contracts/adapters/exchange-rate-reader.js +3 -0
- package/dist/contracts/adapters/exchange-rate-reader.js.map +1 -0
- package/dist/contracts/adapters/index.d.ts +10 -2
- package/dist/contracts/adapters/index.d.ts.map +1 -1
- package/dist/contracts/adapters/index.js +4 -0
- package/dist/contracts/adapters/index.js.map +1 -1
- package/dist/contracts/adapters/kyc.d.ts +21 -7
- package/dist/contracts/adapters/kyc.d.ts.map +1 -1
- package/dist/contracts/adapters/kyc.js.map +1 -1
- package/dist/contracts/adapters/rg-limits.d.ts +47 -0
- package/dist/contracts/adapters/rg-limits.d.ts.map +1 -0
- package/dist/contracts/adapters/rg-limits.js +18 -0
- package/dist/contracts/adapters/rg-limits.js.map +1 -0
- package/dist/contracts/adapters/swap.d.ts +75 -0
- package/dist/contracts/adapters/swap.d.ts.map +1 -0
- package/dist/contracts/adapters/swap.js +5 -0
- package/dist/contracts/adapters/swap.js.map +1 -0
- package/dist/contracts/adapters/wallet-commands.d.ts +6 -0
- package/dist/contracts/adapters/wallet-commands.d.ts.map +1 -1
- package/dist/contracts/adapters/wallet-commands.js.map +1 -1
- package/dist/contracts/adapters/wallet-reader.d.ts +12 -0
- package/dist/contracts/adapters/wallet-reader.d.ts.map +1 -1
- package/dist/contracts/adapters/wallet-reader.js.map +1 -1
- package/dist/contracts/schemas/__tests__/events.test.js +89 -1
- package/dist/contracts/schemas/__tests__/events.test.js.map +1 -1
- package/dist/contracts/schemas/chat-command.d.ts +11 -0
- package/dist/contracts/schemas/chat-command.d.ts.map +1 -1
- package/dist/contracts/schemas/chat-command.js +12 -1
- package/dist/contracts/schemas/chat-command.js.map +1 -1
- package/dist/contracts/schemas/common.d.ts +2 -0
- package/dist/contracts/schemas/common.d.ts.map +1 -1
- package/dist/contracts/schemas/common.js +4 -0
- package/dist/contracts/schemas/common.js.map +1 -1
- package/dist/contracts/schemas/compliance.d.ts +22 -0
- package/dist/contracts/schemas/compliance.d.ts.map +1 -1
- package/dist/contracts/schemas/compliance.js +12 -0
- package/dist/contracts/schemas/compliance.js.map +1 -1
- package/dist/contracts/schemas/display-currency.d.ts +14 -0
- package/dist/contracts/schemas/display-currency.d.ts.map +1 -0
- package/dist/contracts/schemas/display-currency.js +45 -0
- package/dist/contracts/schemas/display-currency.js.map +1 -0
- package/dist/contracts/schemas/events.d.ts +165 -0
- package/dist/contracts/schemas/events.d.ts.map +1 -1
- package/dist/contracts/schemas/events.js +56 -10
- package/dist/contracts/schemas/events.js.map +1 -1
- package/dist/contracts/schemas/igaming-config.d.ts +6 -0
- package/dist/contracts/schemas/igaming-config.d.ts.map +1 -1
- package/dist/contracts/schemas/igaming-config.js +7 -0
- package/dist/contracts/schemas/igaming-config.js.map +1 -1
- package/dist/contracts/schemas/index.d.ts +1 -0
- package/dist/contracts/schemas/index.d.ts.map +1 -1
- package/dist/contracts/schemas/index.js +1 -0
- package/dist/contracts/schemas/index.js.map +1 -1
- package/dist/contracts/schemas/platform-config.d.ts +29 -0
- package/dist/contracts/schemas/platform-config.d.ts.map +1 -1
- package/dist/contracts/schemas/platform-config.js +19 -1
- package/dist/contracts/schemas/platform-config.js.map +1 -1
- package/dist/contracts/schemas/player.d.ts +6 -0
- package/dist/contracts/schemas/player.d.ts.map +1 -1
- package/dist/contracts/schemas/player.js +2 -0
- package/dist/contracts/schemas/player.js.map +1 -1
- package/dist/contracts/schemas/wallet-tx.d.ts +5 -1
- package/dist/contracts/schemas/wallet-tx.d.ts.map +1 -1
- package/dist/contracts/schemas/wallet-tx.js +7 -0
- package/dist/contracts/schemas/wallet-tx.js.map +1 -1
- package/dist/engagement/chat/__tests__/chat.service.int.test.js +51 -8
- package/dist/engagement/chat/__tests__/chat.service.int.test.js.map +1 -1
- package/dist/engagement/chat/__tests__/global-chat-room.int.test.js +3 -3
- package/dist/engagement/chat/__tests__/global-chat-room.int.test.js.map +1 -1
- package/dist/engagement/chat/plugin.d.ts +17 -0
- package/dist/engagement/chat/plugin.d.ts.map +1 -1
- package/dist/engagement/chat/service/chat.service.d.ts +30 -10
- package/dist/engagement/chat/service/chat.service.d.ts.map +1 -1
- package/dist/engagement/chat/service/errors/chat-moderation.errors.d.ts +30 -10
- package/dist/engagement/chat/service/errors/chat-moderation.errors.d.ts.map +1 -1
- package/dist/engagement/chat-commands/contract/index.d.ts +12 -12
- package/dist/engagement/chat-commands/contract/index.d.ts.map +1 -1
- package/dist/engagement/chat-commands/contract/index.js +4 -3
- package/dist/engagement/chat-commands/contract/index.js.map +1 -1
- package/dist/engagement/chat-commands/migrate.d.ts +0 -1
- package/dist/engagement/chat-commands/migrate.d.ts.map +1 -1
- package/dist/engagement/chat-commands/migrate.js +0 -2
- package/dist/engagement/chat-commands/migrate.js.map +1 -1
- package/dist/engagement/chat-commands/plugin.d.ts +17 -0
- package/dist/engagement/chat-commands/plugin.d.ts.map +1 -1
- package/dist/engagement/chat-commands/router/index.d.ts +8 -8
- package/dist/engagement/chat-commands/schema/index.d.ts +4 -4
- package/dist/engagement/chat-commands/seed/index.js +3 -3
- package/dist/engagement/chat-commands/seed/index.js.map +1 -1
- package/dist/engagement/chat-commands/service/chat-commands.service.d.ts +5 -3
- package/dist/engagement/chat-commands/service/chat-commands.service.d.ts.map +1 -1
- package/dist/engagement/notifications/__tests__/notifications.router.int.test.js +8 -2
- package/dist/engagement/notifications/__tests__/notifications.router.int.test.js.map +1 -1
- package/dist/engagement/notifications/contract/index.d.ts +11 -3
- package/dist/engagement/notifications/contract/index.d.ts.map +1 -1
- package/dist/engagement/notifications/contract/index.js +2 -0
- package/dist/engagement/notifications/contract/index.js.map +1 -1
- package/dist/engagement/notifications/plugin.d.ts +17 -0
- package/dist/engagement/notifications/plugin.d.ts.map +1 -1
- package/dist/engagement/notifications/plugin.js +24 -1
- package/dist/engagement/notifications/plugin.js.map +1 -1
- package/dist/engagement/notifications/router/index.d.ts +4 -2
- package/dist/engagement/notifications/router/index.d.ts.map +1 -1
- package/dist/engagement/notifications/service/notifications.service.d.ts +6 -2
- package/dist/engagement/notifications/service/notifications.service.d.ts.map +1 -1
- package/dist/engagement/social/plugin.d.ts +17 -0
- package/dist/engagement/social/plugin.d.ts.map +1 -1
- package/dist/engagement/social/service/social.service.d.ts +27 -9
- package/dist/engagement/social/service/social.service.d.ts.map +1 -1
- package/dist/fx/contracts.d.ts +2 -0
- package/dist/fx/contracts.d.ts.map +1 -0
- package/dist/fx/contracts.js +2 -0
- package/dist/fx/contracts.js.map +1 -0
- package/dist/fx/exchange-rate/__tests__/exchange-rate-reader.service.int.test.d.ts +2 -0
- package/dist/fx/exchange-rate/__tests__/exchange-rate-reader.service.int.test.d.ts.map +1 -0
- package/dist/fx/exchange-rate/__tests__/exchange-rate-reader.service.int.test.js +221 -0
- package/dist/fx/exchange-rate/__tests__/exchange-rate-reader.service.int.test.js.map +1 -0
- package/dist/fx/exchange-rate/__tests__/select-provider.test.d.ts +2 -0
- package/dist/fx/exchange-rate/__tests__/select-provider.test.d.ts.map +1 -0
- package/dist/fx/exchange-rate/__tests__/select-provider.test.js +26 -0
- package/dist/fx/exchange-rate/__tests__/select-provider.test.js.map +1 -0
- package/dist/fx/exchange-rate/adapters/exchange-rate-reader.service.d.ts +59 -0
- package/dist/fx/exchange-rate/adapters/exchange-rate-reader.service.d.ts.map +1 -0
- package/dist/fx/exchange-rate/adapters/exchange-rate-reader.service.js +212 -0
- package/dist/fx/exchange-rate/adapters/exchange-rate-reader.service.js.map +1 -0
- package/dist/fx/exchange-rate/contract/index.d.ts +44 -0
- package/dist/fx/exchange-rate/contract/index.d.ts.map +1 -0
- package/dist/fx/exchange-rate/contract/index.js +30 -0
- package/dist/fx/exchange-rate/contract/index.js.map +1 -0
- package/dist/fx/exchange-rate/drizzle/migrations/0000_hot_trauma.sql +10 -0
- package/dist/fx/exchange-rate/drizzle/migrations/meta/0000_snapshot.json +92 -0
- package/dist/fx/exchange-rate/drizzle/migrations/meta/_journal.json +13 -0
- package/dist/fx/exchange-rate/drizzle.config.d.ts +4 -0
- package/dist/fx/exchange-rate/drizzle.config.d.ts.map +1 -0
- package/dist/fx/exchange-rate/drizzle.config.js +18 -0
- package/dist/fx/exchange-rate/drizzle.config.js.map +1 -0
- package/dist/fx/exchange-rate/index.d.ts +3 -0
- package/dist/fx/exchange-rate/index.d.ts.map +1 -0
- package/dist/fx/exchange-rate/index.js +3 -0
- package/dist/fx/exchange-rate/index.js.map +1 -0
- package/dist/fx/exchange-rate/migrate.d.ts +5 -0
- package/dist/fx/exchange-rate/migrate.d.ts.map +1 -0
- package/dist/fx/exchange-rate/migrate.js +14 -0
- package/dist/fx/exchange-rate/migrate.js.map +1 -0
- package/dist/fx/exchange-rate/plugin.d.ts +175 -0
- package/dist/fx/exchange-rate/plugin.d.ts.map +1 -0
- package/dist/fx/exchange-rate/plugin.js +46 -0
- package/dist/fx/exchange-rate/plugin.js.map +1 -0
- package/dist/fx/exchange-rate/react/exchange-rate.d.ts +12 -0
- package/dist/fx/exchange-rate/react/exchange-rate.d.ts.map +1 -0
- package/dist/fx/exchange-rate/react/exchange-rate.js +17 -0
- package/dist/fx/exchange-rate/react/exchange-rate.js.map +1 -0
- package/dist/fx/exchange-rate/router/index.d.ts +22 -0
- package/dist/fx/exchange-rate/router/index.d.ts.map +1 -0
- package/dist/fx/exchange-rate/router/index.js +18 -0
- package/dist/fx/exchange-rate/router/index.js.map +1 -0
- package/dist/fx/exchange-rate/schema/index.d.ts +116 -0
- package/dist/fx/exchange-rate/schema/index.d.ts.map +1 -0
- package/dist/fx/exchange-rate/schema/index.js +18 -0
- package/dist/fx/exchange-rate/schema/index.js.map +1 -0
- package/dist/fx/exchange-rate/service/exchange-rate.service.d.ts +22 -0
- package/dist/fx/exchange-rate/service/exchange-rate.service.d.ts.map +1 -0
- package/dist/fx/exchange-rate/service/exchange-rate.service.js +38 -0
- package/dist/fx/exchange-rate/service/exchange-rate.service.js.map +1 -0
- package/dist/fx/index.d.ts +2 -0
- package/dist/fx/index.d.ts.map +1 -0
- package/dist/fx/index.js +2 -0
- package/dist/fx/index.js.map +1 -0
- package/dist/fx/react.d.ts +2 -0
- package/dist/fx/react.d.ts.map +1 -0
- package/dist/fx/react.js +2 -0
- package/dist/fx/react.js.map +1 -0
- package/dist/fx/server.d.ts +2 -0
- package/dist/fx/server.d.ts.map +1 -0
- package/dist/fx/server.js +2 -0
- package/dist/fx/server.js.map +1 -0
- package/dist/iam/plugin.d.ts +17 -0
- package/dist/iam/plugin.d.ts.map +1 -1
- package/dist/iam/seed/data/default-admin-roles.d.ts.map +1 -1
- package/dist/iam/seed/data/default-admin-roles.js +1 -0
- package/dist/iam/seed/data/default-admin-roles.js.map +1 -1
- package/dist/iam/service/iam.service.d.ts +30 -10
- package/dist/iam/service/iam.service.d.ts.map +1 -1
- package/dist/pam/identity/__tests__/session.service.int.test.js +4 -1
- package/dist/pam/identity/__tests__/session.service.int.test.js.map +1 -1
- package/dist/pam/identity/adapters/mock/mock-kyc-adapter.d.ts +3 -3
- package/dist/pam/identity/adapters/mock/mock-kyc-adapter.d.ts.map +1 -1
- package/dist/pam/identity/adapters/mock/mock-kyc-adapter.js +3 -3
- package/dist/pam/identity/adapters/mock/mock-kyc-adapter.js.map +1 -1
- package/dist/pam/identity/plugin.d.ts +17 -0
- package/dist/pam/identity/plugin.d.ts.map +1 -1
- package/dist/pam/identity/service/admin-security.service.d.ts +3 -1
- package/dist/pam/identity/service/admin-security.service.d.ts.map +1 -1
- package/dist/pam/identity/service/identity.service.d.ts +6 -2
- package/dist/pam/identity/service/identity.service.d.ts.map +1 -1
- package/dist/pam/identity/service/session.service.d.ts +6 -2
- package/dist/pam/identity/service/session.service.d.ts.map +1 -1
- package/dist/pam/identity/service/trusted-device.service.d.ts +3 -1
- package/dist/pam/identity/service/trusted-device.service.d.ts.map +1 -1
- package/dist/pam/player-management/__tests__/kyc-status-writer.int.test.js +10 -23
- package/dist/pam/player-management/__tests__/kyc-status-writer.int.test.js.map +1 -1
- package/dist/pam/player-management/plugin.d.ts +17 -0
- package/dist/pam/player-management/plugin.d.ts.map +1 -1
- package/dist/pam/player-management/plugin.js +1 -1
- package/dist/pam/player-management/plugin.js.map +1 -1
- package/dist/pam/player-management/service/kyc-status-writer.d.ts +5 -8
- package/dist/pam/player-management/service/kyc-status-writer.d.ts.map +1 -1
- package/dist/pam/player-management/service/kyc-status-writer.js +5 -16
- package/dist/pam/player-management/service/kyc-status-writer.js.map +1 -1
- package/dist/pam/player-management/service/player.service.d.ts +3 -1
- package/dist/pam/player-management/service/player.service.d.ts.map +1 -1
- package/dist/pam/player-note/plugin.d.ts +17 -0
- package/dist/pam/player-note/plugin.d.ts.map +1 -1
- package/dist/pam/profile/__tests__/profile.service.int.test.js +90 -2
- package/dist/pam/profile/__tests__/profile.service.int.test.js.map +1 -1
- package/dist/pam/profile/contract/index.d.ts +67 -47
- package/dist/pam/profile/contract/index.d.ts.map +1 -1
- package/dist/pam/profile/contract/index.js +16 -1
- package/dist/pam/profile/contract/index.js.map +1 -1
- package/dist/pam/profile/drizzle/migrations/0004_fuzzy_korvac.sql +1 -0
- package/dist/pam/profile/drizzle/migrations/meta/0004_snapshot.json +230 -0
- package/dist/pam/profile/drizzle/migrations/meta/_journal.json +7 -0
- package/dist/pam/profile/plugin.d.ts +18 -0
- package/dist/pam/profile/plugin.d.ts.map +1 -1
- package/dist/pam/profile/plugin.js +3 -2
- package/dist/pam/profile/plugin.js.map +1 -1
- package/dist/pam/profile/react/profile.d.ts +12 -0
- package/dist/pam/profile/react/profile.d.ts.map +1 -1
- package/dist/pam/profile/react/profile.js +12 -0
- package/dist/pam/profile/react/profile.js.map +1 -1
- package/dist/pam/profile/router/index.d.ts +10 -0
- package/dist/pam/profile/router/index.d.ts.map +1 -1
- package/dist/pam/profile/router/index.js +4 -1
- package/dist/pam/profile/router/index.js.map +1 -1
- package/dist/pam/profile/schema/index.d.ts +17 -0
- package/dist/pam/profile/schema/index.d.ts.map +1 -1
- package/dist/pam/profile/schema/index.js +1 -0
- package/dist/pam/profile/schema/index.js.map +1 -1
- package/dist/pam/profile/service/profile.service.d.ts +18 -4
- package/dist/pam/profile/service/profile.service.d.ts.map +1 -1
- package/dist/pam/profile/service/profile.service.js +75 -5
- package/dist/pam/profile/service/profile.service.js.map +1 -1
- package/dist/pam/react.d.ts +1 -1
- package/dist/pam/react.d.ts.map +1 -1
- package/dist/pam/react.js +1 -1
- package/dist/pam/react.js.map +1 -1
- package/dist/pam/shared/language.d.ts +3 -1
- package/dist/pam/shared/language.d.ts.map +1 -1
- package/dist/pam/tag/__tests__/tag-evaluation.service.int.test.js +46 -2
- package/dist/pam/tag/__tests__/tag-evaluation.service.int.test.js.map +1 -1
- package/dist/pam/tag/plugin.d.ts +17 -0
- package/dist/pam/tag/plugin.d.ts.map +1 -1
- package/dist/pam/tag/service/tag-evaluation.service.d.ts +12 -6
- package/dist/pam/tag/service/tag-evaluation.service.d.ts.map +1 -1
- package/dist/pam/tag/service/tag-evaluation.service.js +33 -15
- package/dist/pam/tag/service/tag-evaluation.service.js.map +1 -1
- package/dist/pam/tag/service/tag-rule.service.d.ts +3 -1
- package/dist/pam/tag/service/tag-rule.service.d.ts.map +1 -1
- package/dist/pam/tag/service/tag.service.d.ts +18 -6
- package/dist/pam/tag/service/tag.service.d.ts.map +1 -1
- package/dist/server/db/__tests__/query-helpers.test.js +50 -4
- package/dist/server/db/__tests__/query-helpers.test.js.map +1 -1
- package/dist/server/db/index.d.ts +1 -1
- package/dist/server/db/index.d.ts.map +1 -1
- package/dist/server/db/index.js +1 -1
- package/dist/server/db/index.js.map +1 -1
- package/dist/server/db/query-helpers.d.ts +3 -0
- package/dist/server/db/query-helpers.d.ts.map +1 -1
- package/dist/server/db/query-helpers.js +23 -0
- package/dist/server/db/query-helpers.js.map +1 -1
- package/dist/server/kernel/__tests__/redis-pubsub-realtime-transport.int.test.d.ts +2 -0
- package/dist/server/kernel/__tests__/redis-pubsub-realtime-transport.int.test.d.ts.map +1 -0
- package/dist/server/kernel/__tests__/redis-pubsub-realtime-transport.int.test.js +218 -0
- package/dist/server/kernel/__tests__/redis-pubsub-realtime-transport.int.test.js.map +1 -0
- package/dist/server/kernel/__tests__/redis-streams-broker.int.test.js +18 -0
- package/dist/server/kernel/__tests__/redis-streams-broker.int.test.js.map +1 -1
- package/dist/server/kernel/domain-error.d.ts +15 -5
- package/dist/server/kernel/domain-error.d.ts.map +1 -1
- package/dist/server/kernel/domain-error.js +4 -2
- package/dist/server/kernel/domain-error.js.map +1 -1
- package/dist/server/kernel/index.d.ts +1 -1
- package/dist/server/kernel/index.d.ts.map +1 -1
- package/dist/server/kernel/index.js +1 -1
- package/dist/server/kernel/index.js.map +1 -1
- package/dist/server/kernel/redis-pubsub-realtime-transport.d.ts +43 -0
- package/dist/server/kernel/redis-pubsub-realtime-transport.d.ts.map +1 -0
- package/dist/server/kernel/redis-pubsub-realtime-transport.js +0 -0
- package/dist/server/kernel/redis-pubsub-realtime-transport.js.map +1 -0
- package/dist/server/kernel/redis-streams-broker.d.ts.map +1 -1
- package/dist/server/kernel/redis-streams-broker.js +1 -6
- package/dist/server/kernel/redis-streams-broker.js.map +1 -1
- package/dist/server/plugin-host/core-plugins.d.ts.map +1 -1
- package/dist/server/plugin-host/core-plugins.js +1 -0
- package/dist/server/plugin-host/core-plugins.js.map +1 -1
- package/dist/server/runtime/assert-durable-seams.d.ts +4 -4
- package/dist/server/runtime/assert-durable-seams.d.ts.map +1 -1
- package/dist/server/runtime/assert-durable-seams.js +6 -1
- package/dist/server/runtime/assert-durable-seams.js.map +1 -1
- package/dist/server/runtime/core-token-catalog.d.ts +17 -0
- package/dist/server/runtime/core-token-catalog.d.ts.map +1 -1
- package/dist/server/runtime/core-token-catalog.js +7 -1
- package/dist/server/runtime/core-token-catalog.js.map +1 -1
- package/dist/server/runtime/create-app.d.ts +9 -8
- package/dist/server/runtime/create-app.d.ts.map +1 -1
- package/dist/server/runtime/create-app.js +16 -18
- package/dist/server/runtime/create-app.js.map +1 -1
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +2 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +9 -1
- package/dist/testing/mock.d.ts.map +1 -1
- package/dist/testing/mock.js +43 -0
- package/dist/testing/mock.js.map +1 -1
- package/dist/testing/real-infra.d.ts.map +1 -1
- package/dist/testing/real-infra.js +26 -0
- package/dist/testing/real-infra.js.map +1 -1
- package/dist/testing/realtime-transport-conformance.d.ts.map +1 -1
- package/dist/testing/realtime-transport-conformance.js +23 -10
- package/dist/testing/realtime-transport-conformance.js.map +1 -1
- package/dist/testing/seed-player.d.ts +1 -0
- package/dist/testing/seed-player.d.ts.map +1 -1
- package/dist/testing/seed-wallet.d.ts +9 -0
- package/dist/testing/seed-wallet.d.ts.map +1 -0
- package/dist/testing/seed-wallet.js +27 -0
- package/dist/testing/seed-wallet.js.map +1 -0
- package/dist/wallet/__tests__/reconciliation.service.int.test.js +41 -12
- package/dist/wallet/__tests__/reconciliation.service.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-address-format.test.d.ts +2 -0
- package/dist/wallet/__tests__/wallet-address-format.test.d.ts.map +1 -0
- package/dist/wallet/__tests__/wallet-address-format.test.js +102 -0
- package/dist/wallet/__tests__/wallet-address-format.test.js.map +1 -0
- package/dist/wallet/__tests__/wallet-asset.router.int.test.js +22 -4
- package/dist/wallet/__tests__/wallet-asset.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-auto-rule.router.int.test.js +3 -3
- package/dist/wallet/__tests__/wallet-auto-rule.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-auto-withdrawal-config.router.int.test.js +3 -3
- package/dist/wallet/__tests__/wallet-auto-withdrawal-config.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-balance-stream.router.int.test.d.ts +2 -0
- package/dist/wallet/__tests__/wallet-balance-stream.router.int.test.d.ts.map +1 -0
- package/dist/wallet/__tests__/wallet-balance-stream.router.int.test.js +38 -0
- package/dist/wallet/__tests__/wallet-balance-stream.router.int.test.js.map +1 -0
- package/dist/wallet/__tests__/wallet-bonus-rollover-config.router.int.test.js +3 -3
- package/dist/wallet/__tests__/wallet-bonus-rollover-config.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-commands.service.int.test.js +86 -0
- package/dist/wallet/__tests__/wallet-commands.service.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-rg-limit-gate.int.test.d.ts +2 -0
- package/dist/wallet/__tests__/wallet-rg-limit-gate.int.test.d.ts.map +1 -0
- package/dist/wallet/__tests__/wallet-rg-limit-gate.int.test.js +93 -0
- package/dist/wallet/__tests__/wallet-rg-limit-gate.int.test.js.map +1 -0
- package/dist/wallet/__tests__/wallet-transaction-direction-backfill.test.js +14 -2
- package/dist/wallet/__tests__/wallet-transaction-direction-backfill.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-webhook.router.int.test.js +3 -3
- package/dist/wallet/__tests__/wallet-webhook.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-withdrawal-address-cap.int.test.d.ts +2 -0
- package/dist/wallet/__tests__/wallet-withdrawal-address-cap.int.test.d.ts.map +1 -0
- package/dist/wallet/__tests__/wallet-withdrawal-address-cap.int.test.js +64 -0
- package/dist/wallet/__tests__/wallet-withdrawal-address-cap.int.test.js.map +1 -0
- package/dist/wallet/__tests__/wallet-withdrawal-address.router.int.test.js +13 -5
- package/dist/wallet/__tests__/wallet-withdrawal-address.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet.router.int.test.js +3 -3
- package/dist/wallet/__tests__/wallet.router.int.test.js.map +1 -1
- package/dist/wallet/adapters/wallet-reader.service.d.ts +2 -1
- package/dist/wallet/adapters/wallet-reader.service.d.ts.map +1 -1
- package/dist/wallet/adapters/wallet-reader.service.js +4 -0
- package/dist/wallet/adapters/wallet-reader.service.js.map +1 -1
- package/dist/wallet/admin-reporting.d.ts +2 -2
- package/dist/wallet/contract/index.d.ts +14 -0
- package/dist/wallet/contract/index.d.ts.map +1 -1
- package/dist/wallet/contract/index.js +51 -23
- package/dist/wallet/contract/index.js.map +1 -1
- package/dist/wallet/drizzle/migrations/0017_workable_wolf_cub.sql +2 -0
- package/dist/wallet/drizzle/migrations/meta/0017_snapshot.json +1945 -0
- package/dist/wallet/drizzle/migrations/meta/_journal.json +7 -0
- package/dist/wallet/plugin.d.ts +17 -0
- package/dist/wallet/plugin.d.ts.map +1 -1
- package/dist/wallet/plugin.js +3 -2
- package/dist/wallet/plugin.js.map +1 -1
- package/dist/wallet/router/index.d.ts +4 -0
- package/dist/wallet/router/index.d.ts.map +1 -1
- package/dist/wallet/router/index.js +10 -4
- package/dist/wallet/router/index.js.map +1 -1
- package/dist/wallet/schema/index.d.ts +3 -3
- package/dist/wallet/schema/index.d.ts.map +1 -1
- package/dist/wallet/service/reconciliation.service.d.ts +9 -3
- package/dist/wallet/service/reconciliation.service.d.ts.map +1 -1
- package/dist/wallet/service/wallet-commands.service.d.ts +12 -6
- package/dist/wallet/service/wallet-commands.service.d.ts.map +1 -1
- package/dist/wallet/service/wallet-commands.service.js +45 -16
- package/dist/wallet/service/wallet-commands.service.js.map +1 -1
- package/dist/wallet/service/wallet.service.d.ts +109 -32
- package/dist/wallet/service/wallet.service.d.ts.map +1 -1
- package/dist/wallet/service/wallet.service.js +158 -67
- package/dist/wallet/service/wallet.service.js.map +1 -1
- package/package.json +41 -1
- package/dist/server/kernel/__tests__/realtime-transport.test.d.ts +0 -2
- package/dist/server/kernel/__tests__/realtime-transport.test.d.ts.map +0 -1
- package/dist/server/kernel/__tests__/realtime-transport.test.js +0 -70
- package/dist/server/kernel/__tests__/realtime-transport.test.js.map +0 -1
- package/dist/server/kernel/realtime-transport.d.ts +0 -21
- package/dist/server/kernel/realtime-transport.d.ts.map +0 -1
- package/dist/server/kernel/realtime-transport.js +0 -136
- package/dist/server/kernel/realtime-transport.js.map +0 -1
- package/dist/wallet/__tests__/wallet-balance-stream.router.test.d.ts +0 -2
- package/dist/wallet/__tests__/wallet-balance-stream.router.test.d.ts.map +0 -1
- package/dist/wallet/__tests__/wallet-balance-stream.router.test.js +0 -31
- package/dist/wallet/__tests__/wallet-balance-stream.router.test.js.map +0 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { and, eq, sql } from 'drizzle-orm';
|
|
3
|
+
import { createTestDb } from '@openora/core/testing';
|
|
4
|
+
import { migrate } from '../migrate.js';
|
|
5
|
+
import { exchangeRateQuote } from '../schema/index.js';
|
|
6
|
+
import { ExchangeRateReaderService, } from '../adapters/exchange-rate-reader.service.js';
|
|
7
|
+
import { mock } from '../../../testing/mock.js';
|
|
8
|
+
let db;
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
db = await createTestDb([migrate]);
|
|
11
|
+
});
|
|
12
|
+
afterAll(async () => {
|
|
13
|
+
await db.drop();
|
|
14
|
+
});
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
await db.drizzle.db.execute(sql `TRUNCATE ${exchangeRateQuote} RESTART IDENTITY CASCADE`);
|
|
17
|
+
});
|
|
18
|
+
/** An ISO timestamp `ageMs` in the past. The reader ages a quote off its provider stamp. */
|
|
19
|
+
function agedIso(ageMs) {
|
|
20
|
+
return new Date(Date.now() - ageMs).toISOString();
|
|
21
|
+
}
|
|
22
|
+
async function seedQuote(base, quote, rate, opts = {}) {
|
|
23
|
+
await db.drizzle.db.insert(exchangeRateQuote).values({
|
|
24
|
+
baseCurrency: base,
|
|
25
|
+
quoteCurrency: quote,
|
|
26
|
+
rate,
|
|
27
|
+
providerAsOf: new Date(opts.asOf ?? agedIso(opts.ageMs ?? 0)),
|
|
28
|
+
updatedAt: new Date(),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function getRow(base, quote) {
|
|
32
|
+
const [row] = await db.drizzle.db
|
|
33
|
+
.select()
|
|
34
|
+
.from(exchangeRateQuote)
|
|
35
|
+
.where(and(eq(exchangeRateQuote.baseCurrency, base), eq(exchangeRateQuote.quoteCurrency, quote)));
|
|
36
|
+
return row ?? null;
|
|
37
|
+
}
|
|
38
|
+
function delayedProvider(rate, delayMs, asOf) {
|
|
39
|
+
const getRate = vi.fn(() => new Promise((resolve) => {
|
|
40
|
+
setTimeout(() => resolve({ rate, asOf: asOf ?? new Date().toISOString() }), delayMs);
|
|
41
|
+
}));
|
|
42
|
+
return mock({ getRate });
|
|
43
|
+
}
|
|
44
|
+
function baseDeps(over = {}) {
|
|
45
|
+
return {
|
|
46
|
+
drizzle: db.drizzle,
|
|
47
|
+
pivot: 'USD',
|
|
48
|
+
freshTtlMs: 200,
|
|
49
|
+
hardMaxAgeMs: 800,
|
|
50
|
+
providerTimeoutMs: 150,
|
|
51
|
+
...over,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function wait(ms) {
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
56
|
+
}
|
|
57
|
+
const SOFT_STALE_AGE_MS = 400;
|
|
58
|
+
const HARD_STALE_AGE_MS = 1_000;
|
|
59
|
+
const PROVIDER_DELAY_PAST_TIMEOUT_MS = 500;
|
|
60
|
+
describe('ExchangeRateReaderService.getRate - identity and cross-pair derivation', () => {
|
|
61
|
+
it('returns the identity quote for the same currency on both sides without touching the table', async () => {
|
|
62
|
+
const reader = new ExchangeRateReaderService(baseDeps());
|
|
63
|
+
const quote = await reader.getRate('EUR', 'EUR');
|
|
64
|
+
expect(quote).not.toBeNull();
|
|
65
|
+
expect(quote?.rate).toBe('1.000000000000000000');
|
|
66
|
+
});
|
|
67
|
+
it('returns null when nothing is stored for either leg (no provider bound)', async () => {
|
|
68
|
+
const reader = new ExchangeRateReaderService(baseDeps());
|
|
69
|
+
expect(await reader.getRate('EUR', 'GBP')).toBeNull();
|
|
70
|
+
});
|
|
71
|
+
it('derives a cross rate as from/pivot ÷ to/pivot from fresh stored legs, with no provider call', async () => {
|
|
72
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000');
|
|
73
|
+
await seedQuote('GBP', 'USD', '1.250000000000000000');
|
|
74
|
+
const fiatProvider = delayedProvider('9.000000000000000000', 0);
|
|
75
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
76
|
+
const quote = await reader.getRate('EUR', 'GBP');
|
|
77
|
+
expect(quote?.rate).toBe('0.880000000000000000');
|
|
78
|
+
expect(fiatProvider.getRate).not.toHaveBeenCalled();
|
|
79
|
+
});
|
|
80
|
+
it('resolves a currency against the pivot directly using the pivot leg shortcut', async () => {
|
|
81
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000');
|
|
82
|
+
const reader = new ExchangeRateReaderService(baseDeps());
|
|
83
|
+
const quote = await reader.getRate('EUR', 'USD');
|
|
84
|
+
expect(quote?.rate).toBe('1.100000000000000000');
|
|
85
|
+
});
|
|
86
|
+
it('convert() scales an amount by the derived rate', async () => {
|
|
87
|
+
await seedQuote('EUR', 'USD', '2.000000000000000000');
|
|
88
|
+
const reader = new ExchangeRateReaderService(baseDeps());
|
|
89
|
+
expect(await reader.convert('10', 'EUR', 'USD')).toBe('20.000000000000000000');
|
|
90
|
+
});
|
|
91
|
+
it('convert() returns null when no rate is available', async () => {
|
|
92
|
+
const reader = new ExchangeRateReaderService(baseDeps());
|
|
93
|
+
expect(await reader.convert('10', 'EUR', 'GBP')).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('ExchangeRateReaderService.getRate - age bands', () => {
|
|
97
|
+
it('fresh: serves the stored quote with no provider call', async () => {
|
|
98
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000', { ageMs: 10 });
|
|
99
|
+
const fiatProvider = delayedProvider('9.000000000000000000', 0);
|
|
100
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
101
|
+
const quote = await reader.getRate('EUR', 'USD');
|
|
102
|
+
expect(quote?.rate).toBe('1.100000000000000000');
|
|
103
|
+
expect(fiatProvider.getRate).not.toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
it('soft-stale: returns the cached value immediately and refreshes in the background', async () => {
|
|
106
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000', { ageMs: SOFT_STALE_AGE_MS });
|
|
107
|
+
const fiatProvider = delayedProvider('1.500000000000000000', 30);
|
|
108
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
109
|
+
const start = Date.now();
|
|
110
|
+
const quote = await reader.getRate('EUR', 'USD');
|
|
111
|
+
const elapsed = Date.now() - start;
|
|
112
|
+
expect(quote?.rate).toBe('1.100000000000000000');
|
|
113
|
+
expect(elapsed).toBeLessThan(30);
|
|
114
|
+
await wait(80);
|
|
115
|
+
expect(fiatProvider.getRate).toHaveBeenCalledTimes(1);
|
|
116
|
+
const row = await getRow('EUR', 'USD');
|
|
117
|
+
expect(row?.rate).toBe('1.500000000000000000');
|
|
118
|
+
});
|
|
119
|
+
it('soft-stale: a background refresh timeout is completely invisible to the caller', async () => {
|
|
120
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000', { ageMs: SOFT_STALE_AGE_MS });
|
|
121
|
+
const fiatProvider = delayedProvider('9.000000000000000000', PROVIDER_DELAY_PAST_TIMEOUT_MS);
|
|
122
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
123
|
+
const quote = await reader.getRate('EUR', 'USD');
|
|
124
|
+
expect(quote?.rate).toBe('1.100000000000000000');
|
|
125
|
+
await wait(200);
|
|
126
|
+
const row = await getRow('EUR', 'USD');
|
|
127
|
+
expect(row?.rate).toBe('1.100000000000000000');
|
|
128
|
+
});
|
|
129
|
+
it('hard-stale: fetches synchronously, persists, and returns the fresh quote', async () => {
|
|
130
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000', { ageMs: HARD_STALE_AGE_MS });
|
|
131
|
+
const providerAsOf = agedIso(0);
|
|
132
|
+
const fiatProvider = delayedProvider('1.300000000000000000', 5, providerAsOf);
|
|
133
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
134
|
+
const quote = await reader.getRate('EUR', 'USD');
|
|
135
|
+
expect(quote).toEqual({ rate: '1.300000000000000000', asOf: providerAsOf });
|
|
136
|
+
expect(fiatProvider.getRate).toHaveBeenCalledTimes(1);
|
|
137
|
+
const row = await getRow('EUR', 'USD');
|
|
138
|
+
expect(row?.rate).toBe('1.300000000000000000');
|
|
139
|
+
});
|
|
140
|
+
it('hard-stale with no row at all: fetches synchronously and persists a first row', async () => {
|
|
141
|
+
const fiatProvider = delayedProvider('1.400000000000000000', 5);
|
|
142
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
143
|
+
const quote = await reader.getRate('EUR', 'USD');
|
|
144
|
+
expect(quote?.rate).toBe('1.400000000000000000');
|
|
145
|
+
expect((await getRow('EUR', 'USD'))?.rate).toBe('1.400000000000000000');
|
|
146
|
+
});
|
|
147
|
+
it('hard-stale + provider failure (throw) returns null and fails closed, without touching a stored row', async () => {
|
|
148
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000', { ageMs: HARD_STALE_AGE_MS });
|
|
149
|
+
const fiatProvider = mock({
|
|
150
|
+
getRate: vi.fn(async () => {
|
|
151
|
+
throw new Error('vendor unreachable');
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
155
|
+
expect(await reader.getRate('EUR', 'USD')).toBeNull();
|
|
156
|
+
expect((await getRow('EUR', 'USD'))?.rate).toBe('1.100000000000000000');
|
|
157
|
+
});
|
|
158
|
+
it('hard-stale + provider returning null returns null and fails closed', async () => {
|
|
159
|
+
const fiatProvider = mock({ getRate: vi.fn(async () => null) });
|
|
160
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
161
|
+
expect(await reader.getRate('EUR', 'USD')).toBeNull();
|
|
162
|
+
});
|
|
163
|
+
it('hard-stale + no provider bound for the rail returns null and fails closed', async () => {
|
|
164
|
+
const reader = new ExchangeRateReaderService(baseDeps());
|
|
165
|
+
expect(await reader.getRate('EUR', 'USD')).toBeNull();
|
|
166
|
+
});
|
|
167
|
+
it('hard-stale: a provider call exceeding providerTimeoutMs returns null promptly (does not hang)', async () => {
|
|
168
|
+
const fiatProvider = delayedProvider('9.000000000000000000', 5_000);
|
|
169
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider, providerTimeoutMs: 30 }));
|
|
170
|
+
const start = Date.now();
|
|
171
|
+
const quote = await reader.getRate('EUR', 'USD');
|
|
172
|
+
const elapsed = Date.now() - start;
|
|
173
|
+
expect(quote).toBeNull();
|
|
174
|
+
expect(elapsed).toBeLessThan(500);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe('ExchangeRateReaderService.getRate - single-flight', () => {
|
|
178
|
+
it('collapses concurrent hard-stale callers for the same leg into one provider call', async () => {
|
|
179
|
+
const providerAsOf = agedIso(0);
|
|
180
|
+
const fiatProvider = delayedProvider('1.200000000000000000', 40, providerAsOf);
|
|
181
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
182
|
+
const [a, b, c] = await Promise.all([
|
|
183
|
+
reader.getRate('EUR', 'USD'),
|
|
184
|
+
reader.getRate('EUR', 'USD'),
|
|
185
|
+
reader.getRate('EUR', 'USD'),
|
|
186
|
+
]);
|
|
187
|
+
expect(a).toEqual({ rate: '1.200000000000000000', asOf: providerAsOf });
|
|
188
|
+
expect(b).toEqual(a);
|
|
189
|
+
expect(c).toEqual(a);
|
|
190
|
+
expect(fiatProvider.getRate).toHaveBeenCalledTimes(1);
|
|
191
|
+
});
|
|
192
|
+
it('a settled in-flight call is removed from the single-flight map so a later call fetches again', async () => {
|
|
193
|
+
const fiatProvider = delayedProvider('1.200000000000000000', 5);
|
|
194
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider, freshTtlMs: 0, hardMaxAgeMs: 50 }));
|
|
195
|
+
await reader.getRate('EUR', 'USD');
|
|
196
|
+
await wait(80);
|
|
197
|
+
await reader.getRate('EUR', 'USD');
|
|
198
|
+
expect(fiatProvider.getRate).toHaveBeenCalledTimes(2);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
describe('ExchangeRateReaderService.getRate - cross-pair freshness', () => {
|
|
202
|
+
it('a derived pair is only as fresh as its staler leg', async () => {
|
|
203
|
+
const stalerAsOf = agedIso(SOFT_STALE_AGE_MS);
|
|
204
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000', { ageMs: 10 });
|
|
205
|
+
await seedQuote('GBP', 'USD', '1.250000000000000000', { asOf: stalerAsOf });
|
|
206
|
+
const fiatProvider = delayedProvider('1.300000000000000000', 30);
|
|
207
|
+
const reader = new ExchangeRateReaderService(baseDeps({ fiatProvider }));
|
|
208
|
+
const quote = await reader.getRate('EUR', 'GBP');
|
|
209
|
+
expect(quote?.rate).toBe('0.880000000000000000');
|
|
210
|
+
expect(quote?.asOf).toBe(stalerAsOf);
|
|
211
|
+
// The staler leg refreshes in the background. Let that write land before the next
|
|
212
|
+
// test truncates, or it lands after the truncate and leaves a fresh GBP/USD row.
|
|
213
|
+
await vi.waitFor(async () => expect((await getRow('GBP', 'USD'))?.rate).toBe('1.300000000000000000'));
|
|
214
|
+
});
|
|
215
|
+
it('returns null when one leg is hard-stale and its fetch fails, even if the other leg is fresh', async () => {
|
|
216
|
+
await seedQuote('EUR', 'USD', '1.100000000000000000', { ageMs: 10 });
|
|
217
|
+
const reader = new ExchangeRateReaderService(baseDeps());
|
|
218
|
+
expect(await reader.getRate('EUR', 'GBP')).toBeNull();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
//# sourceMappingURL=exchange-rate-reader.service.int.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exchange-rate-reader.service.int.test.js","sourceRoot":"","sources":["../../../../src/fx/exchange-rate/__tests__/exchange-rate-reader.service.int.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-provider.test.d.ts","sourceRoot":"","sources":["../../../../src/fx/exchange-rate/__tests__/select-provider.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mock } from '../../../testing/mock.js';
|
|
3
|
+
import { selectProvider } from '../adapters/exchange-rate-reader.service.js';
|
|
4
|
+
const cryptoProvider = mock({});
|
|
5
|
+
const fiatProvider = mock({});
|
|
6
|
+
describe('selectProvider', () => {
|
|
7
|
+
it('routes a crypto currency to the crypto provider via railFor', () => {
|
|
8
|
+
expect(selectProvider('BTC', cryptoProvider, fiatProvider)).toBe(cryptoProvider);
|
|
9
|
+
});
|
|
10
|
+
it('routes a fiat currency to the fiat provider via railFor', () => {
|
|
11
|
+
expect(selectProvider('EUR', cryptoProvider, fiatProvider)).toBe(fiatProvider);
|
|
12
|
+
});
|
|
13
|
+
it('returns undefined when the rail has no provider bound', () => {
|
|
14
|
+
expect(selectProvider('BTC', undefined, fiatProvider)).toBeUndefined();
|
|
15
|
+
expect(selectProvider('EUR', cryptoProvider, undefined)).toBeUndefined();
|
|
16
|
+
});
|
|
17
|
+
it('returns undefined when neither provider is bound', () => {
|
|
18
|
+
expect(selectProvider('BTC', undefined, undefined)).toBeUndefined();
|
|
19
|
+
expect(selectProvider('EUR', undefined, undefined)).toBeUndefined();
|
|
20
|
+
});
|
|
21
|
+
it('respects a custom crypto-currency override list, same as railFor', () => {
|
|
22
|
+
expect(selectProvider('SOL', cryptoProvider, fiatProvider, ['SOL'])).toBe(cryptoProvider);
|
|
23
|
+
expect(selectProvider('BTC', cryptoProvider, fiatProvider, ['SOL'])).toBe(fiatProvider);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=select-provider.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-provider.test.js","sourceRoot":"","sources":["../../../../src/fx/exchange-rate/__tests__/select-provider.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAE7E,MAAM,cAAc,GAAG,IAAI,CAAuB,EAAE,CAAC,CAAC;AACtD,MAAM,YAAY,GAAG,IAAI,CAAuB,EAAE,CAAC,CAAC;AAEpD,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACvE,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACpE,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC1F,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { DrizzleService } from '@openora/core/server';
|
|
2
|
+
import type { ExchangeRateProvider, ExchangeRateQuote, ExchangeRateReader } from '@openora/core/contracts';
|
|
3
|
+
export declare function selectProvider(currency: string, cryptoProvider: ExchangeRateProvider | undefined, fiatProvider: ExchangeRateProvider | undefined, cryptoCurrencies?: readonly string[]): ExchangeRateProvider | undefined;
|
|
4
|
+
export type ExchangeRateReaderServiceDeps = {
|
|
5
|
+
drizzle: DrizzleService;
|
|
6
|
+
pivot: string;
|
|
7
|
+
cryptoProvider?: ExchangeRateProvider;
|
|
8
|
+
fiatProvider?: ExchangeRateProvider;
|
|
9
|
+
cryptoCurrencies?: readonly string[];
|
|
10
|
+
freshTtlMs: number;
|
|
11
|
+
hardMaxAgeMs: number;
|
|
12
|
+
providerTimeoutMs: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Read-through cache in front of the two exchange-rate provider ports. A player
|
|
16
|
+
* request MAY reach a vendor: a hard-stale or missing quote is fetched
|
|
17
|
+
* synchronously (with a short timeout, failing closed to `null` on error) before
|
|
18
|
+
* answering. Three age bands off the quote's own provider timestamp
|
|
19
|
+
* (`exchange_rate_quote.providerAsOf`), never off the local write time - re-persisting
|
|
20
|
+
* an old quote must not make it look fresh:
|
|
21
|
+
* - fresh (age < freshTtlMs): return the stored value, no provider call.
|
|
22
|
+
* - soft-stale (freshTtlMs <= age < hardMaxAgeMs): return the stored value
|
|
23
|
+
* immediately, kick a single-flight background refresh that is invisible to
|
|
24
|
+
* the caller (its failure is logged, never thrown).
|
|
25
|
+
* - hard-stale (age >= hardMaxAgeMs) or no row at all: fetch synchronously
|
|
26
|
+
* through the same single-flight path; on success persist and return; on
|
|
27
|
+
* failure return `null`.
|
|
28
|
+
* Concurrent callers for the same (currency, pivot) leg share one in-flight
|
|
29
|
+
* provider call.
|
|
30
|
+
*/
|
|
31
|
+
export declare class ExchangeRateReaderService implements ExchangeRateReader {
|
|
32
|
+
private readonly drizzle;
|
|
33
|
+
private readonly pivot;
|
|
34
|
+
private readonly cryptoProvider?;
|
|
35
|
+
private readonly fiatProvider?;
|
|
36
|
+
private readonly cryptoCurrencies?;
|
|
37
|
+
private readonly freshTtlMs;
|
|
38
|
+
private readonly hardMaxAgeMs;
|
|
39
|
+
private readonly providerTimeoutMs;
|
|
40
|
+
private readonly inFlight;
|
|
41
|
+
private readonly failedUntil;
|
|
42
|
+
constructor(deps: ExchangeRateReaderServiceDeps);
|
|
43
|
+
getRate(from: string, to: string): Promise<ExchangeRateQuote | null>;
|
|
44
|
+
convert(amount: string, from: string, to: string): Promise<string | null>;
|
|
45
|
+
private resolveLeg;
|
|
46
|
+
/** Records a cooldown, evicting expired entries first so the map cannot grow without bound. */
|
|
47
|
+
private rememberFailure;
|
|
48
|
+
private readRow;
|
|
49
|
+
private fetchLeg;
|
|
50
|
+
private fetchFromProvider;
|
|
51
|
+
/**
|
|
52
|
+
* A rate is money arithmetic: a zero, negative, absurd or unparseable one would silently
|
|
53
|
+
* convert a wager to nothing and walk it past an RG limit, and a timestamp we cannot trust
|
|
54
|
+
* would age wrong. Refuse before persisting, so the bad value never enters the cache.
|
|
55
|
+
*/
|
|
56
|
+
private assertUsableQuote;
|
|
57
|
+
private persist;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=exchange-rate-reader.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exchange-rate-reader.service.d.ts","sourceRoot":"","sources":["../../../../src/fx/exchange-rate/adapters/exchange-rate-reader.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA2C,MAAM,sBAAsB,CAAC;AAC/F,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAcjC,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,oBAAoB,GAAG,SAAS,EAChD,YAAY,EAAE,oBAAoB,GAAG,SAAS,EAC9C,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,GACnC,oBAAoB,GAAG,SAAS,CAElC;AAoBD,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,yBAA0B,YAAW,kBAAkB;IAClE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAuB;IACvD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAuB;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiD;IAC1E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;gBAE7C,IAAI,EAAE,6BAA6B;IAWzC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAoBpE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAQjE,UAAU;IA4CxB,+FAA+F;IAC/F,OAAO,CAAC,eAAe;YAiBT,OAAO;IAgBrB,OAAO,CAAC,QAAQ;YAgBF,iBAAiB;IAyB/B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;YAmBX,OAAO;CAUtB"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { moneyDivide, moneyScaleBy, createLogger } from '@openora/core/server';
|
|
2
|
+
import { railFor } from '@openora/core/contracts';
|
|
3
|
+
import { and, eq } from 'drizzle-orm';
|
|
4
|
+
import { exchangeRateQuote } from '../schema/index.js';
|
|
5
|
+
const logger = createLogger('exchange-rate-reader');
|
|
6
|
+
/** Tolerated forward clock skew between the provider's `asOf` and this process. */
|
|
7
|
+
const MAX_PROVIDER_CLOCK_SKEW_MS = 60_000;
|
|
8
|
+
/** Upper bound on a plausible pivot-denominated rate; anything beyond is a provider defect. */
|
|
9
|
+
const MAX_PLAUSIBLE_RATE = 1e12;
|
|
10
|
+
/** Hard cap on the failure-cooldown map, so an unbounded code space cannot grow it. */
|
|
11
|
+
const MAX_FAILURE_ENTRIES = 512;
|
|
12
|
+
export function selectProvider(currency, cryptoProvider, fiatProvider, cryptoCurrencies) {
|
|
13
|
+
return railFor(currency, cryptoCurrencies) === 'crypto' ? cryptoProvider : fiatProvider;
|
|
14
|
+
}
|
|
15
|
+
function withTimeout(promise, timeoutMs, label) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const timer = setTimeout(() => {
|
|
18
|
+
reject(new Error(`exchange rate provider timed out fetching ${label}`));
|
|
19
|
+
}, timeoutMs);
|
|
20
|
+
promise.then((value) => {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
resolve(value);
|
|
23
|
+
}, (err) => {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Read-through cache in front of the two exchange-rate provider ports. A player
|
|
31
|
+
* request MAY reach a vendor: a hard-stale or missing quote is fetched
|
|
32
|
+
* synchronously (with a short timeout, failing closed to `null` on error) before
|
|
33
|
+
* answering. Three age bands off the quote's own provider timestamp
|
|
34
|
+
* (`exchange_rate_quote.providerAsOf`), never off the local write time - re-persisting
|
|
35
|
+
* an old quote must not make it look fresh:
|
|
36
|
+
* - fresh (age < freshTtlMs): return the stored value, no provider call.
|
|
37
|
+
* - soft-stale (freshTtlMs <= age < hardMaxAgeMs): return the stored value
|
|
38
|
+
* immediately, kick a single-flight background refresh that is invisible to
|
|
39
|
+
* the caller (its failure is logged, never thrown).
|
|
40
|
+
* - hard-stale (age >= hardMaxAgeMs) or no row at all: fetch synchronously
|
|
41
|
+
* through the same single-flight path; on success persist and return; on
|
|
42
|
+
* failure return `null`.
|
|
43
|
+
* Concurrent callers for the same (currency, pivot) leg share one in-flight
|
|
44
|
+
* provider call.
|
|
45
|
+
*/
|
|
46
|
+
export class ExchangeRateReaderService {
|
|
47
|
+
drizzle;
|
|
48
|
+
pivot;
|
|
49
|
+
cryptoProvider;
|
|
50
|
+
fiatProvider;
|
|
51
|
+
cryptoCurrencies;
|
|
52
|
+
freshTtlMs;
|
|
53
|
+
hardMaxAgeMs;
|
|
54
|
+
providerTimeoutMs;
|
|
55
|
+
inFlight = new Map();
|
|
56
|
+
failedUntil = new Map();
|
|
57
|
+
constructor(deps) {
|
|
58
|
+
this.drizzle = deps.drizzle;
|
|
59
|
+
this.pivot = deps.pivot.toUpperCase();
|
|
60
|
+
this.cryptoProvider = deps.cryptoProvider;
|
|
61
|
+
this.fiatProvider = deps.fiatProvider;
|
|
62
|
+
this.cryptoCurrencies = deps.cryptoCurrencies;
|
|
63
|
+
this.freshTtlMs = deps.freshTtlMs;
|
|
64
|
+
this.hardMaxAgeMs = deps.hardMaxAgeMs;
|
|
65
|
+
this.providerTimeoutMs = deps.providerTimeoutMs;
|
|
66
|
+
}
|
|
67
|
+
async getRate(from, to) {
|
|
68
|
+
const fromCode = from.toUpperCase();
|
|
69
|
+
const toCode = to.toUpperCase();
|
|
70
|
+
if (fromCode === toCode) {
|
|
71
|
+
return { rate: '1.000000000000000000', asOf: new Date().toISOString() };
|
|
72
|
+
}
|
|
73
|
+
const [fromLeg, toLeg] = await Promise.all([
|
|
74
|
+
this.resolveLeg(fromCode),
|
|
75
|
+
this.resolveLeg(toCode),
|
|
76
|
+
]);
|
|
77
|
+
if (!fromLeg || !toLeg) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const rate = moneyDivide(fromLeg.rate, toLeg.rate);
|
|
81
|
+
const asOf = fromLeg.asOf < toLeg.asOf ? fromLeg.asOf : toLeg.asOf;
|
|
82
|
+
return { rate, asOf };
|
|
83
|
+
}
|
|
84
|
+
async convert(amount, from, to) {
|
|
85
|
+
const quote = await this.getRate(from, to);
|
|
86
|
+
if (!quote) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return moneyScaleBy(amount, quote.rate);
|
|
90
|
+
}
|
|
91
|
+
async resolveLeg(currency) {
|
|
92
|
+
if (currency === this.pivot) {
|
|
93
|
+
return { rate: '1.000000000000000000', asOf: new Date().toISOString() };
|
|
94
|
+
}
|
|
95
|
+
const row = await this.readRow(currency);
|
|
96
|
+
// A clock ahead of ours only ever makes a quote look newer, never older, so clamp at 0.
|
|
97
|
+
const ageMs = row
|
|
98
|
+
? Math.max(0, Date.now() - row.providerAsOf.getTime())
|
|
99
|
+
: Number.POSITIVE_INFINITY;
|
|
100
|
+
if (row && ageMs < this.freshTtlMs) {
|
|
101
|
+
return { rate: row.rate, asOf: row.providerAsOf.toISOString() };
|
|
102
|
+
}
|
|
103
|
+
if (row && ageMs < this.hardMaxAgeMs) {
|
|
104
|
+
void this.fetchLeg(currency).catch((err) => {
|
|
105
|
+
logger.warn({ err, currency, pivot: this.pivot }, 'background exchange rate refresh failed');
|
|
106
|
+
});
|
|
107
|
+
return { rate: row.rate, asOf: row.providerAsOf.toISOString() };
|
|
108
|
+
}
|
|
109
|
+
const cooldownUntil = this.failedUntil.get(currency);
|
|
110
|
+
if (cooldownUntil !== undefined && Date.now() < cooldownUntil) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const quote = await this.fetchLeg(currency);
|
|
115
|
+
this.failedUntil.delete(currency);
|
|
116
|
+
return quote;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
this.rememberFailure(currency);
|
|
120
|
+
logger.error({ err, currency, pivot: this.pivot, hadRow: row !== null }, 'exchange rate hard-stale and unavailable; failing closed');
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Records a cooldown, evicting expired entries first so the map cannot grow without bound. */
|
|
125
|
+
rememberFailure(currency) {
|
|
126
|
+
const now = Date.now();
|
|
127
|
+
for (const [key, until] of this.failedUntil) {
|
|
128
|
+
if (until <= now) {
|
|
129
|
+
this.failedUntil.delete(key);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
while (this.failedUntil.size >= MAX_FAILURE_ENTRIES) {
|
|
133
|
+
const [oldest] = this.failedUntil.keys();
|
|
134
|
+
if (oldest === undefined) {
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
this.failedUntil.delete(oldest);
|
|
138
|
+
}
|
|
139
|
+
this.failedUntil.set(currency, now + this.providerTimeoutMs);
|
|
140
|
+
}
|
|
141
|
+
async readRow(currency) {
|
|
142
|
+
const [row] = await this.drizzle.db
|
|
143
|
+
.select({
|
|
144
|
+
rate: exchangeRateQuote.rate,
|
|
145
|
+
providerAsOf: exchangeRateQuote.providerAsOf,
|
|
146
|
+
})
|
|
147
|
+
.from(exchangeRateQuote)
|
|
148
|
+
.where(and(eq(exchangeRateQuote.baseCurrency, currency), eq(exchangeRateQuote.quoteCurrency, this.pivot)));
|
|
149
|
+
return row ?? null;
|
|
150
|
+
}
|
|
151
|
+
fetchLeg(currency) {
|
|
152
|
+
const key = `${currency}:${this.pivot}`;
|
|
153
|
+
const existing = this.inFlight.get(key);
|
|
154
|
+
if (existing) {
|
|
155
|
+
return existing;
|
|
156
|
+
}
|
|
157
|
+
const attempt = this.fetchFromProvider(currency).finally(() => {
|
|
158
|
+
if (this.inFlight.get(key) === attempt) {
|
|
159
|
+
this.inFlight.delete(key);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
this.inFlight.set(key, attempt);
|
|
163
|
+
return attempt;
|
|
164
|
+
}
|
|
165
|
+
async fetchFromProvider(currency) {
|
|
166
|
+
const provider = selectProvider(currency, this.cryptoProvider, this.fiatProvider, this.cryptoCurrencies);
|
|
167
|
+
if (!provider) {
|
|
168
|
+
throw new Error(`no exchange rate provider bound for ${currency}/${this.pivot}`);
|
|
169
|
+
}
|
|
170
|
+
const quote = await withTimeout(provider.getRate(currency, this.pivot), this.providerTimeoutMs, `${currency}/${this.pivot}`);
|
|
171
|
+
if (!quote) {
|
|
172
|
+
throw new Error(`exchange rate provider returned no quote for ${currency}/${this.pivot}`);
|
|
173
|
+
}
|
|
174
|
+
this.assertUsableQuote(currency, quote);
|
|
175
|
+
await this.persist(currency, quote);
|
|
176
|
+
return quote;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* A rate is money arithmetic: a zero, negative, absurd or unparseable one would silently
|
|
180
|
+
* convert a wager to nothing and walk it past an RG limit, and a timestamp we cannot trust
|
|
181
|
+
* would age wrong. Refuse before persisting, so the bad value never enters the cache.
|
|
182
|
+
*/
|
|
183
|
+
assertUsableQuote(currency, quote) {
|
|
184
|
+
const pair = `${currency}/${this.pivot}`;
|
|
185
|
+
const rate = Number(quote.rate);
|
|
186
|
+
if (!Number.isFinite(rate) || rate <= 0 || rate > MAX_PLAUSIBLE_RATE) {
|
|
187
|
+
throw new Error(`exchange rate provider returned an out-of-range rate for ${pair}`);
|
|
188
|
+
}
|
|
189
|
+
const asOf = new Date(quote.asOf).getTime();
|
|
190
|
+
if (Number.isNaN(asOf)) {
|
|
191
|
+
throw new Error(`exchange rate provider returned an unparseable timestamp for ${pair}`);
|
|
192
|
+
}
|
|
193
|
+
const now = Date.now();
|
|
194
|
+
if (asOf > now + MAX_PROVIDER_CLOCK_SKEW_MS) {
|
|
195
|
+
throw new Error(`exchange rate provider returned a future timestamp for ${pair}`);
|
|
196
|
+
}
|
|
197
|
+
if (now - asOf >= this.hardMaxAgeMs) {
|
|
198
|
+
throw new Error(`exchange rate provider returned an already hard-stale quote for ${pair}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async persist(base, value) {
|
|
202
|
+
const providerAsOf = new Date(value.asOf);
|
|
203
|
+
await this.drizzle.db
|
|
204
|
+
.insert(exchangeRateQuote)
|
|
205
|
+
.values({ baseCurrency: base, quoteCurrency: this.pivot, rate: value.rate, providerAsOf })
|
|
206
|
+
.onConflictDoUpdate({
|
|
207
|
+
target: [exchangeRateQuote.baseCurrency, exchangeRateQuote.quoteCurrency],
|
|
208
|
+
set: { rate: value.rate, providerAsOf, updatedAt: new Date() },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=exchange-rate-reader.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exchange-rate-reader.service.js","sourceRoot":"","sources":["../../../../src/fx/exchange-rate/adapters/exchange-rate-reader.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
export declare const ExchangeRateQuoteSchema: z.ZodObject<{
|
|
3
|
+
rate: z.ZodString;
|
|
4
|
+
asOf: z.ZodISODateTime;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
export type ExchangeRateQuoteDto = z.infer<typeof ExchangeRateQuoteSchema>;
|
|
7
|
+
export declare const GetExchangeRateInputSchema: z.ZodObject<{
|
|
8
|
+
from: z.ZodString;
|
|
9
|
+
to: z.ZodString;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type GetExchangeRateInput = z.infer<typeof GetExchangeRateInputSchema>;
|
|
12
|
+
export declare const GetExchangeRatesInputSchema: z.ZodObject<{
|
|
13
|
+
to: z.ZodString;
|
|
14
|
+
from: z.ZodArray<z.ZodString>;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
export type GetExchangeRatesInput = z.infer<typeof GetExchangeRatesInputSchema>;
|
|
17
|
+
export declare const ExchangeRateBatchEntrySchema: z.ZodObject<{
|
|
18
|
+
from: z.ZodString;
|
|
19
|
+
quote: z.ZodNullable<z.ZodObject<{
|
|
20
|
+
rate: z.ZodString;
|
|
21
|
+
asOf: z.ZodISODateTime;
|
|
22
|
+
}, z.core.$strip>>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
export type ExchangeRateBatchEntry = z.infer<typeof ExchangeRateBatchEntrySchema>;
|
|
25
|
+
export declare const exchangeRateContract: {
|
|
26
|
+
getRate: import("@orpc/contract").ContractProcedureBuilderWithInputOutput<z.ZodObject<{
|
|
27
|
+
from: z.ZodString;
|
|
28
|
+
to: z.ZodString;
|
|
29
|
+
}, z.core.$strip>, z.ZodNullable<z.ZodObject<{
|
|
30
|
+
rate: z.ZodString;
|
|
31
|
+
asOf: z.ZodISODateTime;
|
|
32
|
+
}, z.core.$strip>>, Record<never, never>, Record<never, never>>;
|
|
33
|
+
getRates: import("@orpc/contract").ContractProcedureBuilderWithInputOutput<z.ZodObject<{
|
|
34
|
+
to: z.ZodString;
|
|
35
|
+
from: z.ZodArray<z.ZodString>;
|
|
36
|
+
}, z.core.$strip>, z.ZodArray<z.ZodObject<{
|
|
37
|
+
from: z.ZodString;
|
|
38
|
+
quote: z.ZodNullable<z.ZodObject<{
|
|
39
|
+
rate: z.ZodString;
|
|
40
|
+
asOf: z.ZodISODateTime;
|
|
41
|
+
}, z.core.$strip>>;
|
|
42
|
+
}, z.core.$strip>>, Record<never, never>, Record<never, never>>;
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/fx/exchange-rate/contract/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAQzB,eAAO,MAAM,uBAAuB;;;iBAGlC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE3E,eAAO,MAAM,0BAA0B;;;iBAGrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,2BAA2B;;;iBAGtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,eAAO,MAAM,4BAA4B;;;;;;iBAGvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;CAUhC,CAAC"}
|