From 623401f70bc2fa646d327c472e0207e9280efc62 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:01 -0400 Subject: [PATCH 01/43] ENG-297 feat(bridge): add core Bridge integration --- dev/config/base-config.yaml | 12 + docs/bridge-integration/API.md | 199 ++++++ docs/bridge-integration/ARCHITECTURE.md | 67 ++ docs/bridge-integration/FLOWS.md | 133 ++++ docs/bridge-integration/WEBHOOKS.md | 65 ++ package.json | 9 +- src/app/accounts/mark-account-for-deletion.ts | 15 +- src/app/errors.ts | 2 + src/app/offers/Validator.ts | 11 +- src/app/wallets/get-balance-for-wallet.ts | 19 +- src/config/schema.ts | 35 ++ src/config/schema.types.d.ts | 28 +- src/config/yaml.ts | 42 +- src/domain/accounts/index.types.d.ts | 21 +- src/domain/primitives/bridge.ts | 23 + src/domain/shared/MoneyAmount.ts | 78 ++- src/domain/shared/primitives.ts | 1 + src/graphql/error-map.ts | 59 +- src/graphql/public/mutations.ts | 8 + src/graphql/public/queries.ts | 12 +- .../mutation/bridge-add-external-account.ts | 36 ++ .../mutation/bridge-create-virtual-account.ts | 36 ++ .../root/mutation/bridge-initiate-kyc.ts | 36 ++ .../mutation/bridge-initiate-withdrawal.ts | 53 ++ .../root/query/bridge-external-accounts.ts | 26 + .../public/root/query/bridge-kyc-status.ts | 25 + .../root/query/bridge-virtual-account.ts | 26 + .../public/root/query/bridge-withdrawals.ts | 26 + src/graphql/public/types/index.ts | 2 + .../types/object/bridge-external-account.ts | 13 + .../public/types/object/bridge-kyc-link.ts | 11 + .../types/object/bridge-virtual-account.ts | 13 + .../public/types/object/bridge-withdrawal.ts | 14 + src/graphql/shared/types/object/usd-wallet.ts | 10 +- .../shared/types/object/usdt-wallet.ts | 140 +++++ .../shared/types/scalar/wallet-currency.ts | 1 + src/servers/bridge-webhook-server.ts | 8 + src/servers/graphql-admin-server.ts | 37 +- src/servers/graphql-server.ts | 2 +- src/servers/plugins/complexity.ts | 45 ++ src/services/bridge/client.ts | 367 +++++++++++ src/services/bridge/errors.ts | 89 +++ src/services/bridge/index.ts | 585 ++++++++++++++++++ src/services/bridge/index.types.d.ts | 121 ++++ src/services/bridge/webhook-server/index.ts | 44 ++ .../middleware/verify-signature.ts | 67 ++ .../bridge/webhook-server/routes/deposit.ts | 49 ++ .../bridge/webhook-server/routes/kyc.ts | 80 +++ .../bridge/webhook-server/routes/transfer.ts | 88 +++ src/services/ibex/client.ts | 309 ++++++--- src/services/ibex/types.ts | 48 +- src/services/ibex/webhook-server/index.ts | 8 +- .../webhook-server/routes/crypto-receive.ts | 104 ++++ .../ibex/webhook-server/routes/index.ts | 3 +- src/services/mongoose/accounts.ts | 51 ++ src/services/mongoose/bridge-accounts.ts | 138 +++++ src/services/mongoose/schema.ts | 91 ++- src/services/mongoose/schema.types.d.ts | 5 + yarn.lock | 363 ++++++++++- 59 files changed, 3797 insertions(+), 212 deletions(-) create mode 100644 docs/bridge-integration/API.md create mode 100644 docs/bridge-integration/ARCHITECTURE.md create mode 100644 docs/bridge-integration/FLOWS.md create mode 100644 docs/bridge-integration/WEBHOOKS.md create mode 100644 src/domain/primitives/bridge.ts create mode 100644 src/graphql/public/root/mutation/bridge-add-external-account.ts create mode 100644 src/graphql/public/root/mutation/bridge-create-virtual-account.ts create mode 100644 src/graphql/public/root/mutation/bridge-initiate-kyc.ts create mode 100644 src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts create mode 100644 src/graphql/public/root/query/bridge-external-accounts.ts create mode 100644 src/graphql/public/root/query/bridge-kyc-status.ts create mode 100644 src/graphql/public/root/query/bridge-virtual-account.ts create mode 100644 src/graphql/public/root/query/bridge-withdrawals.ts create mode 100644 src/graphql/public/types/object/bridge-external-account.ts create mode 100644 src/graphql/public/types/object/bridge-kyc-link.ts create mode 100644 src/graphql/public/types/object/bridge-virtual-account.ts create mode 100644 src/graphql/public/types/object/bridge-withdrawal.ts create mode 100644 src/graphql/shared/types/object/usdt-wallet.ts create mode 100644 src/servers/bridge-webhook-server.ts create mode 100644 src/servers/plugins/complexity.ts create mode 100644 src/services/bridge/client.ts create mode 100644 src/services/bridge/errors.ts create mode 100644 src/services/bridge/index.ts create mode 100644 src/services/bridge/index.types.d.ts create mode 100644 src/services/bridge/webhook-server/index.ts create mode 100644 src/services/bridge/webhook-server/middleware/verify-signature.ts create mode 100644 src/services/bridge/webhook-server/routes/deposit.ts create mode 100644 src/services/bridge/webhook-server/routes/kyc.ts create mode 100644 src/services/bridge/webhook-server/routes/transfer.ts create mode 100644 src/services/ibex/webhook-server/routes/crypto-receive.ts create mode 100644 src/services/mongoose/bridge-accounts.ts diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index e6618350d..c19cbc19c 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -12,6 +12,18 @@ ibex: port: 4008 secret: "not-so-secret" +bridge: + enabled: false + apiKey: "" + baseUrl: "https://api.bridge.xyz" + webhook: + port: 4009 + publicKeys: + kyc: "" + deposit: "" + transfer: "" + timestampSkewMs: 300000 + # See Foreign Exchange Rates: https://www.firstglobal-bank.com/ exchangeRates: USD: diff --git a/docs/bridge-integration/API.md b/docs/bridge-integration/API.md new file mode 100644 index 000000000..2bd24e5fb --- /dev/null +++ b/docs/bridge-integration/API.md @@ -0,0 +1,199 @@ +# Bridge.xyz GraphQL API Reference + +All Bridge-related operations require the user to be authenticated and have an **Account Level 2** or higher. + +## Mutations + +### `bridgeInitiateKyc` + +Starts the KYC process for the authenticated user. + +**Request:** +```graphql +mutation BridgeInitiateKyc { + bridgeInitiateKyc { + errors { + message + } + kycLink { + kycLink + tosLink + } + } +} +``` + +**Response:** +- `kycLink`: URL to the Bridge/Persona KYC flow. +- `tosLink`: URL to the Bridge Terms of Service. + +--- + +### `bridgeCreateVirtualAccount` + +Creates a virtual bank account for the user to receive USD deposits. Requires approved KYC. + +**Request:** +```graphql +mutation BridgeCreateVirtualAccount { + bridgeCreateVirtualAccount { + errors { + message + } + virtualAccount { + bridgeVirtualAccountId + bankName + routingNumber + accountNumberLast4 + } + } +} +``` + +**Response:** +- `bridgeVirtualAccountId`: Unique identifier for the virtual account. +- `bankName`: Name of the bank (e.g., "Bridge Bank"). +- `routingNumber`: ABA routing number. +- `accountNumberLast4`: Last 4 digits of the account number. + +--- + +### `bridgeAddExternalAccount` + +Returns a hosted URL for the user to link their external bank account (via Plaid/Bridge). + +**Request:** +```graphql +mutation BridgeAddExternalAccount { + bridgeAddExternalAccount { + errors { + message + } + externalAccount { + linkUrl + expiresAt + } + } +} +``` + +**Response:** +- `linkUrl`: URL to the bank linking flow. +- `expiresAt`: Expiration timestamp for the link. + +--- + +### `bridgeInitiateWithdrawal` + +Initiates a withdrawal from the user's USDT balance to a linked external bank account. + +**Request:** +```graphql +mutation BridgeInitiateWithdrawal($input: BridgeInitiateWithdrawalInput!) { + bridgeInitiateWithdrawal(input: $input) { + errors { + message + } + withdrawal { + transferId + amount + currency + state + } + } +} +``` + +**Input:** +- `amount`: String representation of the amount (e.g., "100.00"). +- `externalAccountId`: The ID of the linked bank account. + +**Response:** +- `transferId`: Unique identifier for the transfer. +- `state`: Current state of the transfer (e.g., "pending", "processing"). + +--- + +## Queries + +### `bridgeKycStatus` + +Returns the current KYC status for the user. + +**Request:** +```graphql +query BridgeKycStatus { + bridgeKycStatus +} +``` + +**Possible Values:** +- `"pending"`: KYC is in progress. +- `"approved"`: KYC is complete and approved. +- `"rejected"`: KYC was rejected. +- `null`: KYC has not been initiated. + +--- + +### `bridgeVirtualAccount` + +Returns the user's virtual account details if one exists. + +**Request:** +```graphql +query BridgeVirtualAccount { + bridgeVirtualAccount { + bridgeVirtualAccountId + bankName + routingNumber + accountNumberLast4 + } +} +``` + +--- + +### `bridgeExternalAccounts` + +Lists all linked external bank accounts. + +**Request:** +```graphql +query BridgeExternalAccounts { + bridgeExternalAccounts { + bridgeExternalAccountId + bankName + accountNumberLast4 + status + } +} +``` + +--- + +### `bridgeWithdrawals` + +Lists the user's withdrawal history. + +**Request:** +```graphql +query BridgeWithdrawals { + bridgeWithdrawals { + transferId + amount + currency + state + createdAt + } +} +``` + +## Error Codes + +| Code | Description | +| --- | --- | +| `BRIDGE_DISABLED` | Bridge integration is disabled in configuration. | +| `BRIDGE_ACCOUNT_LEVEL_ERROR` | User account level is below 2. | +| `BRIDGE_KYC_PENDING` | Operation requires approved KYC, but it is still pending. | +| `BRIDGE_KYC_REJECTED` | KYC was rejected. | +| `BRIDGE_CUSTOMER_NOT_FOUND` | Bridge customer record not found for the user. | diff --git a/docs/bridge-integration/ARCHITECTURE.md b/docs/bridge-integration/ARCHITECTURE.md new file mode 100644 index 000000000..0ec665227 --- /dev/null +++ b/docs/bridge-integration/ARCHITECTURE.md @@ -0,0 +1,67 @@ +# Bridge.xyz Integration Architecture + +## System Overview + +The Bridge.xyz integration enables USD on-ramp and off-ramp functionality for Flash users. It allows users to convert between USD (via bank transfers) and USDT (on the Tron network), which is then integrated into the Flash ecosystem via IBEX. + +## Component Architecture + +The integration consists of three main components: + +1. **Flash Backend**: The core service that orchestrates the flow between users, Bridge.xyz, and IBEX. It exposes a GraphQL API for the mobile app and handles webhooks from Bridge.xyz. +2. **Bridge.xyz API**: An external service that provides virtual bank accounts, KYC processing, and USD/USDT conversion. +3. **IBEX**: An external service used by Flash to manage Bitcoin and Lightning wallets, and in this context, to provide Tron USDT receive addresses and handle USDT deposits. + +### Component Diagram + +```ascii ++-------------+ GraphQL +----------------+ +| Mobile App | <-----------------> | Flash Backend | ++-------------+ +----------------+ + ^ ^ + | | + | API | API + v v + +------------+ +------------+ + | Bridge.xyz | | IBEX | + +------------+ +------------+ + | | + | USD/USDT | USDT + v v + +----------------------------+ + | Tron Network | + +----------------------------+ +``` + +## Data Flow + +### On-Ramp (USD -> USDT) + +1. **KYC**: User initiates KYC via Flash, which creates a Bridge customer and returns a KYC link (Persona). +2. **Virtual Account**: Once KYC is approved, Flash creates a Tron USDT address via IBEX and a Bridge virtual account pointing to that address. +3. **Deposit**: User sends USD to the virtual account. +4. **Conversion**: Bridge converts USD to USDT and sends it to the Tron address. +5. **Credit**: IBEX detects the USDT deposit and notifies Flash via webhook, which credits the user's wallet. + +### Off-Ramp (USDT -> USD) + +1. **Link Bank**: User links an external bank account via Bridge's hosted UI. +2. **Withdrawal**: User initiates a withdrawal in Flash. +3. **Transfer**: Flash creates a Bridge transfer from the user's Tron address to the linked bank account. +4. **Conversion**: Bridge converts USDT to USD and sends it to the bank via ACH. + +## Technology Stack + +- **Language**: TypeScript +- **Runtime**: Node.js +- **API**: GraphQL (Apollo Server) +- **Database**: MongoDB (Mongoose) for storing Bridge account mappings and transfer states. +- **Communication**: REST API (Bridge.xyz), Webhooks. + +## Security Model + +- **Account Level**: Bridge functionality is restricted to users with Account Level 2 or higher. +- **KYC**: All users must pass Bridge's KYC process (powered by Persona). +- **Webhook Verification**: All incoming webhooks from Bridge.xyz are verified using asymmetric RSA-SHA256 signatures. +- **Idempotency**: All critical API calls to Bridge include an `Idempotency-Key` to prevent duplicate transactions. +- **Data Isolation**: Bridge customer IDs and account details are mapped to Flash internal account IDs. diff --git a/docs/bridge-integration/FLOWS.md b/docs/bridge-integration/FLOWS.md new file mode 100644 index 000000000..9b76cffe8 --- /dev/null +++ b/docs/bridge-integration/FLOWS.md @@ -0,0 +1,133 @@ +# Bridge.xyz Integration Flows + +This document describes the step-by-step flows for USD on-ramp and off-ramp using Bridge.xyz and IBEX. + +## On-Ramp Flow (USD -> USDT) + +This flow allows users to deposit USD from their bank account and receive USDT in their Flash wallet. + +### Sequence Diagram + +```ascii +User Flash App Flash Backend Bridge.xyz IBEX + | | | | | + | 1. Start KYC | | | | + |----------------->| 2. bridgeInitKyc | | | + | |------------------>| 3. Create Customer | | + | | |-------------------->| | + | | | 4. Create KYC Link | | + | | |-------------------->| | + | | 5. KYC Link | | | + | |<------------------| | | + | 6. Complete KYC | | | | + |----------------->| | | | + | (Persona Flow) | | | | + | | | 7. kyc.approved | | + | | |<--------------------| | + | | | 8. Create Tron Addr | | + | | |---------------------------------------->| + | | | 9. Create Virt Acc | | + | | |-------------------->| | + | 10. View Bank Det| | | | + |<-----------------| | | | + | 11. Transfer USD | | | | + |----------------------------------------------------------->| | + | | | | 12. Convert USD | + | | | | 13. Send USDT | + | | | |------------------>| + | | | | | + | | | 14. Crypto Webhook | | + | | |<----------------------------------------| + | | 15. Notify User | | | + |<-----------------| | | | +``` + +### Steps + +1. **Initiate KYC**: User clicks "Deposit USD" in the app. +2. **GraphQL Mutation**: App calls `bridgeInitiateKyc`. +3. **Bridge Customer**: Flash creates a Bridge customer if one doesn't exist. +4. **KYC Link**: Flash requests a KYC link from Bridge. +5. **Redirect**: App opens the KYC link (Persona). +6. **Verification**: User completes identity verification. +7. **KYC Webhook**: Bridge sends `kyc.approved` webhook to Flash. +8. **Tron Address**: Flash requests a unique Tron USDT receive address from IBEX. +9. **Virtual Account**: Flash creates a Bridge virtual account linked to the Tron address. +10. **Display Details**: User sees bank name, routing number, and account number in the app. +11. **Bank Transfer**: User initiates a transfer from their banking app. +12. **Conversion**: Bridge receives USD and converts it to USDT. +13. **Settlement**: Bridge sends USDT to the user's Tron address. +14. **IBEX Webhook**: IBEX detects the incoming USDT and notifies Flash. +15. **Credit**: Flash credits the user's USDT wallet and sends a push notification. + +--- + +## Off-Ramp Flow (USDT -> USD) + +This flow allows users to withdraw USDT from their Flash wallet to their external bank account. + +### Sequence Diagram + +```ascii +User Flash App Flash Backend Bridge.xyz Bank + | (Check for KYC, if complete, skip to 12. | | + | 1. Start KYC | | | | + |----------------->| 2. bridgeInitKyc | | | + | |------------------>| 3. Create Customer | | + | | |-------------------->| | + | | | 4. Create KYC Link | | + | | |-------------------->| | + | | 5. KYC Link | | | + | |<------------------| | | + | 6. Complete KYC | | | | + |----------------->| | | | + | (Persona Flow) | | | | + | | | 7. kyc.approved | | + | | |<--------------------| | + | 8. Link Bank | | | | + |----------------->| 9. bridgeAddExtAcc| | | + | |------------------>| 10. Get Link URL | | + | | |-------------------->| | + | | 11. Link URL | | | + | |<------------------| | | + | 12. Auth Bank | | | | + |----------------->| | | | + | (Plaid Flow) | | | | + | | | 13. ext_acc.verified| | + | | |<--------------------| | + | 14. Withdraw | | | | + |----------------->| 15. bridgeInitWith| | | + | |------------------>| 16. Create Transfer | | + | | |-------------------->| | + | | 17. Pending | | | + |<-----------------| | | | + | | | | 18. Convert USDT | + | | | | 19. Send ACH | + | | | |------------------>| + | | | 20. trans.completed | | + | | |<--------------------| | + | 21. Funds Arrive | | | | + |<-------------------------------------------------------------------------------| +``` + +### Steps + +1. **Link Bank**: User chooses to add a bank account. +2. **GraphQL Mutation**: App calls `bridgeAddExternalAccount`. +3. **Link URL**: Flash requests a hosted link URL from Bridge. +4. **Redirect**: App opens the Bridge/Plaid flow. +5. **Authentication**: User logs into their bank and selects an account. +6. **Verification Webhook**: Bridge notifies Flash when the external account is verified. +7. **Initiate Withdrawal**: User enters amount and selects the linked bank account. +8. **GraphQL Mutation**: App calls `bridgeInitiateWithdrawal`. +9. **Bridge Transfer**: Flash creates a transfer in Bridge from the user's Tron address to the external account. +10. **Confirmation**: App shows the withdrawal as "Pending". +11. **Conversion**: Bridge converts USDT from the user's balance to USD. +12. **ACH Transfer**: Bridge sends USD to the user's bank via ACH. +13. **Transfer Webhook**: Bridge sends `transfer.completed` webhook to Flash. +14. **Completion**: User receives funds in their bank account (usually 1-3 business days). + +## Fee Structure + +- **Bridge Fees**: Bridge.xyz charges fees for conversion and transfers (see Bridge.xyz documentation for current rates). +- **Flash Fee**: Flash charges a **0.5%** service fee on all Bridge transactions, which is included in the total amount shown to the user. diff --git a/docs/bridge-integration/WEBHOOKS.md b/docs/bridge-integration/WEBHOOKS.md new file mode 100644 index 000000000..bd129d808 --- /dev/null +++ b/docs/bridge-integration/WEBHOOKS.md @@ -0,0 +1,65 @@ +# Bridge.xyz Webhook Handling + +Flash receives real-time updates from Bridge.xyz via webhooks. These webhooks are used to update KYC status, confirm deposits, and track withdrawal progress. + +## Webhook Endpoint + +The webhook server listens on the configured port (default: `3005`) and expects POST requests at the following endpoints: + +- `POST /bridge/webhooks/kyc` +- `POST /bridge/webhooks/deposit` +- `POST /bridge/webhooks/transfer` + +## Signature Verification + +All incoming webhooks from Bridge.xyz are signed using asymmetric RSA-SHA256. Flash verifies these signatures using the public keys provided by Bridge.xyz. + +### Verification Process + +1. Retrieve the signature from the `Bridge-Signature` header. +2. Retrieve the timestamp from the `Bridge-Timestamp` header. +3. Verify that the timestamp is within the allowed skew (default: 5 minutes) to prevent replay attacks. +4. Construct the signed payload by concatenating the timestamp and the raw request body: `timestamp + "." + rawBody`. +5. Verify the signature against the signed payload using the appropriate public key (KYC, Deposit, or Transfer). + +## Event Types + +### KYC Events + +#### `kyc.approved` +Sent when a user's KYC application is approved. +- **Action**: Update user's `bridgeKycStatus` to `approved`. + +#### `kyc.rejected` +Sent when a user's KYC application is rejected. +- **Action**: Update user's `bridgeKycStatus` to `rejected`. + +### Deposit Events + +#### `deposit.completed` +Sent when a USD deposit to a virtual account is successfully converted to USDT and sent to the destination Tron address. +- **Action**: This event is primarily for tracking. The actual crediting of the user's wallet is handled by the IBEX webhook when the USDT arrives. + +### Transfer Events + +#### `transfer.completed` +Sent when an off-ramp transfer (USDT -> USD) is successfully completed. +- **Action**: Update the withdrawal record status to `completed` and notify the user. + +#### `transfer.failed` +Sent when an off-ramp transfer fails. +- **Action**: Update the withdrawal record status to `failed`, record the reason, and notify the user. + +## Idempotency Handling + +Each webhook event includes a unique `id`. Flash ensures that each event is processed exactly once by: + +1. Checking if the event ID has already been processed in the database. +2. Using a distributed lock (Redis) during processing to prevent race conditions from duplicate deliveries. + +## Response Codes + +- `200 OK`: Event successfully processed or already processed. +- `400 Bad Request`: Invalid payload or missing headers. +- `401 Unauthorized`: Signature verification failed. +- `500 Internal Server Error`: Temporary failure; Bridge.xyz will retry the webhook. diff --git a/package.json b/package.json index 700257594..831e4e618 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "watch-main": ". ./.env && nodemon -V -e ts,graphql -w ./src --exec yarn run start-main", "start-main": ". ./.env && yarn run build && node --inspect -r ./lib/services/tracing.js ./lib/servers/graphql-main-server.js", "migrate-mongo-up": "migrate-mongo up -f './src/migrations/migrate-mongo-config.js'", - "gen-test-jwt": "ts-node ./dev/bin/gen-test-jwt.ts" + "gen-test-jwt": "ts-node ./dev/bin/gen-test-jwt.ts", + "bridge-webhook": "ts-node src/servers/bridge-webhook-server.ts" }, "engines": { "node": ">=20.18.1 <21" @@ -38,6 +39,7 @@ "@google-cloud/storage": "^7.1.0", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", + "@lnflash/bridge-mcp": "github:lnflash/bridge-mcp", "@opentelemetry/api": "^1.6.0", "@opentelemetry/core": "^1.17.0", "@opentelemetry/exporter-trace-otlp-http": "^0.43.0", @@ -82,7 +84,6 @@ "graphql": "^16.8.0", "graphql-middleware": "^6.1.33", "graphql-query-complexity": "^0.12.0", - "graphql-query-complexity-apollo-plugin": "^1.0.2", "graphql-redis-subscriptions": "^2.6.0", "graphql-relay": "^0.10.0", "graphql-shield": "^7.6.4", @@ -177,11 +178,11 @@ "eslint-plugin-jest": "^27.4.0", "eslint-plugin-prettier": "^5.0.0", "graphql-subscriptions": "^2.0.0", - "grpc-tools": "^1.12.4", "grpc_tools_node_protoc_ts": "^5.3.3", + "grpc-tools": "^1.12.4", "jest": "^29.7.0", - "jest-junit": "^16.0.0", "jest_workaround": "^0.79.19", + "jest-junit": "^16.0.0", "lodash.difference": "^4.5.0", "lodash.find": "^4.6.0", "madge": "^6.1.0", diff --git a/src/app/accounts/mark-account-for-deletion.ts b/src/app/accounts/mark-account-for-deletion.ts index d8207eeeb..6e3619988 100644 --- a/src/app/accounts/mark-account-for-deletion.ts +++ b/src/app/accounts/mark-account-for-deletion.ts @@ -25,17 +25,26 @@ export const markAccountForDeletion = async ({ if (wallets instanceof Error) return wallets for (const wallet of wallets) { - const balance = await getBalanceForWallet({ walletId: wallet.id }) + const balance = await getBalanceForWallet({ + walletId: wallet.id, + currency: wallet.currency, + }) if (balance instanceof Error) return balance - if (balance.isGreaterThan(USDAmount.ZERO) && cancelIfPositiveBalance) { + if ( + balance instanceof USDAmount && + balance.isGreaterThan(USDAmount.ZERO) && + cancelIfPositiveBalance + ) { return new AccountHasPositiveBalanceError( `The new phone is associated with an account with a non empty wallet. walletId: ${wallet.id}, balance: ${balance}, accountId: ${account.id}, cancelIfPositiveBalance: ${cancelIfPositiveBalance}`, ) } + const balanceDisplay = + balance instanceof USDAmount ? balance.asDollars() : balance.asNumber() addEventToCurrentSpan(`deleting_wallet`, { walletId: wallet.id, currency: wallet.currency, - balance: balance.asDollars(), + balance: balanceDisplay, }) } diff --git a/src/app/errors.ts b/src/app/errors.ts index 89f68e8ae..6e747daf6 100644 --- a/src/app/errors.ts +++ b/src/app/errors.ts @@ -27,6 +27,7 @@ import * as SvixErrors from "@services/svix/errors" import * as IbexErrors from "@services/ibex/errors" import * as ErpNextErrors from "@services/frappe/errors" +import * as BridgeErrors from "@services/bridge/errors" export const ApplicationErrors = { ...SharedErrors, @@ -59,4 +60,5 @@ export const ApplicationErrors = { // Flash Errors ...IbexErrors, ...ErpNextErrors, + ...BridgeErrors, } as const diff --git a/src/app/offers/Validator.ts b/src/app/offers/Validator.ts index 316e7531b..680e6538a 100644 --- a/src/app/offers/Validator.ts +++ b/src/app/offers/Validator.ts @@ -1,7 +1,7 @@ import { getBalanceForWallet } from "@app/wallets"; import { Cashout } from "@config"; import { AccountValidator, hasErpParty, isActiveAccount, walletBelongsToAccount } from "@domain/accounts"; -import { JMDAmount, USDAmount, ValidationError, ValidationFn, validator } from "@domain/shared"; +import { JMDAmount, USDAmount, USDTAmount, ValidationError, ValidationFn, validator } from "@domain/shared"; import { ValidationInputs } from "./types"; import ErpNext from "@services/frappe/ErpNext"; @@ -36,9 +36,14 @@ const isUsd = async (o: ValidationInputs) => { } const hasSufficientBalance = async (o: ValidationInputs): Promise => { - const balance = await getBalanceForWallet({ walletId: o.wallet.id }) + const balance = await getBalanceForWallet({ + walletId: o.wallet.id, + currency: o.wallet.currency, + }) if (balance instanceof Error) return new ValidationError(balance) + if (balance instanceof USDTAmount) + return new ValidationError("Cash out only supports withdrawals from USD wallets") else if (o.payment.amount.isGreaterThan(balance)) return new ValidationError("Transfer amount is greater than wallet balance.") else return true @@ -74,4 +79,4 @@ export const CashoutValidator = validator([ hasErpParty, verifyBankAccount, // TODO daily/weekly/monthly volume limits -]) \ No newline at end of file +]) diff --git a/src/app/wallets/get-balance-for-wallet.ts b/src/app/wallets/get-balance-for-wallet.ts index 7a7ce6128..30aaff7a4 100644 --- a/src/app/wallets/get-balance-for-wallet.ts +++ b/src/app/wallets/get-balance-for-wallet.ts @@ -1,15 +1,26 @@ import { UnknownLedgerError } from "@domain/ledger" -import { USDAmount } from "@domain/shared" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" import Ibex from "@services/ibex/client" import { IbexError, UnexpectedIbexResponse } from "@services/ibex/errors" export const getBalanceForWallet = async ({ walletId, + currency, }: { walletId: WalletId -}): Promise => { - // return LedgerService().getWalletBalance(walletId) - try { + currency?: WalletCurrency +}): Promise => { + try { + if (currency === WalletCurrency.Usdt) { + const resp = await Ibex.getCryptoReceiveBalance(walletId) + if (resp instanceof IbexError) { + if (resp.httpCode === 404) return USDTAmount.ZERO + return resp + } + if (resp === undefined) return new UnexpectedIbexResponse("Balance not found") + return resp + } + const resp = await Ibex.getAccountDetails(walletId) if (resp instanceof IbexError) { if (resp.httpCode === 404) return USDAmount.ZERO diff --git a/src/config/schema.ts b/src/config/schema.ts index fb28b6833..479bc0081 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -642,6 +642,32 @@ export const configSchema = { }, }, }, + bridge: { + type: "object", + properties: { + enabled: { type: "boolean" }, + apiKey: { type: "string" }, + baseUrl: { type: "string" }, + webhook: { + type: "object", + properties: { + port: { type: "integer" }, + publicKeys: { + type: "object", + properties: { + kyc: { type: "string" }, + deposit: { type: "string" }, + transfer: { type: "string" }, + }, + required: ["kyc", "deposit", "transfer"], + }, + timestampSkewMs: { type: "integer" }, + }, + required: ["port", "publicKeys", "timestampSkewMs"], + }, + }, + required: ["enabled", "apiKey", "baseUrl", "webhook"], + }, exchangeRates: { type: "object", }, @@ -659,6 +685,14 @@ export const configSchema = { properties: { url: { type: "string" }, credentials: { type: "object" }, + erpnext: { + type: "object", + properties: { + accounts: { + type: "object", + }, + }, + }, }, required: ["url", "credentials"], }, @@ -711,6 +745,7 @@ export const configSchema = { "exchangeRates", "cashout", "ibex", + "bridge", ], additionalProperties: false, } as const diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index 3c8619c26..bd04a4e9c 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -29,6 +29,25 @@ type IbexConfig = { webhook: WebhookServer } +type BridgeWebhookPublicKeys = { + kyc: string + deposit: string + transfer: string +} + +type BridgeWebhook = { + port: number + publicKeys: BridgeWebhookPublicKeys + timestampSkewMs: number +} + +type BridgeConfig = { + enabled: boolean + apiKey: string + baseUrl: string + webhook: BridgeWebhook +} + type CashoutEmail = { to: string from: string @@ -171,7 +190,8 @@ type YamlSchema = { skipFeeProbeConfig: { pubkey: string[]; chanId: string[] } smsAuthUnsupportedCountries: string[] whatsAppAuthUnsupportedCountries: string[] - ibex: IbexConfig, + ibex: IbexConfig + bridge: BridgeConfig exchangeRates: StaticRates cashout: { enabled: boolean @@ -211,12 +231,12 @@ type FrappeConfig = { type CurrencyCode = string type PriceSpread = { - bid: number, - ask: number, + bid: number + ask: number } type StaticRates = { [key: CurrencyCode]: { [key: CurrencyCode]: PriceSpread } -} \ No newline at end of file +} diff --git a/src/config/yaml.ts b/src/config/yaml.ts index 0ac041840..136300441 100644 --- a/src/config/yaml.ts +++ b/src/config/yaml.ts @@ -24,44 +24,43 @@ import mergeWith from "lodash.mergewith" import { configSchema } from "./schema" import { ConfigError } from "./error" -import yargs from "yargs"; +import yargs from "yargs" -const argv: any = yargs(process.argv.slice(2)) - .option("configPath", { +const argv: any = + // .help() + yargs(process.argv.slice(2)).option("configPath", { alias: "c", type: "array", description: "Paths to YAML configuration files", demandOption: true, - }) - // .help() - .argv; + }).argv // replaces array with override const merge = (defaultConfig: unknown, customConfig: unknown) => mergeWith(defaultConfig, customConfig, (a, b) => (Array.isArray(b) ? b : undefined)) export const mergeYamls = (filePaths: string[]): Record => { - const mergedConfig: Record = {}; + const mergedConfig: Record = {} filePaths.forEach((filePath) => { try { - const resolvedPath = path.resolve(filePath); - const fileContent = fs.readFileSync(resolvedPath, "utf8"); - const parsedConfig = yaml.load(fileContent) as Record; + const resolvedPath = path.resolve(filePath) + const fileContent = fs.readFileSync(resolvedPath, "utf8") + const parsedConfig = yaml.load(fileContent) as Record merge(mergedConfig, parsedConfig) - baseLogger.info(`Successfully loaded config from ${resolvedPath}`); + baseLogger.info(`Successfully loaded config from ${resolvedPath}`) } catch (err) { - baseLogger.warn({ err, filePath }, `Failed to load config from ${filePath}`); + baseLogger.warn({ err, filePath }, `Failed to load config from ${filePath}`) } - }); + }) - return mergedConfig; -}; + return mergedConfig +} -const paths = argv.configPath.map((p: string) => path.resolve(p)) -const yamlConfigInit = mergeYamls(paths) +const paths = argv.configPath.map((p: string) => path.resolve(p)) +const yamlConfigInit = mergeYamls(paths) // TODO: fix errors // const ajv = new Ajv({ allErrors: true, strict: "log" }) @@ -359,7 +358,7 @@ const { ask } = yamlConfig.exchangeRates["USD"]["JMD"] const sellRate = JMDAmount.dollars(ask) if (sellRate instanceof BigIntConversionError) throw sellRate export const ExchangeRates = { - jmd: { sell: sellRate } + jmd: { sell: sellRate }, } export const Cashout = { @@ -384,12 +383,13 @@ export const Cashout = { to: yamlConfig.cashout.email.to, from: yamlConfig.cashout.email.from, subject: yamlConfig.cashout.email.subject, - } - + }, } export const SendGridConfig = yamlConfig.sendgrid as SendGridConfig export const IbexConfig = yamlConfig.ibex as IbexConfig -export const FrappeConfig = yamlConfig.frappe as FrappeConfig \ No newline at end of file +export const BridgeConfig = yamlConfig.bridge as BridgeConfig + +export const FrappeConfig = yamlConfig.frappe as FrappeConfig diff --git a/src/domain/accounts/index.types.d.ts b/src/domain/accounts/index.types.d.ts index 1f4de02b8..c2c11bd2a 100644 --- a/src/domain/accounts/index.types.d.ts +++ b/src/domain/accounts/index.types.d.ts @@ -1,5 +1,7 @@ type AccountError = import("./errors").AccountError +type BridgeCustomerId = import("@domain/primitives/bridge").BridgeCustomerId + type CurrencyRatio = number & { readonly brand: unique symbol } type AccountLevel = (typeof import("./index").AccountLevel)[keyof typeof import("./index").AccountLevel] @@ -84,6 +86,10 @@ type Account = { // temp role?: string erpParty?: string // Lookup key to Customer in ERPNext. Required for Account level > 1 + // Bridge integration: + bridgeCustomerId?: BridgeCustomerId + bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeTronAddress?: string } // deprecated @@ -152,7 +158,7 @@ type AccountLimitsVolumes = type AccountValidator = { isActive(): true | ValidationError - isLevel(accountLevel: number): true | ValidationError + isLevel(accountLevel: number): true | ValidationError validateWalletForAccount(wallet: Wallet): true | ValidationError } @@ -168,6 +174,19 @@ interface IAccountsRepository { // listBusinessesForMap(): Promise findByNpub(npub: Npub): Promise update(account: Account): Promise + + updateBridgeFields( + id: AccountId, + fields: { + bridgeCustomerId?: BridgeCustomerId + bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeTronAddress?: string + }, + ): Promise + + findByBridgeTronAddress(address: string): Promise + + findByBridgeCustomerId(customerId: BridgeCustomerId): Promise } type AdminRole = "dealer" | "funder" | "bankowner" | "editor" diff --git a/src/domain/primitives/bridge.ts b/src/domain/primitives/bridge.ts new file mode 100644 index 000000000..00305d3d7 --- /dev/null +++ b/src/domain/primitives/bridge.ts @@ -0,0 +1,23 @@ +// Bridge domain primitives - branded ID types for type safety + +export type BridgeCustomerId = string & { readonly brand: unique symbol } +export type BridgeVirtualAccountId = string & { readonly brand: unique symbol } +export type BridgeExternalAccountId = string & { readonly brand: unique symbol } +export type BridgeTransferId = string & { readonly brand: unique symbol } + +// Helper functions to create branded IDs +export const toBridgeCustomerId = (id: string): BridgeCustomerId => { + return id as BridgeCustomerId +} + +export const toBridgeVirtualAccountId = (id: string): BridgeVirtualAccountId => { + return id as BridgeVirtualAccountId +} + +export const toBridgeExternalAccountId = (id: string): BridgeExternalAccountId => { + return id as BridgeExternalAccountId +} + +export const toBridgeTransferId = (id: string): BridgeTransferId => { + return id as BridgeTransferId +} diff --git a/src/domain/shared/MoneyAmount.ts b/src/domain/shared/MoneyAmount.ts index 694bc0dc1..8818c20aa 100644 --- a/src/domain/shared/MoneyAmount.ts +++ b/src/domain/shared/MoneyAmount.ts @@ -60,6 +60,7 @@ export abstract class MoneyAmount { static from(amount: number | string, currency: WalletCurrency): MoneyAmount | Error { if (currency === WalletCurrency.Usd) return USDAmount.cents(amount.toString()) else if (currency === WalletCurrency.Jmd) return JMDAmount.cents(amount.toString()) + else if (currency === WalletCurrency.Usdt) return USDTAmount.smallestUnits(amount.toString()) else return new UnsupportedCurrencyError(`Could not read currency: ${currency}`) } } @@ -75,7 +76,9 @@ export class USDAmount extends MoneyAmount { try { return new USDAmount(cents) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } } @@ -87,9 +90,10 @@ export class USDAmount extends MoneyAmount { if (cents instanceof BigIntConversionError) return cents // should never happen return new USDAmount(cents.money.multiply(dollarAmt).toFixed(2)) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } - } static ZERO = new USDAmount(0) @@ -102,12 +106,12 @@ export class USDAmount extends MoneyAmount { return this.money.divide(100).toFixed(precision) } - // const jmdLiability = { - // amount: BigInt(usdLiability.asCents()) * exchangeRate / 100n, - // currency: "JMD", - // } + // const jmdLiability = { + // amount: BigInt(usdLiability.asCents()) * exchangeRate / 100n, + // currency: "JMD", + // } // Rate is the ratio at which one currency can be exchanged for another. - // T:USD + // T:USD convertAtRate(rate: T): T { const converted = rate.money.multiply(this.money).divide(100) return rate.getInstance(converted) @@ -133,7 +137,9 @@ export class JMDAmount extends MoneyAmount { try { return new JMDAmount(c) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } } @@ -141,9 +147,10 @@ export class JMDAmount extends MoneyAmount { try { return new JMDAmount(BigInt(d) * 100n) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } - } asCents(precision: number = 0): string { @@ -182,3 +189,52 @@ export class BtcAmount extends MoneyAmount { return new BtcAmount(amount) as this } } + +export class USDTAmount extends MoneyAmount { + static currencyId: IbexCurrencyId = 4 as IbexCurrencyId + + private constructor(amount: Money | bigint | string | number) { + super(amount, WalletCurrency.Usdt) + } + + static smallestUnits(units: string | bigint): USDTAmount | BigIntConversionError { + try { + return new USDTAmount(units) + } catch (error) { + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) + } + } + + static fromNumber(d: number | string): USDTAmount | BigIntConversionError { + try { + const usdtAmt = new Money(d.toString(), "USDT", Round.HALF_TO_EVEN) + const multiplier = USDTAmount.smallestUnits(1_000_000n) + if (multiplier instanceof BigIntConversionError) return multiplier + return new USDTAmount(multiplier.money.multiply(usdtAmt).toFixed(0)) + } catch (error) { + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) + } + } + + static ZERO = new USDTAmount(0) + + asSmallestUnits(precision: number = 0): string { + return this.money.toFixed(precision) + } + + asNumber(precision: number = 6): string { + return this.money.divide(1_000_000).toFixed(precision) + } + + toIbex(): number { + return Number(this.asNumber(8)) + } + + getInstance(amount: Money): this { + return new USDTAmount(amount) as this + } +} diff --git a/src/domain/shared/primitives.ts b/src/domain/shared/primitives.ts index 4bc41e1d2..8a23e05c1 100644 --- a/src/domain/shared/primitives.ts +++ b/src/domain/shared/primitives.ts @@ -5,6 +5,7 @@ export const WalletCurrency = { Usd: "USD", Jmd: "JMD", Btc: "BTC", + Usdt: "USDT", } as const export const ExchangeCurrencyUnit = { diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 2af9f8ffc..67183ead0 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -471,10 +471,52 @@ export const mapError = (error: ApplicationError): CustomApolloError => { case "UnexpectedIbexResponse": return new IbexError(baseLogger) case "OfferNotFound": - return new NotFoundError({ - message: "Offer not available. Try again.", - logger: baseLogger + return new NotFoundError({ + message: "Offer not available. Try again.", + logger: baseLogger, }) + + case "BridgeDisabledError": + message = "Bridge integration is currently disabled" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeAccountLevelError": + message = "Bridge requires Pro account (Level 2+)" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeKycPendingError": + message = "KYC verification is pending" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeKycRejectedError": + message = "KYC verification was rejected" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeCustomerNotFoundError": + message = "Bridge customer not found" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeInsufficientFundsError": + message = "Insufficient funds for withdrawal" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeRateLimitError": + message = "Rate limit exceeded, please try again later" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeTimeoutError": + message = "Request timed out" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeWebhookValidationError": + message = "Invalid webhook signature" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeApiError": + case "BridgeError": + message = error.message || "Bridge API error" + return new UnknownClientError({ message, logger: baseLogger }) + // ---------- // Unhandled below here // ---------- @@ -745,18 +787,19 @@ export const mapError = (error: ApplicationError): CustomApolloError => { } // Move to CustomApolloError class? -export const apolloErrorResponse = (e: CustomApolloError): { errors: IError[] } => { +export const apolloErrorResponse = (e: CustomApolloError): { errors: IError[] } => { return { errors: [ { message: e.message, path: e.path, - code: e.extensions.code - } + code: e.extensions.code, + }, ] } } + export const mapAndParseErrorForGqlResponse = (err: ApplicationError): IError => { const mappedError = mapError(err) return { @@ -767,7 +810,7 @@ export const mapAndParseErrorForGqlResponse = (err: ApplicationError): IError => } export const mapToGqlErrorList = (err: ApplicationError): IError[] => { - if (err instanceof ValidationError && err.errors) return err.errors.map(mapAndParseErrorForGqlResponse) + if (err instanceof ValidationError && err.errors) + return err.errors.map(mapAndParseErrorForGqlResponse) else return [mapAndParseErrorForGqlResponse(err)] - } diff --git a/src/graphql/public/mutations.ts b/src/graphql/public/mutations.ts index 8e0ccc102..8469c5266 100644 --- a/src/graphql/public/mutations.ts +++ b/src/graphql/public/mutations.ts @@ -61,6 +61,10 @@ import RequestCashoutMutation from "./root/mutation/offers/request-cash-out" import InitiateCashoutMutation from "./root/mutation/offers/initiate-cash-out" import IdDocumentUploadUrlGenerateMutation from "./root/mutation/id-document-upload-url-generate" import UpdateExternalWalletMutation from "./root/mutation/update-external-wallet" +import BridgeInitiateKycMutation from "./root/mutation/bridge-initiate-kyc" +import BridgeCreateVirtualAccountMutation from "./root/mutation/bridge-create-virtual-account" +import BridgeAddExternalAccountMutation from "./root/mutation/bridge-add-external-account" +import BridgeInitiateWithdrawalMutation from "./root/mutation/bridge-initiate-withdrawal" // TODO: // const fields: { [key: string]: GraphQLFieldConfig } export const mutationFields = { @@ -115,6 +119,10 @@ export const mutationFields = { idDocumentUploadUrlGenerate: IdDocumentUploadUrlGenerateMutation, updateExternalWallet: UpdateExternalWalletMutation, + bridgeInitiateKyc: BridgeInitiateKycMutation, + bridgeCreateVirtualAccount: BridgeCreateVirtualAccountMutation, + bridgeAddExternalAccount: BridgeAddExternalAccountMutation, + bridgeInitiateWithdrawal: BridgeInitiateWithdrawalMutation, }, atWalletLevel: { diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index d30d84a32..2d95858b7 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -19,8 +19,12 @@ import LnInvoicePaymentStatusQuery from "@graphql/public/root/query/ln-invoice-p import NpubByUserNameQuery from "./root/query/username-npub-query" import IsFlashNpubQuery from "./root/query/is-flash-npub-query" import TransactionDetailsQuery from "./root/query/transaction-details" -import LatestAccountUpgradeRequestQuery from "./root/query/account-upgrade-request" +import AccountUpgradeRequestQuery from "./root/query/account-upgrade-request" import SupportedBanksQuery from "./root/query/supported-banks" +import BridgeKycStatusQuery from "./root/query/bridge-kyc-status" +import BridgeVirtualAccountQuery from "./root/query/bridge-virtual-account" +import BridgeExternalAccountsQuery from "./root/query/bridge-external-accounts" +import BridgeWithdrawalsQuery from "./root/query/bridge-withdrawals" export const queryFields = { unauthed: { @@ -44,7 +48,11 @@ export const queryFields = { atAccountLevel: { me: MeQuery, transactionDetails: TransactionDetailsQuery, - latestAccountUpgradeRequest: LatestAccountUpgradeRequestQuery, + accountUpgradeRequest: AccountUpgradeRequestQuery, + bridgeKycStatus: BridgeKycStatusQuery, + bridgeVirtualAccount: BridgeVirtualAccountQuery, + bridgeExternalAccounts: BridgeExternalAccountsQuery, + bridgeWithdrawals: BridgeWithdrawalsQuery, }, atWalletLevel: { onChainTxFee: OnChainTxFeeQuery, diff --git a/src/graphql/public/root/mutation/bridge-add-external-account.ts b/src/graphql/public/root/mutation/bridge-add-external-account.ts new file mode 100644 index 000000000..10e4e279b --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-add-external-account.ts @@ -0,0 +1,36 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeExternalAccount from "@graphql/public/types/object/bridge-external-account" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeAddExternalAccountPayload = GT.Object({ + name: "BridgeAddExternalAccountPayload", + fields: () => ({ + errors: { type: GT.List(GT.NonNull(Error)) }, + externalAccount: { type: BridgeExternalAccount }, + }), +}) + +const bridgeAddExternalAccount = GT.Field({ + type: GT.NonNull(BridgeAddExternalAccountPayload), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level < 2) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.addExternalAccount(domainAccount.id) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { externalAccount: result, errors: [] } + }, +}) + +export default bridgeAddExternalAccount diff --git a/src/graphql/public/root/mutation/bridge-create-virtual-account.ts b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts new file mode 100644 index 000000000..55abd4998 --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts @@ -0,0 +1,36 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeCreateVirtualAccountPayload = GT.Object({ + name: "BridgeCreateVirtualAccountPayload", + fields: () => ({ + errors: { type: GT.List(GT.NonNull(Error)) }, + virtualAccount: { type: BridgeVirtualAccount }, + }), +}) + +const bridgeCreateVirtualAccount = GT.Field({ + type: GT.NonNull(BridgeCreateVirtualAccountPayload), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level < 2) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.createVirtualAccount(domainAccount.id) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { virtualAccount: result, errors: [] } + }, +}) + +export default bridgeCreateVirtualAccount diff --git a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts new file mode 100644 index 000000000..9e3234ee3 --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts @@ -0,0 +1,36 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeKycLink from "@graphql/public/types/object/bridge-kyc-link" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeInitiateKycPayload = GT.Object({ + name: "BridgeInitiateKycPayload", + fields: () => ({ + errors: { type: GT.List(GT.NonNull(Error)) }, + kycLink: { type: BridgeKycLink }, + }), +}) + +const bridgeInitiateKyc = GT.Field({ + type: GT.NonNull(BridgeInitiateKycPayload), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level < 2) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.initiateKyc(domainAccount.id) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { kycLink: result, errors: [] } + }, +}) + +export default bridgeInitiateKyc diff --git a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts new file mode 100644 index 000000000..738c10c83 --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts @@ -0,0 +1,53 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeInitiateWithdrawalInput = GT.Input({ + name: "BridgeInitiateWithdrawalInput", + fields: () => ({ + amount: { type: GT.NonNull(GT.String) }, + externalAccountId: { type: GT.NonNull(GT.ID) }, + }), +}) + +const BridgeInitiateWithdrawalPayload = GT.Object({ + name: "BridgeInitiateWithdrawalPayload", + fields: () => ({ + errors: { type: GT.List(GT.NonNull(Error)) }, + withdrawal: { type: BridgeWithdrawal }, + }), +}) + +const bridgeInitiateWithdrawal = GT.Field({ + type: GT.NonNull(BridgeInitiateWithdrawalPayload), + args: { + input: { type: GT.NonNull(BridgeInitiateWithdrawalInput) }, + }, + resolve: async (_, args, { domainAccount }: GraphQLPublicContextAuth) => { + const { amount, externalAccountId } = args.input + + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level < 2) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.initiateWithdrawal( + domainAccount.id, + amount, + externalAccountId, + ) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { withdrawal: result, errors: [] } + }, +}) + +export default bridgeInitiateWithdrawal diff --git a/src/graphql/public/root/query/bridge-external-accounts.ts b/src/graphql/public/root/query/bridge-external-accounts.ts new file mode 100644 index 000000000..d58b9b41c --- /dev/null +++ b/src/graphql/public/root/query/bridge-external-accounts.ts @@ -0,0 +1,26 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeExternalAccount from "@graphql/public/types/object/bridge-external-account" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError } from "@services/bridge/errors" + +const bridgeExternalAccounts = GT.Field({ + type: GT.List(BridgeExternalAccount), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const result = await BridgeService.getExternalAccounts(domainAccount.id) + if (result instanceof Error) { + throw mapAndParseErrorForGqlResponse(result) + } + + return result + }, +}) + +export default bridgeExternalAccounts diff --git a/src/graphql/public/root/query/bridge-kyc-status.ts b/src/graphql/public/root/query/bridge-kyc-status.ts new file mode 100644 index 000000000..fab9cc4b3 --- /dev/null +++ b/src/graphql/public/root/query/bridge-kyc-status.ts @@ -0,0 +1,25 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError } from "@services/bridge/errors" + +const bridgeKycStatus = GT.Field({ + type: GT.String, + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const result = await BridgeService.getKycStatus(domainAccount.id) + if (result instanceof Error) { + throw mapAndParseErrorForGqlResponse(result) + } + + return result + }, +}) + +export default bridgeKycStatus diff --git a/src/graphql/public/root/query/bridge-virtual-account.ts b/src/graphql/public/root/query/bridge-virtual-account.ts new file mode 100644 index 000000000..a563273dd --- /dev/null +++ b/src/graphql/public/root/query/bridge-virtual-account.ts @@ -0,0 +1,26 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError } from "@services/bridge/errors" + +const bridgeVirtualAccount = GT.Field({ + type: BridgeVirtualAccount, + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const result = await BridgeService.getVirtualAccount(domainAccount.id) + if (result instanceof Error) { + throw mapAndParseErrorForGqlResponse(result) + } + + return result + }, +}) + +export default bridgeVirtualAccount diff --git a/src/graphql/public/root/query/bridge-withdrawals.ts b/src/graphql/public/root/query/bridge-withdrawals.ts new file mode 100644 index 000000000..1995670e9 --- /dev/null +++ b/src/graphql/public/root/query/bridge-withdrawals.ts @@ -0,0 +1,26 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError } from "@services/bridge/errors" + +const bridgeWithdrawals = GT.Field({ + type: GT.List(BridgeWithdrawal), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const result = await BridgeService.getWithdrawals(domainAccount.id) + if (result instanceof Error) { + throw mapAndParseErrorForGqlResponse(result) + } + + return result + }, +}) + +export default bridgeWithdrawals diff --git a/src/graphql/public/types/index.ts b/src/graphql/public/types/index.ts index a134f1349..9d9acada0 100644 --- a/src/graphql/public/types/index.ts +++ b/src/graphql/public/types/index.ts @@ -4,6 +4,7 @@ import BtcWallet from "../../shared/types/object/btc-wallet" import GraphQLApplicationError from "../../shared/types/object/graphql-application-error" import UsdWallet from "../../shared/types/object/usd-wallet" +import UsdtWallet from "../../shared/types/object/usdt-wallet" import ConsumerAccount from "./object/consumer-account" import OneDayAccountLimit from "./object/one-day-account-limit" @@ -17,5 +18,6 @@ export const ALL_INTERFACE_TYPES = [ // BusinessAccount, BtcWallet, UsdWallet, + UsdtWallet, OneDayAccountLimit, ] diff --git a/src/graphql/public/types/object/bridge-external-account.ts b/src/graphql/public/types/object/bridge-external-account.ts new file mode 100644 index 000000000..890733978 --- /dev/null +++ b/src/graphql/public/types/object/bridge-external-account.ts @@ -0,0 +1,13 @@ +import { GT } from "@graphql/index" + +const BridgeExternalAccount = GT.Object({ + name: "BridgeExternalAccount", + fields: () => ({ + id: { type: GT.NonNullID }, + bankName: { type: GT.NonNull(GT.String) }, + accountNumberLast4: { type: GT.NonNull(GT.String) }, + status: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeExternalAccount diff --git a/src/graphql/public/types/object/bridge-kyc-link.ts b/src/graphql/public/types/object/bridge-kyc-link.ts new file mode 100644 index 000000000..9f0071dd9 --- /dev/null +++ b/src/graphql/public/types/object/bridge-kyc-link.ts @@ -0,0 +1,11 @@ +import { GT } from "@graphql/index" + +const BridgeKycLink = GT.Object({ + name: "BridgeKycLink", + fields: () => ({ + kycLink: { type: GT.NonNull(GT.String) }, + tosLink: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeKycLink diff --git a/src/graphql/public/types/object/bridge-virtual-account.ts b/src/graphql/public/types/object/bridge-virtual-account.ts new file mode 100644 index 000000000..eef1c4468 --- /dev/null +++ b/src/graphql/public/types/object/bridge-virtual-account.ts @@ -0,0 +1,13 @@ +import { GT } from "@graphql/index" + +const BridgeVirtualAccount = GT.Object({ + name: "BridgeVirtualAccount", + fields: () => ({ + id: { type: GT.NonNullID }, + bankName: { type: GT.NonNull(GT.String) }, + routingNumber: { type: GT.NonNull(GT.String) }, + accountNumberLast4: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeVirtualAccount diff --git a/src/graphql/public/types/object/bridge-withdrawal.ts b/src/graphql/public/types/object/bridge-withdrawal.ts new file mode 100644 index 000000000..f29ba250d --- /dev/null +++ b/src/graphql/public/types/object/bridge-withdrawal.ts @@ -0,0 +1,14 @@ +import { GT } from "@graphql/index" + +const BridgeWithdrawal = GT.Object({ + name: "BridgeWithdrawal", + fields: () => ({ + id: { type: GT.NonNullID }, + amount: { type: GT.NonNull(GT.String) }, + currency: { type: GT.NonNull(GT.String) }, + status: { type: GT.NonNull(GT.String) }, + createdAt: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeWithdrawal diff --git a/src/graphql/shared/types/object/usd-wallet.ts b/src/graphql/shared/types/object/usd-wallet.ts index af7e947f0..27096a000 100644 --- a/src/graphql/shared/types/object/usd-wallet.ts +++ b/src/graphql/shared/types/object/usd-wallet.ts @@ -10,7 +10,7 @@ import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fract import { Wallets } from "@app" -import { WalletCurrency as WalletCurrencyDomain } from "@domain/shared" +import { WalletCurrency as WalletCurrencyDomain, USDTAmount } from "@domain/shared" import { WalletType } from "@domain/wallets" import IWallet from "../abstract/wallet" @@ -51,10 +51,16 @@ const UsdWallet = GT.Object({ type: FractionalCentAmount, resolve: async (source) => { if (source.type === WalletType.External) return null - const balance = await Wallets.getBalanceForWallet({ walletId: source.id }) + const balance = await Wallets.getBalanceForWallet({ + walletId: source.id, + currency: source.currency, + }) if (balance instanceof Error) { throw mapError(balance) } + if (balance instanceof USDTAmount) { + return Number(balance.asSmallestUnits(8)) + } return Number(balance.asCents(8)) }, }, diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts new file mode 100644 index 000000000..d91a0e613 --- /dev/null +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -0,0 +1,140 @@ +import { GT } from "@graphql/index" +import { + connectionArgs, + connectionFromPaginatedArray, + checkedConnectionArgs, +} from "@graphql/connections" +import { normalizePaymentAmount } from "@graphql/shared/root/mutation" +import { mapError } from "@graphql/error-map" + +import { Wallets } from "@app" + +import { WalletCurrency as WalletCurrencyDomain, USDTAmount } from "@domain/shared" + +import IWallet from "../abstract/wallet" + +import WalletCurrency from "../scalar/wallet-currency" +import SignedAmount from "../scalar/signed-amount" +import OnChainAddress from "../scalar/on-chain-address" +import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" + +import { TransactionConnection } from "./transaction" +import { baseLogger } from "@services/logger" +import Lnurl from "../scalar/lnurl" + +const UsdtWallet = GT.Object({ + name: "UsdtWallet", + description: + "A wallet belonging to an account which contains a USDT balance and a list of transactions.", + interfaces: () => [IWallet], + isTypeOf: (source) => source.currency === WalletCurrencyDomain.Usdt, + fields: () => ({ + id: { + type: GT.NonNullID, + }, + accountId: { + type: GT.NonNullID, + }, + walletCurrency: { + type: GT.NonNull(WalletCurrency), + resolve: (source) => source.currency, + }, + + lnurlp: { + type: Lnurl, + resolve: (source) => source.lnurlp, + }, + + balance: { + type: GT.NonNull(FractionalCentAmount), + resolve: async (source) => { + const balance = await Wallets.getBalanceForWallet({ + walletId: source.id, + currency: source.currency, + }) + if (balance instanceof Error) { + throw mapError(balance) + } + if (balance instanceof USDTAmount) { + return Number(balance.asSmallestUnits(8)) + } + return Number((balance as any).asCents(8)) + }, + }, + pendingIncomingBalance: { + type: GT.NonNull(SignedAmount), + description: "An unconfirmed incoming onchain balance.", + resolve: async (source) => { + const balanceSats = await Wallets.getPendingOnChainBalanceForWallets([source]) + if (balanceSats instanceof Error) { + throw mapError(balanceSats) + } + return normalizePaymentAmount(balanceSats[source.id]).amount + }, + }, + transactions: { + type: TransactionConnection, + args: connectionArgs, + resolve: async (source, args) => { + const paginationArgs = checkedConnectionArgs(args) + if (paginationArgs instanceof Error) { + throw paginationArgs + } + + const { result, error } = await Wallets.getTransactionsForWallets({ + wallets: [source], + paginationArgs, + }) + if (error instanceof Error) { + throw mapError(error) + } + + if (!result?.slice) throw error + + return connectionFromPaginatedArray( + result.slice, + result.total, + paginationArgs, + ) + }, + }, + transactionsByAddress: { + type: TransactionConnection, + args: { + ...connectionArgs, + address: { + type: GT.NonNull(OnChainAddress), + description: "Returns the items that include this address.", + }, + }, + resolve: async (source, args) => { + const paginationArgs = checkedConnectionArgs(args) + if (paginationArgs instanceof Error) { + throw paginationArgs + } + + const { address } = args + if (address instanceof Error) throw address + + const { result, error } = await Wallets.getTransactionsForWalletsByAddresses({ + wallets: [source], + addresses: [address], + paginationArgs, + }) + if (error instanceof Error) { + throw mapError(error) + } + + if (!result?.slice) throw error + + return connectionFromPaginatedArray( + result.slice, + result.total, + paginationArgs, + ) + }, + }, + }), +}) + +export default UsdtWallet diff --git a/src/graphql/shared/types/scalar/wallet-currency.ts b/src/graphql/shared/types/scalar/wallet-currency.ts index 65b9090b6..9cbaf314d 100644 --- a/src/graphql/shared/types/scalar/wallet-currency.ts +++ b/src/graphql/shared/types/scalar/wallet-currency.ts @@ -5,6 +5,7 @@ const WalletCurrency = GT.Enum({ values: { BTC: {}, USD: {}, + USDT: {}, }, }) diff --git a/src/servers/bridge-webhook-server.ts b/src/servers/bridge-webhook-server.ts new file mode 100644 index 000000000..b05763936 --- /dev/null +++ b/src/servers/bridge-webhook-server.ts @@ -0,0 +1,8 @@ +/** + * Bridge Webhook Server Entrypoint + * Starts the standalone Bridge webhook server + */ + +import { startBridgeWebhookServer } from "@services/bridge/webhook-server" + +startBridgeWebhookServer() diff --git a/src/servers/graphql-admin-server.ts b/src/servers/graphql-admin-server.ts index 1a220b07c..3a31cae69 100644 --- a/src/servers/graphql-admin-server.ts +++ b/src/servers/graphql-admin-server.ts @@ -15,31 +15,34 @@ import { GraphQLError, GraphQLSchema } from "graphql" import PinoHttp from "pino-http" import { mapError } from "@graphql/error-map" import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity" -import { createComplexityPlugin } from "graphql-query-complexity-apollo-plugin" +import { createComplexityPlugin } from "./plugins/complexity" import { parseUnknownDomainErrorFromUnknown } from "@domain/shared" import healthzHandler from "./middlewares/healthz" import { idempotencyMiddleware } from "./middlewares/idempotency" import requestIp from "request-ip" -import jwt from 'jsonwebtoken' +import jwt from "jsonwebtoken" import { ErpNextRole, ErpNextRoles } from "@services/frappe/Roles" const graphqlLogger = baseLogger.child({ module: "graphql" }) interface JWTPayload { - userId: string; - roles: string[]; + userId: string + roles: string[] } // Parse the "Authorization" header to verify the JWT token and return its payload function parseAuthHeader(authHeader: string | undefined): JWTPayload { - if (!authHeader || !authHeader.startsWith('Bearer ')) { - throw new AuthenticationError({ message: 'Invalid authorization header', logger: graphqlLogger }); + if (!authHeader || !authHeader.startsWith("Bearer ")) { + throw new AuthenticationError({ + message: "Invalid authorization header", + logger: graphqlLogger, + }) } try { - const token = authHeader.slice(7); - return jwt.verify(token, ADMIN_CONFIG.ERPNEXT_JWT_SECRET as string) as JWTPayload; // process.env.ERPNEXT_JWT_SECRET + const token = authHeader.slice(7) + return jwt.verify(token, ADMIN_CONFIG.ERPNEXT_JWT_SECRET as string) as JWTPayload // process.env.ERPNEXT_JWT_SECRET } catch (error) { - throw new AuthenticationError({ message: 'Invalid Token', logger: graphqlLogger }); + throw new AuthenticationError({ message: "Invalid Token", logger: graphqlLogger }) } } @@ -62,7 +65,7 @@ export const hasRole = (role: ErpNextRole) => rule({ cache: "contextual" })(( // // } // addAttributesToCurrentSpanAndPropagate( // { - // [SemanticAttributes.HTTP_CLIENT_IP]: ip, +// [SemanticAttributes.HTTP_CLIENT_IP]: ip, // [SemanticAttributes.HTTP_USER_AGENT]: req.headers["user-agent"], // }, // next, @@ -99,15 +102,15 @@ const startAdminServer = async ({ cache: "bounded", plugins: apolloPlugins, context: (ctx: ExpressContext) => { - const { authorization } = ctx.req.headers; - const decodedJwt = parseAuthHeader(authorization); + const { authorization } = ctx.req.headers + const decodedJwt = parseAuthHeader(authorization) return { - logger: graphqlLogger, + logger: graphqlLogger, user: { id: decodedJwt.userId, roles: decodedJwt.roles, ip: requestIp.getClientIp(ctx.req), - } + }, } }, formatError: (err) => { @@ -201,9 +204,7 @@ const startAdminServer = async ({ ) console.log( - `in dev mode, ${type} server should be accessed through oathkeeper reverse proxy at ${ - "http://localhost:4002/admin/graphql" - }`, + `in dev mode, ${type} server should be accessed through oathkeeper reverse proxy at ${"http://localhost:4002/admin/graphql"}`, ) resolve({ app, httpServer, apolloServer }) @@ -259,4 +260,4 @@ if (require.main === module) { await startApolloServerForAdminSchema() }) .catch((err) => graphqlLogger.error(err, "server error")) -} \ No newline at end of file +} diff --git a/src/servers/graphql-server.ts b/src/servers/graphql-server.ts index 4e2dbc538..6ddc84da6 100644 --- a/src/servers/graphql-server.ts +++ b/src/servers/graphql-server.ts @@ -16,7 +16,7 @@ import { mapError } from "@graphql/error-map" import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity" -import { createComplexityPlugin } from "graphql-query-complexity-apollo-plugin" +import { createComplexityPlugin } from "./plugins/complexity" import jwksRsa from "jwks-rsa" diff --git a/src/servers/plugins/complexity.ts b/src/servers/plugins/complexity.ts new file mode 100644 index 000000000..5bfc3ee1b --- /dev/null +++ b/src/servers/plugins/complexity.ts @@ -0,0 +1,45 @@ +import { GraphQLSchema } from "graphql" +import { getComplexity, ComplexityEstimator } from "graphql-query-complexity" +import type { + ApolloServerPlugin, + GraphQLRequestListener, +} from "apollo-server-plugin-base" + +interface ComplexityPluginOptions { + schema: GraphQLSchema + estimators: ComplexityEstimator[] + maximumComplexity: number + onComplete?: (complexity: number) => void +} + +export function createComplexityPlugin( + options: ComplexityPluginOptions, +): ApolloServerPlugin { + const { schema, estimators, maximumComplexity, onComplete } = options + + return { + async requestDidStart(): Promise { + return { + async didResolveOperation({ request, document }) { + const complexity = getComplexity({ + schema, + operationName: request.operationName ?? undefined, + query: document, + variables: request.variables ?? {}, + estimators, + }) + + if (onComplete) { + onComplete(complexity) + } + + if (complexity > maximumComplexity) { + throw new Error( + `Query complexity of ${complexity} exceeds maximum allowed complexity of ${maximumComplexity}`, + ) + } + }, + } + }, + } +} diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts new file mode 100644 index 000000000..f8e64dfd2 --- /dev/null +++ b/src/services/bridge/client.ts @@ -0,0 +1,367 @@ +/** + * Bridge.xyz API Client + * Ported from bridge-mcp and extended with Tron/USDT support + */ + +import { BridgeConfig } from "@config" +import { + BridgeCustomerId, + BridgeVirtualAccountId, + BridgeExternalAccountId, + BridgeTransferId, + toBridgeCustomerId, + toBridgeVirtualAccountId, + toBridgeExternalAccountId, + toBridgeTransferId, +} from "@domain/primitives/bridge" + +// ============ Error Handling ============ + +export class BridgeApiError extends Error { + constructor( + message: string, + public statusCode: number, + public response?: unknown, + ) { + super(message) + this.name = "BridgeApiError" + } +} + +// ============ Request/Response Types ============ + +export interface CreateIndividualCustomerRequest { + type: "individual" + first_name: string + last_name: string + email: string + phone?: string + address?: { + street_line_1: string + street_line_2?: string + city: string + state?: string + postal_code: string + country: string + } + birth_date?: string + tax_identification_number?: string +} + +export interface CreateBusinessCustomerRequest { + type: "business" + business_name: string + email: string + phone?: string + address?: { + street_line_1: string + street_line_2?: string + city: string + state?: string + postal_code: string + country: string + } + ein?: string +} + +export type CreateCustomerRequest = + | CreateIndividualCustomerRequest + | CreateBusinessCustomerRequest + +export interface Customer { + id: string + type: "individual" | "business" + kyc_status: string + tos_status: string + created_at: string + updated_at: string + first_name?: string + last_name?: string + email?: string + business_name?: string +} + +export interface KycLink { + kyc_link: string + tos_link: string + customer_id: string +} + +// Extended payment rails to include Tron +export type PaymentRail = + | "solana" + | "ethereum" + | "polygon" + | "base" + | "tron" + | "ach_push" + | "ach_pull" + | "wire" + | "sepa" + | "spei" + | "pix" + +// Extended currencies to include USDT +export type Currency = + | "usd" + | "eur" + | "mxn" + | "brl" + | "gbp" + | "usdc" + | "usdb" + | "eurc" + | "usdt" + +export interface CreateVirtualAccountRequest { + developer_fee_percent?: string + source: { + currency: "usd" | "eur" | "mxn" | "brl" | "gbp" + } + destination: { + currency: Currency + payment_rail: PaymentRail + address?: string + bridge_wallet_id?: string + } +} + +export interface VirtualAccount { + id: string + status: string + customer_id: string + developer_fee_percent?: string + source_deposit_instructions: { + currency: string + payment_rails: string[] + bank_name?: string + bank_address?: string + bank_beneficiary_name?: string + bank_account_number?: string + bank_routing_number?: string + iban?: string + bic?: string + } + destination: { + currency: string + payment_rail: string + address?: string + bridge_wallet_id?: string + } + created_at: string +} + +export interface ExternalAccount { + id: string + customer_id: string + account_type: string + currency: string + bank_name?: string + account_number_last_4?: string + routing_number?: string + iban?: string + created_at: string +} + +export interface ExternalAccountLinkUrl { + link_url: string + expires_at: string +} + +export interface ListResponse { + data: T[] + has_more: boolean + cursor?: string +} + +export interface CreateTransferRequest { + amount?: string + currency?: string + on_behalf_of: string + developer_fee?: string + developer_fee_percent?: string + source: { + payment_rail: PaymentRail | "bridge_wallet" + currency: string + from_address?: string + external_account_id?: string + bridge_wallet_id?: string + } + destination: { + payment_rail: PaymentRail | "bridge_wallet" | "ach" + currency: string + to_address?: string + external_account_id?: string + bridge_wallet_id?: string + wire_message?: string + } + dry_run?: boolean + features?: { + flexible_amount?: boolean + static_template?: boolean + allow_any_from_address?: boolean + } +} + +export interface Transfer { + id: string + client_reference_id?: string + amount: string + currency: string + on_behalf_of: string + developer_fee?: string + source: { + payment_rail: string + currency: string + from_address?: string + external_account_id?: string + bridge_wallet_id?: string + } + destination: { + payment_rail: string + currency: string + to_address?: string + external_account_id?: string + bridge_wallet_id?: string + } + state: string + source_deposit_instructions?: { + payment_rail: string + currency: string + amount?: string + bank_name?: string + bank_address?: string + bank_account_number?: string + bank_routing_number?: string + to_address?: string + } + receipt?: Record + created_at: string + updated_at: string +} + +// ============ Bridge Client ============ + +export class BridgeClient { + private apiKey: string + private baseUrl: string + + constructor() { + this.apiKey = BridgeConfig.apiKey + this.baseUrl = BridgeConfig.baseUrl || "https://api.bridge.xyz/v0" + } + + private async request( + method: string, + path: string, + body?: unknown, + idempotencyKey?: string, + ): Promise { + const url = `${this.baseUrl}${path}` + const headers: Record = { + "Api-Key": this.apiKey, + "Content-Type": "application/json", + } + + if (idempotencyKey) { + headers["Idempotency-Key"] = idempotencyKey + } + + const response = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + }) + + const responseData = await response.json().catch(() => null) + + if (!response.ok) { + throw new BridgeApiError( + `Bridge API error: ${response.status} ${response.statusText}`, + response.status, + responseData, + ) + } + + return responseData as T + } + + // ============ Customers ============ + + async createCustomer( + data: CreateCustomerRequest, + idempotencyKey?: string, + ): Promise { + return this.request("POST", "/customers", data, idempotencyKey) + } + + async getCustomer(customerId: BridgeCustomerId): Promise { + return this.request("GET", `/customers/${customerId}`) + } + + // ============ KYC ============ + + async createKycLink(customerId: BridgeCustomerId): Promise { + return this.request("POST", "/kyc_links", { customer_id: customerId }) + } + + // ============ Virtual Accounts ============ + + async createVirtualAccount( + customerId: BridgeCustomerId, + data: CreateVirtualAccountRequest, + idempotencyKey?: string, + ): Promise { + return this.request( + "POST", + `/customers/${customerId}/virtual_accounts`, + data, + idempotencyKey, + ) + } + + // ============ External Accounts ============ + + async getExternalAccountLinkUrl( + customerId: BridgeCustomerId, + ): Promise { + return this.request( + "POST", + `/customers/${customerId}/external_accounts/link`, + ) + } + + async listExternalAccounts( + customerId: BridgeCustomerId, + ): Promise> { + return this.request>( + "GET", + `/customers/${customerId}/external_accounts`, + ) + } + + // ============ Transfers ============ + + async createTransfer( + customerId: BridgeCustomerId, + data: CreateTransferRequest, + idempotencyKey?: string, + ): Promise { + // Note: Bridge API expects on_behalf_of in the body, not in the path + const bodyWithCustomer = { + ...data, + on_behalf_of: customerId, + } + return this.request("POST", "/transfers", bodyWithCustomer, idempotencyKey) + } + + async getTransfer( + customerId: BridgeCustomerId, + transferId: BridgeTransferId, + ): Promise { + // Note: Bridge API uses /transfers/{id} not /customers/{id}/transfers/{id} + return this.request("GET", `/transfers/${transferId}`) + } +} + +export default new BridgeClient() diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts new file mode 100644 index 000000000..737869fba --- /dev/null +++ b/src/services/bridge/errors.ts @@ -0,0 +1,89 @@ +import { DomainError, ErrorLevel } from "@domain/shared" + +export class BridgeError extends DomainError { + readonly level: ErrorLevel = ErrorLevel.Warn +} + +export class BridgeApiError extends BridgeError { + readonly statusCode: number + readonly response?: unknown + + constructor(message: string, statusCode: number, response?: unknown) { + super(message) + this.statusCode = statusCode + this.response = response + } +} + +export class BridgeRateLimitError extends BridgeError { + constructor(message: string = "Rate limit exceeded, please try again later") { + super(message) + } +} + +export class BridgeTimeoutError extends BridgeError { + constructor(message: string = "Request timed out") { + super(message) + } +} + +export class BridgeCustomerNotFoundError extends BridgeError { + constructor(message: string = "Bridge customer not found") { + super(message) + } +} + +export class BridgeKycPendingError extends BridgeError { + constructor(message: string = "KYC verification is pending") { + super(message) + } +} + +export class BridgeKycRejectedError extends BridgeError { + constructor(message: string = "KYC verification was rejected") { + super(message) + } +} + +export class BridgeInsufficientFundsError extends BridgeError { + constructor(message: string = "Insufficient funds for withdrawal") { + super(message) + } +} + +export class BridgeAccountLevelError extends BridgeError { + constructor(message: string = "Bridge requires Pro account (Level 2+)") { + super(message) + } +} + +export class BridgeDisabledError extends BridgeError { + constructor(message: string = "Bridge integration is currently disabled") { + super(message) + } +} + +export class BridgeWebhookValidationError extends BridgeError { + constructor(message: string = "Invalid webhook signature") { + super(message) + } +} + +/** + * Maps HTTP status codes from Bridge API to domain error types + */ +export const mapBridgeHttpError = ( + statusCode: number, + response?: unknown, +): BridgeError => { + switch (statusCode) { + case 404: + return new BridgeCustomerNotFoundError() + case 429: + return new BridgeRateLimitError() + case 408: + return new BridgeTimeoutError() + default: + return new BridgeApiError("Bridge API error", statusCode, response) + } +} diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts new file mode 100644 index 000000000..fca80b89d --- /dev/null +++ b/src/services/bridge/index.ts @@ -0,0 +1,585 @@ +/** + * Bridge Service Layer + * Orchestrates Bridge API client, repository, and implements business logic + * for USD on/off-ramp functionality via Bridge.xyz + */ + +import { BridgeConfig } from "@config" +import BridgeClient, { + KycLink, + VirtualAccount, + ExternalAccountLinkUrl, + Transfer, +} from "./client" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import { AccountsRepository } from "@services/mongoose/accounts" +import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" +import { baseLogger } from "@services/logger" +import { + BridgeDisabledError, + BridgeAccountLevelError, + BridgeKycPendingError, + BridgeKycRejectedError, + BridgeCustomerNotFoundError, +} from "./errors" +import { RepositoryError } from "@domain/errors" +import { toBridgeCustomerId, toBridgeExternalAccountId } from "@domain/primitives/bridge" + +// ============ Types ============ + +type InitiateKycResult = { + kycLink: string + tosLink: string +} + +type CreateVirtualAccountResult = { + virtualAccountId: string + bankName: string + routingNumber: string + accountNumberLast4: string +} + +type AddExternalAccountResult = { + linkUrl: string + expiresAt: string +} + +type InitiateWithdrawalResult = { + transferId: string + amount: string + currency: string + state: string +} + +type WithdrawalResult = { + transferId: string + amount: string + currency: string + state: string + createdAt: string +} + +type KycStatusResult = "pending" | "approved" | "rejected" | null + +type VirtualAccountResult = { + bridgeVirtualAccountId: string + bankName: string + routingNumber: string + accountNumberLast4: string +} | null + +type ExternalAccountResult = { + bridgeExternalAccountId: string + bankName: string + accountNumberLast4: string + status: "pending" | "verified" | "failed" +} + +// ============ Guards ============ + +const checkBridgeEnabled = (): true | BridgeDisabledError => { + if (!BridgeConfig.enabled) { + return new BridgeDisabledError() + } + return true +} + +const checkAccountLevel = async ( + accountId: AccountId, +): Promise => { + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + if (account.level < 2) { + return new BridgeAccountLevelError() + } + return account +} + +// ============ Service Methods ============ + +/** + * Initiates KYC process for an account + * - Creates Bridge customer if not exists + * - Returns KYC and TOS links + */ +const initiateKyc = async (accountId: AccountId): Promise => { + baseLogger.info({ accountId, operation: "initiateKyc" }, "Bridge operation started") + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + let customerId = account.bridgeCustomerId + + // Create customer if not exists + if (!customerId) { + // For now, create with minimal data - in production, gather from account profile + const customer = await BridgeClient.createCustomer({ + type: "individual", + first_name: account.username || "Flash", + last_name: "User", + email: `${account.id}@flash.app`, // Placeholder - should use real email + }) + + customerId = toBridgeCustomerId(customer.id) + + // Store customer ID + const updateResult = await AccountsRepository().updateBridgeFields(accountId, { + bridgeCustomerId: customerId, + bridgeKycStatus: "pending", + }) + if (updateResult instanceof Error) return updateResult + } + + // Create KYC link + const kycLink = await BridgeClient.createKycLink(customerId) + + const result: InitiateKycResult = { + kycLink: kycLink.kyc_link, + tosLink: kycLink.tos_link, + } + + baseLogger.info( + { accountId, operation: "initiateKyc", customerId }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "initiateKyc", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Creates a virtual account for receiving USD deposits + * - Requires approved KYC + * - Creates IBEX Tron USDT receive address + * - Creates Bridge virtual account pointing to Tron address + */ +const createVirtualAccount = async ( + accountId: AccountId, +): Promise => { + baseLogger.info( + { accountId, operation: "createVirtualAccount" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const customerId = account.bridgeCustomerId + if (!customerId) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + // Check KYC status + if (account.bridgeKycStatus === "pending") { + return new BridgeKycPendingError() + } + if (account.bridgeKycStatus === "rejected") { + return new BridgeKycRejectedError() + } + if (account.bridgeKycStatus !== "approved") { + return new BridgeKycPendingError("KYC not yet completed") + } + + // Get or create Tron address + let tronAddress = account.bridgeTronAddress + + if (!tronAddress) { + // TODO: Integrate with IBEX to create Tron USDT receive address + // For now, this is a placeholder - actual implementation requires: + // 1. Call Ibex.getCryptoReceiveOptions() to get Tron USDT option + // 2. Call Ibex.createCryptoReceiveInfo() to get Tron address + // This will be implemented when IBEX crypto receive methods are available + return new Error("IBEX Tron address creation not yet implemented") + } + + // Create Bridge virtual account + const virtualAccount = await BridgeClient.createVirtualAccount(customerId, { + source: { currency: "usd" }, + destination: { + currency: "usdt", + payment_rail: "tron", + address: tronAddress, + }, + }) + + // Store virtual account in repository + const repoResult = await BridgeAccountsRepo.createVirtualAccount({ + accountId: accountId as string, + bridgeVirtualAccountId: virtualAccount.id, + bankName: virtualAccount.source_deposit_instructions.bank_name || "", + routingNumber: virtualAccount.source_deposit_instructions.bank_routing_number || "", + accountNumberLast4: + virtualAccount.source_deposit_instructions.bank_account_number?.slice(-4) || "", + }) + if (repoResult instanceof Error) return repoResult + + const result: CreateVirtualAccountResult = { + virtualAccountId: virtualAccount.id, + bankName: virtualAccount.source_deposit_instructions.bank_name || "", + routingNumber: virtualAccount.source_deposit_instructions.bank_routing_number || "", + accountNumberLast4: + virtualAccount.source_deposit_instructions.bank_account_number?.slice(-4) || "", + } + + baseLogger.info( + { + accountId, + operation: "createVirtualAccount", + virtualAccountId: virtualAccount.id, + }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "createVirtualAccount", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Returns Bridge hosted bank linking URL for adding external accounts + */ +const addExternalAccount = async ( + accountId: AccountId, +): Promise => { + baseLogger.info( + { accountId, operation: "addExternalAccount" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const customerId = account.bridgeCustomerId + if (!customerId) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + const linkUrl = await BridgeClient.getExternalAccountLinkUrl(customerId) + + const result: AddExternalAccountResult = { + linkUrl: linkUrl.link_url, + expiresAt: linkUrl.expires_at, + } + + baseLogger.info( + { accountId, operation: "addExternalAccount" }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "addExternalAccount", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Initiates a withdrawal from USDT to USD bank account + * - Orchestrates IBEX → Bridge transfer + */ +const initiateWithdrawal = async ( + accountId: AccountId, + amount: string, + externalAccountId: string, +): Promise => { + baseLogger.info( + { accountId, amount, externalAccountId, operation: "initiateWithdrawal" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const customerId = account.bridgeCustomerId + if (!customerId) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + const tronAddress = account.bridgeTronAddress + if (!tronAddress) { + return new Error("Account has no Tron address. Create virtual account first.") + } + + // Verify external account exists + const externalAccounts = await BridgeAccountsRepo.findExternalAccountsByAccountId( + accountId as string, + ) + if (externalAccounts instanceof Error) return externalAccounts + + const targetAccount = externalAccounts.find( + (acc) => acc.bridgeExternalAccountId === externalAccountId, + ) + if (!targetAccount) { + return new Error("External account not found") + } + if (targetAccount.status !== "verified") { + return new Error("External account is not verified") + } + + // Create transfer via Bridge + const transfer = await BridgeClient.createTransfer(customerId, { + amount, + on_behalf_of: customerId, + source: { + payment_rail: "tron", + currency: "usdt", + from_address: tronAddress, + }, + destination: { + payment_rail: "ach", + currency: "usd", + external_account_id: externalAccountId, + }, + }) + + // Store withdrawal record + const withdrawalResult = await BridgeAccountsRepo.createWithdrawal({ + accountId: accountId as string, + bridgeTransferId: transfer.id, + amount: transfer.amount, + currency: transfer.currency, + externalAccountId, + status: "pending", + }) + if (withdrawalResult instanceof Error) return withdrawalResult + + const result: InitiateWithdrawalResult = { + transferId: transfer.id, + amount: transfer.amount, + currency: transfer.currency, + state: transfer.state, + } + + baseLogger.info( + { accountId, operation: "initiateWithdrawal", transferId: transfer.id }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "initiateWithdrawal", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Returns KYC status for an account + */ +const getKycStatus = async (accountId: AccountId): Promise => { + baseLogger.info({ accountId, operation: "getKycStatus" }, "Bridge operation started") + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + const result = account.bridgeKycStatus || null + + baseLogger.info( + { accountId, operation: "getKycStatus", status: result }, + "Bridge operation completed", + ) + + return result +} + +/** + * Returns virtual account details for an account + */ +const getVirtualAccount = async ( + accountId: AccountId, +): Promise => { + baseLogger.info( + { accountId, operation: "getVirtualAccount" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const virtualAccount = await BridgeAccountsRepo.findVirtualAccountByAccountId( + accountId as string, + ) + + // Repository returns RepositoryError if not found + if (virtualAccount instanceof RepositoryError) { + baseLogger.info( + { accountId, operation: "getVirtualAccount", result: null }, + "Bridge operation completed - no virtual account", + ) + return null + } + + const result: VirtualAccountResult = { + bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId, + bankName: virtualAccount.bankName, + routingNumber: virtualAccount.routingNumber, + accountNumberLast4: virtualAccount.accountNumberLast4, + } + + baseLogger.info( + { + accountId, + operation: "getVirtualAccount", + virtualAccountId: result.bridgeVirtualAccountId, + }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "getVirtualAccount", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Returns list of linked external bank accounts + */ +const getExternalAccounts = async ( + accountId: AccountId, +): Promise => { + baseLogger.info( + { accountId, operation: "getExternalAccounts" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const externalAccounts = await BridgeAccountsRepo.findExternalAccountsByAccountId( + accountId as string, + ) + if (externalAccounts instanceof Error) return externalAccounts + + const result: ExternalAccountResult[] = externalAccounts.map((acc) => ({ + bridgeExternalAccountId: acc.bridgeExternalAccountId, + bankName: acc.bankName, + accountNumberLast4: acc.accountNumberLast4, + status: acc.status as "pending" | "verified" | "failed", + })) + + baseLogger.info( + { accountId, operation: "getExternalAccounts", count: result.length }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "getExternalAccounts", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Returns list of withdrawals + */ +const getWithdrawals = async ( + accountId: AccountId, +): Promise => { + baseLogger.info({ accountId, operation: "getWithdrawals" }, "Bridge operation started") + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const withdrawals = await BridgeAccountsRepo.findWithdrawalsByAccountId( + accountId as string, + ) + if (withdrawals instanceof Error) return withdrawals + + const result: WithdrawalResult[] = withdrawals.map((w) => ({ + transferId: w.bridgeTransferId, + amount: w.amount, + currency: w.currency, + state: w.status, + createdAt: w.createdAt.toISOString(), + })) + + baseLogger.info( + { accountId, operation: "getWithdrawals", count: result.length }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "getWithdrawals", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +// ============ Export with Tracing ============ + +export default wrapAsyncFunctionsToRunInSpan({ + namespace: "services.bridge", + fns: { + initiateKyc, + createVirtualAccount, + addExternalAccount, + initiateWithdrawal, + getKycStatus, + getVirtualAccount, + getExternalAccounts, + getWithdrawals, + }, +}) diff --git a/src/services/bridge/index.types.d.ts b/src/services/bridge/index.types.d.ts new file mode 100644 index 000000000..430e4b601 --- /dev/null +++ b/src/services/bridge/index.types.d.ts @@ -0,0 +1,121 @@ +import { + BridgeCustomerId, + BridgeVirtualAccountId, + BridgeExternalAccountId, + BridgeTransferId, +} from "@domain/primitives/bridge" + +// Bridge API Service Types - Response models from Bridge API + +interface BridgeCustomer { + readonly id: BridgeCustomerId + readonly externalId?: string + readonly email?: string + readonly name?: string + readonly createdAt: string + readonly updatedAt: string +} + +interface BridgeVirtualAccount { + readonly id: BridgeVirtualAccountId + readonly customerId: BridgeCustomerId + readonly accountNumber: string + readonly routingNumber: string + readonly bankName: string + readonly currency: string + readonly status: "active" | "inactive" | "closed" + readonly createdAt: string + readonly updatedAt: string +} + +interface BridgeExternalAccount { + readonly id: BridgeExternalAccountId + readonly customerId: BridgeCustomerId + readonly accountNumber: string + readonly routingNumber: string + readonly accountHolderName: string + readonly bankName: string + readonly accountType: "checking" | "savings" + readonly status: "pending" | "verified" | "failed" + readonly createdAt: string + readonly updatedAt: string +} + +interface BridgeTransfer { + readonly id: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly sourceAccountId: BridgeVirtualAccountId | BridgeExternalAccountId + readonly destinationAccountId: BridgeVirtualAccountId | BridgeExternalAccountId + readonly amount: number + readonly currency: string + readonly status: "pending" | "processing" | "completed" | "failed" | "cancelled" + readonly description?: string + readonly createdAt: string + readonly updatedAt: string + readonly completedAt?: string +} + +// Webhook Event Types + +interface BridgeWebhookEvent { + readonly id: string + readonly type: string + readonly timestamp: string + readonly data: Record +} + +interface BridgeKycApprovedEvent extends BridgeWebhookEvent { + readonly type: "kyc.approved" + readonly data: { + readonly customerId: BridgeCustomerId + readonly approvedAt: string + } +} + +interface BridgeKycRejectedEvent extends BridgeWebhookEvent { + readonly type: "kyc.rejected" + readonly data: { + readonly customerId: BridgeCustomerId + readonly reason: string + readonly rejectedAt: string + } +} + +interface BridgeDepositCompletedEvent extends BridgeWebhookEvent { + readonly type: "deposit.completed" + readonly data: { + readonly transferId: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly amount: number + readonly currency: string + readonly completedAt: string + } +} + +interface BridgeTransferCompletedEvent extends BridgeWebhookEvent { + readonly type: "transfer.completed" + readonly data: { + readonly transferId: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly amount: number + readonly currency: string + readonly completedAt: string + } +} + +interface BridgeTransferFailedEvent extends BridgeWebhookEvent { + readonly type: "transfer.failed" + readonly data: { + readonly transferId: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly reason: string + readonly failedAt: string + } +} + +type BridgeWebhookEventType = + | BridgeKycApprovedEvent + | BridgeKycRejectedEvent + | BridgeDepositCompletedEvent + | BridgeTransferCompletedEvent + | BridgeTransferFailedEvent diff --git a/src/services/bridge/webhook-server/index.ts b/src/services/bridge/webhook-server/index.ts new file mode 100644 index 000000000..79b8778a5 --- /dev/null +++ b/src/services/bridge/webhook-server/index.ts @@ -0,0 +1,44 @@ +/** + * Bridge Webhook Server + * Standalone Express server for handling Bridge.xyz webhook events + * + * Runs on port configured in BridgeConfig.webhook.port (default: 4009) + * Routes: /kyc, /deposit, /transfer + */ + +import express from "express" +import { BridgeConfig } from "@config" +import { baseLogger } from "@services/logger" +import { verifyBridgeSignature } from "./middleware/verify-signature" +import { kycHandler } from "./routes/kyc" +import { depositHandler } from "./routes/deposit" +import { transferHandler } from "./routes/transfer" + +export const startBridgeWebhookServer = () => { + const app = express() + + // Middleware - MUST capture raw body for signature verification + app.use( + express.json({ + verify: (req: any, res, buf) => { + req.rawBody = buf.toString("utf8") + }, + }), + ) + + // Health check + app.get("/health", (req, res) => { + res.status(200).json({ status: "ok", service: "bridge-webhook" }) + }) + + // Webhook routes with signature verification + app.post("/kyc", verifyBridgeSignature("kyc"), kycHandler) + app.post("/deposit", verifyBridgeSignature("deposit"), depositHandler) + app.post("/transfer", verifyBridgeSignature("transfer"), transferHandler) + + // Start server + const port = BridgeConfig.webhook.port + app.listen(port, () => { + baseLogger.info({ port }, "Bridge webhook server started") + }) +} diff --git a/src/services/bridge/webhook-server/middleware/verify-signature.ts b/src/services/bridge/webhook-server/middleware/verify-signature.ts new file mode 100644 index 000000000..b00961288 --- /dev/null +++ b/src/services/bridge/webhook-server/middleware/verify-signature.ts @@ -0,0 +1,67 @@ +/** + * Bridge Webhook Signature Verification Middleware + * + * Bridge uses asymmetric signature verification (RSA-SHA256), not HMAC. + * Header format: X-Webhook-Signature: t=,v0= + * Signature is computed over: . + */ + +import { Request, Response, NextFunction } from "express" +import crypto from "crypto" +import { BridgeConfig } from "@config" +import { baseLogger } from "@services/logger" + +export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transfer") => { + return (req: Request, res: Response, next: NextFunction) => { + const signature = req.headers["x-webhook-signature"] as string + + if (!signature) { + baseLogger.warn("Missing Bridge webhook signature") + return res.status(401).json({ error: "Missing signature" }) + } + + // Parse signature header: t=,v0= + const parts = signature.split(",") + const timestampPart = parts.find((p) => p.startsWith("t=")) + const signaturePart = parts.find((p) => p.startsWith("v0=")) + + if (!timestampPart || !signaturePart) { + baseLogger.warn("Invalid signature format") + return res.status(401).json({ error: "Invalid signature format" }) + } + + const timestamp = timestampPart.split("=")[1] + const sig = signaturePart.split("=")[1] + + // Check timestamp skew (default 5 minutes) + const now = Date.now() + const timestampMs = parseInt(timestamp, 10) + const skew = Math.abs(now - timestampMs) + + if (skew > BridgeConfig.webhook.timestampSkewMs) { + baseLogger.warn({ skew }, "Webhook timestamp too old") + return res.status(401).json({ error: "Timestamp too old" }) + } + + // Verify signature using Bridge public key + const publicKey = BridgeConfig.webhook.publicKeys[publicKeyType] + const rawBody = (req as any).rawBody || JSON.stringify(req.body) + const payload = `${timestamp}.${rawBody}` + + try { + const verifier = crypto.createVerify("RSA-SHA256") + verifier.update(payload) + const isValid = verifier.verify(publicKey, sig, "base64") + + if (!isValid) { + baseLogger.warn("Invalid Bridge webhook signature") + return res.status(401).json({ error: "Invalid signature" }) + } + + next() + } catch (error) { + baseLogger.error({ error }, "Error verifying Bridge webhook signature") + return res.status(500).json({ error: "Signature verification failed" }) + } + } +} diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts new file mode 100644 index 000000000..79d64253a --- /dev/null +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -0,0 +1,49 @@ +/** + * Bridge Deposit Webhook Handler + * Handles deposit.completed events from Bridge.xyz + * + * NOTE: This handler only logs the deposit event. + * The actual balance crediting happens when IBEX sends its crypto.received webhook. + */ + +import { Request, Response } from "express" +import { LockService } from "@services/lock" +import { baseLogger } from "@services/logger" + +export const depositHandler = async (req: Request, res: Response) => { + const { event, data } = req.body + const { transfer_id, amount, currency, tx_hash, customer_id } = data + + if (!transfer_id || !event) { + return res.status(400).json({ error: "Invalid payload" }) + } + + // Idempotency check using transfer_id as lock key + const lockKey = `bridge-deposit:${transfer_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + if (lockResult instanceof Error) { + baseLogger.info({ transfer_id }, "Duplicate Bridge deposit webhook") + return res.status(200).json({ status: "already_processed" }) + } + + try { + // Log deposit event + // The actual balance crediting happens via IBEX crypto webhook + baseLogger.info( + { + transfer_id, + amount, + currency, + tx_hash, + customer_id, + event, + }, + "Bridge deposit completed", + ) + + return res.status(200).json({ status: "success" }) + } catch (error) { + baseLogger.error({ error, transfer_id }, "Error processing Bridge deposit webhook") + return res.status(500).json({ error: "Internal server error" }) + } +} diff --git a/src/services/bridge/webhook-server/routes/kyc.ts b/src/services/bridge/webhook-server/routes/kyc.ts new file mode 100644 index 000000000..b69dfda08 --- /dev/null +++ b/src/services/bridge/webhook-server/routes/kyc.ts @@ -0,0 +1,80 @@ +/** + * Bridge KYC Webhook Handler + * Handles kyc.approved and kyc.rejected events from Bridge.xyz + */ + +import { Request, Response } from "express" +import { AccountsRepository } from "@services/mongoose/accounts" +import { LockService } from "@services/lock" +import { baseLogger } from "@services/logger" +import { toBridgeCustomerId } from "@domain/primitives/bridge" + +export const kycHandler = async (req: Request, res: Response) => { + const { event, data } = req.body + const { customer_id, kyc_status, reason } = data + + if (!customer_id || !event) { + return res.status(400).json({ error: "Invalid payload" }) + } + + // Idempotency check using customer_id + event as lock key + const lockKey = `bridge-kyc:${customer_id}:${event}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + if (lockResult instanceof Error) { + baseLogger.info({ customer_id, event }, "Duplicate Bridge KYC webhook") + return res.status(200).json({ status: "already_processed" }) + } + + try { + const bridgeCustomerId = toBridgeCustomerId(customer_id) + const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) + + if (account instanceof Error) { + baseLogger.error({ customer_id }, "Account not found for Bridge customer") + return res.status(404).json({ error: "Account not found" }) + } + + // Update KYC status based on event + if (event === "kyc.approved") { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: "approved", + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.info({ accountId: account.id, customer_id }, "Bridge KYC approved") + } else if (event === "kyc.rejected") { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: "rejected", + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.warn( + { + accountId: account.id, + customer_id, + reason, + }, + "Bridge KYC rejected", + ) + } + + return res.status(200).json({ status: "success" }) + } catch (error) { + baseLogger.error({ error, customer_id }, "Error processing Bridge KYC webhook") + return res.status(500).json({ error: "Internal server error" }) + } +} diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts new file mode 100644 index 000000000..e08b8fa10 --- /dev/null +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -0,0 +1,88 @@ +/** + * Bridge Transfer Webhook Handler + * Handles transfer.completed and transfer.failed events from Bridge.xyz + */ + +import { Request, Response } from "express" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import { LockService } from "@services/lock" +import { baseLogger } from "@services/logger" +import { toBridgeTransferId } from "@domain/primitives/bridge" + +export const transferHandler = async (req: Request, res: Response) => { + const { event, data } = req.body + const { transfer_id, state, amount, currency } = data + + if (!transfer_id || !event) { + return res.status(400).json({ error: "Invalid payload" }) + } + + // Idempotency check using transfer_id as lock key + const lockKey = `bridge-transfer:${transfer_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + if (lockResult instanceof Error) { + baseLogger.info({ transfer_id }, "Duplicate Bridge transfer webhook") + return res.status(200).json({ status: "already_processed" }) + } + + try { + const bridgeTransferId = toBridgeTransferId(transfer_id) + + // Update withdrawal status based on event + if (event === "transfer.completed") { + const result = await BridgeAccountsRepo.updateWithdrawalStatus( + bridgeTransferId, + "completed", + ) + + if (result instanceof Error) { + baseLogger.error( + { transfer_id, error: result }, + "Failed to update withdrawal status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.info( + { + transfer_id, + amount, + currency, + }, + "Bridge transfer completed", + ) + + // TODO: Send push notification to user + } else if (event === "transfer.failed") { + const result = await BridgeAccountsRepo.updateWithdrawalStatus( + bridgeTransferId, + "failed", + ) + + if (result instanceof Error) { + baseLogger.error( + { transfer_id, error: result }, + "Failed to update withdrawal status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.warn( + { + transfer_id, + state, + amount, + currency, + }, + "Bridge transfer failed", + ) + + // TODO: Send push notification to user + } + + return res.status(200).json({ status: "success" }) + } catch (error) { + baseLogger.error({ error, transfer_id }, "Error processing Bridge transfer webhook") + return res.status(500).json({ error: "Internal server error" }) + } +} diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index facea5a3e..2630e6def 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -1,104 +1,171 @@ import IbexClient, { GetFeeEstimateResponse200, IbexClientError } from "ibex-client" import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" -import { IbexConfig } from "@config"; -import { AddInvoiceBodyParam, AddInvoiceResponse201, CreateAccountResponse201, CreateLnurlPayBodyParam, CreateLnurlPayResponse201, DecodeLnurlMetadataParam, DecodeLnurlResponse200, EstimateFeeCopyMetadataParam, EstimateFeeCopyResponse200, GenerateBitcoinAddressBodyParam, GenerateBitcoinAddressResponse201, GetAccountDetailsMetadataParam, GetAccountDetailsResponse200, GetFeeEstimationMetadataParam, GetFeeEstimationResponse200, GetTransactionDetails1MetadataParam, GetTransactionDetails1Response200, GMetadataParam, GResponse200, InvoiceFromHashMetadataParam, InvoiceFromHashResponse200, PayInvoiceV2BodyParam, PayInvoiceV2Response200, PayToALnurlPayBodyParam, PayToALnurlPayResponse201, SendToAddressCopyBodyParam, SendToAddressCopyResponse200 } from "ibex-client"; -import { addAttributesToCurrentSpan, wrapAsyncFunctionsToRunInSpan } from "@services/tracing"; -import WebhookServer from "./webhook-server"; -import { Redis } from "./cache" -import { GetFeeEstimateArgs, IbexAccountDetails, IbexFeeEstimation, IbexInvoiceArgs, PayInvoiceArgs, SendOnchainArgs } from "./types"; -import { USDAmount } from "@domain/shared"; +import { IbexConfig } from "@config" +import { + AddInvoiceBodyParam, + AddInvoiceResponse201, + CreateAccountResponse201, + CreateLnurlPayBodyParam, + CreateLnurlPayResponse201, + DecodeLnurlMetadataParam, + DecodeLnurlResponse200, + EstimateFeeCopyMetadataParam, + EstimateFeeCopyResponse200, + GenerateBitcoinAddressBodyParam, + GenerateBitcoinAddressResponse201, + GetAccountDetailsMetadataParam, + GetAccountDetailsResponse200, + GetFeeEstimationMetadataParam, + GetFeeEstimationResponse200, + GetTransactionDetails1MetadataParam, + GetTransactionDetails1Response200, + GMetadataParam, + GResponse200, + InvoiceFromHashMetadataParam, + InvoiceFromHashResponse200, + PayInvoiceV2BodyParam, + PayInvoiceV2Response200, + PayToALnurlPayBodyParam, + PayToALnurlPayResponse201, + SendToAddressCopyBodyParam, + SendToAddressCopyResponse200, +} from "ibex-client" +import { + addAttributesToCurrentSpan, + wrapAsyncFunctionsToRunInSpan, +} from "@services/tracing" +import WebhookServer from "./webhook-server" +import { Redis } from "./cache" +import { + GetFeeEstimateArgs, + IbexAccountDetails, + IbexFeeEstimation, + IbexInvoiceArgs, + PayInvoiceArgs, + CryptoReceiveOption, + CryptoReceiveInfo, + CreateCryptoReceiveInfoRequest, +} from "./types" +import { USDAmount, USDTAmount } from "@domain/shared" +import { baseLogger } from "@services/logger" const Ibex = new IbexClient( - { clientId: IbexConfig.clientId, clientSecret: IbexConfig.clientSecret, environment: IbexConfig.environment }, - Redis + { + clientId: IbexConfig.clientId, + clientSecret: IbexConfig.clientSecret, + environment: IbexConfig.environment, + }, + Redis, ) -const createAccount = async (name: string, currencyId: IbexCurrencyId): Promise => { +const createAccount = async ( + name: string, + currencyId: IbexCurrencyId, +): Promise => { return Ibex.createAccount({ name, currencyId }).then(errorHandler) } -const getAccountDetails = async (accountId: IbexAccountId): Promise => { +const getAccountDetails = async ( + accountId: IbexAccountId, +): Promise => { return Ibex.getAccountDetails({ accountId }) - .then(r => { + .then((r) => { if (r instanceof Error) return r else { - let balance = r.balance !== undefined ? USDAmount.dollars(r.balance.toString()) : undefined + let balance = + r.balance !== undefined ? USDAmount.dollars(r.balance.toString()) : undefined if (balance instanceof Error) balance = undefined return { id: r.id, userId: r.userId, name: r.name, - balance + balance, } } }) .then(errorHandler) } -const getAccountTransactions = async (params: GMetadataParam): Promise => { +const getAccountTransactions = async ( + params: GMetadataParam, +): Promise => { addAttributesToCurrentSpan({ "request.params": JSON.stringify(params) }) return Ibex.getAccountTransactions(params).then(errorHandler) } -const addInvoice = async (args: IbexInvoiceArgs): Promise => { - const body = { - ...args, - amount: args.amount?.toIbex(), - webhookUrl: WebhookServer.endpoints.onReceive.invoice, - webhookSecret: WebhookServer.secret, +const addInvoice = async ( + args: IbexInvoiceArgs, +): Promise => { + const body = { + ...args, + amount: args.amount?.toIbex(), + webhookUrl: WebhookServer.endpoints.onReceive.invoice, + webhookSecret: WebhookServer.secret, } as AddInvoiceBodyParam addAttributesToCurrentSpan({ "request.params": JSON.stringify(body) }) return Ibex.addInvoice(body).then(errorHandler) } -const getTransactionDetails = async (id: IbexTransactionId): Promise => { +const getTransactionDetails = async ( + id: IbexTransactionId, +): Promise => { return Ibex.getTransactionDetails({ transaction_id: id }).then(errorHandler) } -const generateBitcoinAddress = async (accountId: IbexAccountId): Promise => { +const generateBitcoinAddress = async ( + accountId: IbexAccountId, +): Promise => { return Ibex.generateBitcoinAddress({ accountId, webhookUrl: WebhookServer.endpoints.onReceive.onchain, - webhookSecret: WebhookServer.secret, + webhookSecret: WebhookServer.secret, }).then(errorHandler) } -const invoiceFromHash = async (invoice_hash: PaymentHash): Promise => { +const invoiceFromHash = async ( + invoice_hash: PaymentHash, +): Promise => { return Ibex.invoiceFromHash({ invoice_hash }).then(errorHandler) } // Only supports USD for now -const getLnFeeEstimation = async (args: GetFeeEstimateArgs): Promise => { +const getLnFeeEstimation = async ( + args: GetFeeEstimateArgs, +): Promise => { const currencyId = USDAmount.currencyId - // const amount = (args.send instanceof IbexCurrency) ? args.send.amount.toString() : undefined - + // const amount = (args.send instanceof IbexCurrency) ? args.send.amount.toString() : undefined + const resp = await Ibex.getFeeEstimation({ bolt11: args.invoice as string, - amount: args.send?.asDollars(8), + amount: args.send?.asDollars(8), currencyId: currencyId.toString(), }) if (resp instanceof Error) return new IbexError(resp) - else if (resp.amount === null || resp.amount === undefined) return new UnexpectedIbexResponse("Fee not found.") - else if (resp.invoiceAmount === null || resp.invoiceAmount === undefined) return new UnexpectedIbexResponse("invoiceAmount not found.") + else if (resp.amount === null || resp.amount === undefined) + return new UnexpectedIbexResponse("Fee not found.") + else if (resp.invoiceAmount === null || resp.invoiceAmount === undefined) + return new UnexpectedIbexResponse("invoiceAmount not found.") else { let fee = USDAmount.dollars(resp.amount) if (fee instanceof Error) return new ParseError(fee) let invoiceAmount = USDAmount.dollars(resp.invoiceAmount) if (invoiceAmount instanceof Error) return new ParseError(invoiceAmount) - return { - fee, + return { + fee, invoice: invoiceAmount, } } } -const payInvoice = async (args: PayInvoiceArgs): Promise => { - const bodyWithHooks = { - accountId: args.accountId, - bolt11: args.invoice, - amount: args.send?.toIbex(), - webhookUrl: WebhookServer.endpoints.onPay.invoice, - webhookSecret: WebhookServer.secret, +const payInvoice = async ( + args: PayInvoiceArgs, +): Promise => { + const bodyWithHooks = { + accountId: args.accountId, + bolt11: args.invoice, + amount: args.send?.toIbex(), + webhookUrl: WebhookServer.endpoints.onPay.invoice, + webhookSecret: WebhookServer.secret, } as PayInvoiceV2BodyParam addAttributesToCurrentSpan({ "request.params": JSON.stringify(bodyWithHooks) }) return Ibex.payInvoiceV2(bodyWithHooks).then(errorHandler) @@ -106,57 +173,125 @@ const payInvoice = async (args: PayInvoiceArgs): Promise => { - const body = { - accountId: args.accountId, - address: args.address, - amount: args.amount.toIbex(), - webhookUrl: WebhookServer.endpoints.onPay.onchain, - webhookSecret: WebhookServer.secret, - } as SendToAddressCopyBodyParam - addAttributesToCurrentSpan({ "request.params": JSON.stringify(body) }) - return Ibex.sendToAddressV2(body).then(errorHandler) -} - -const estimateOnchainFee = async (send: USDAmount, address: OnChainAddress): Promise => { - return Ibex.estimateFeeV2({ - amount: send.toIbex(), - "currency-id": USDAmount.currencyId.toString(), - address +const sendOnchain = async ( + body: SendToAddressCopyBodyParam, +): Promise => { + const bodyWithHooks = { + ...body, + webhookUrl: WebhookServer.endpoints.onPay.onchain, + webhookSecret: WebhookServer.secret, + } as SendToAddressCopyBodyParam + addAttributesToCurrentSpan({ "request.params": JSON.stringify(bodyWithHooks) }) + return Ibex.sendToAddressV2(bodyWithHooks).then(errorHandler) +} + +const estimateOnchainFee = async ( + send: USDAmount, + address: OnChainAddress, +): Promise => { + return Ibex.estimateFeeV2({ + "amount": send.toIbex(), + "currency-id": USDAmount.currencyId.toString(), + address, }).then(errorHandler) } - -const createLnurlPay = async (body: CreateLnurlPayBodyParam): Promise => { - const bodyWithHooks = { - ...body, - webhookUrl: WebhookServer.endpoints.onReceive.lnurl, - webhookSecret: WebhookServer.secret, + +const createLnurlPay = async ( + body: CreateLnurlPayBodyParam, +): Promise => { + const bodyWithHooks = { + ...body, + webhookUrl: WebhookServer.endpoints.onReceive.lnurl, + webhookSecret: WebhookServer.secret, } as CreateLnurlPayBodyParam addAttributesToCurrentSpan({ "request.params": JSON.stringify(bodyWithHooks) }) return Ibex.createLnurlPay(bodyWithHooks).then(errorHandler) } -const decodeLnurl = async (lnurl: DecodeLnurlMetadataParam): Promise => { +const decodeLnurl = async ( + lnurl: DecodeLnurlMetadataParam, +): Promise => { return Ibex.decodeLnurl(lnurl).then(errorHandler) } - -const payToLnurl = async (args: PayLnurlArgs): Promise => { + +const payToLnurl = async ( + args: PayLnurlArgs, +): Promise => { return Ibex.payToLnurl({ accountId: args.accountId, amount: args.send.amount, params: args.params, webhookUrl: WebhookServer.endpoints.onPay.lnurl, - webhookSecret: WebhookServer.secret, + webhookSecret: WebhookServer.secret, }).then(errorHandler) } +const getCryptoReceiveBalance = async ( + receiveInfoId: string, +): Promise => { + try { + const resp = await (Ibex as any).getCryptoReceiveBalance({ receiveInfoId }) + if (resp instanceof Error) return new IbexError(resp) + if (resp.balance === null || resp.balance === undefined) + return new UnexpectedIbexResponse("Balance not found") + const balance = USDTAmount.smallestUnits(resp.balance.toString()) + if (balance instanceof Error) return new IbexError(balance) + return balance + } catch (err) { + return new IbexError(err instanceof Error ? err : new Error(String(err))) + } +} + +const getCryptoReceiveOptions = async (): Promise => { + try { + const resp = await (Ibex as any).getCryptoReceiveOptions() + if (resp instanceof Error) return new IbexError(resp) + return resp.options || [] + } catch (err) { + return new IbexError(err instanceof Error ? err : new Error(String(err))) + } +} + +const createCryptoReceiveInfo = async ( + walletId: IbexAccountId, + optionId: string, +): Promise => { + try { + const resp = await (Ibex as any).createCryptoReceiveInfo({ + wallet_id: walletId, + option_id: optionId, + } as CreateCryptoReceiveInfoRequest) + if (resp instanceof Error) return new IbexError(resp) + if (!resp.address) return new UnexpectedIbexResponse("Address not found") + return resp + } catch (err) { + return new IbexError(err instanceof Error ? err : new Error(String(err))) + } +} + +const getTronUsdtOption = async (): Promise => { + const options = await getCryptoReceiveOptions() + if (options instanceof IbexError) return options + + const tronUsdt = options.find( + (opt) => + opt.currency.toLowerCase() === "usdt" && opt.network.toLowerCase() === "tron", + ) + + if (!tronUsdt) { + return new IbexError(new Error("Tron USDT option not found")) + } + + return tronUsdt.id +} + // const sendBetweenAccounts = async ( -// sender: IbexAccount, -// receiver: IbexAccount, +// sender: IbexAccount, +// receiver: IbexAccount, // transfer: USDollars, // memo: string = "Flash-to-Flash" // ): Promise => { -// const invoiceResp = await addInvoice({ +// const invoiceResp = await addInvoice({ // accountId: receiver.id, // memo, // amount: transfer, // convert cents to dollars for Ibex api @@ -172,20 +307,24 @@ const payToLnurl = async (args: PayLnurlArgs): Promise { const app = express() - // Middleware to parse JSON requests app.use(express.json()) - // Routes app.get("/health", (_: Request, resp: Response) => resp.send("Ibex server is running")) app.use(onReceive.router) app.use(onPay.router) + app.use(cryptoReceive.router) app.listen(IbexConfig.webhook.port, () => logger.info( `Listening for ibex events on port ${IbexConfig.webhook.port}. Can be reached at ${IbexConfig.webhook.uri}`, @@ -35,6 +34,9 @@ export default { lnurl: IbexConfig.webhook.uri + onPay.paths.lnurl, onchain: IbexConfig.webhook.uri + onPay.paths.onchain, }, + cryptoReceive: { + cryptoReceive: IbexConfig.webhook.uri + cryptoReceive.paths.cryptoReceive, + }, }, secret: IbexConfig.webhook.secret, } diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts new file mode 100644 index 000000000..7fe2283a5 --- /dev/null +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -0,0 +1,104 @@ +import express, { Request, Response } from "express" +import { AccountsRepository } from "@services/mongoose/accounts" +import { listWalletsByAccountId } from "@app/wallets" +import { WalletCurrency, USDTAmount } from "@domain/shared" +import { baseLogger } from "@services/logger" +import { LockService } from "@services/lock" +import { authenticate, logRequest } from "../middleware" + +const paths = { + cryptoReceive: "/crypto/receive", +} + +const router = express.Router() + +interface CryptoReceiveResult { + status: "success" | "error" + code?: string +} + +const cryptoReceiveHandler = async (req: Request, res: Response) => { + const { tx_hash, address, amount, currency, network } = req.body + + if (!tx_hash || !address || !amount || currency !== "USDT" || network !== "tron") { + baseLogger.warn( + { tx_hash, address, amount, currency, network }, + "Invalid crypto receive payload", + ) + return res.status(400).json({ error: "Invalid payload" }) + } + + const lockResult = await LockService().lockPaymentHash(tx_hash as any, async () => { + try { + const account = await AccountsRepository().findByBridgeTronAddress(address) + if (account instanceof Error) { + baseLogger.error({ address, tx_hash }, "Account not found for Tron address") + return { status: "error", code: "account_not_found" } as CryptoReceiveResult + } + + const wallets = await listWalletsByAccountId(account.id) + if (wallets instanceof Error) { + baseLogger.error( + { accountId: account.id, error: wallets }, + "Failed to list wallets", + ) + return { status: "error", code: "wallet_list_failed" } as CryptoReceiveResult + } + + const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) + if (!usdtWallet) { + baseLogger.error({ accountId: account.id }, "USDT wallet not found") + return { status: "error", code: "usdt_wallet_not_found" } as CryptoReceiveResult + } + + const usdtAmount = USDTAmount.fromNumber(amount) + if (usdtAmount instanceof Error) { + baseLogger.error({ amount, error: usdtAmount }, "Invalid USDT amount") + return { status: "error", code: "invalid_amount" } as CryptoReceiveResult + } + + baseLogger.info( + { + accountId: account.id, + walletId: usdtWallet.id, + amount: usdtAmount.asNumber(), + tx_hash, + address, + }, + "USDT deposit received", + ) + + return { status: "success" } as CryptoReceiveResult + } catch (error) { + baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") + return { status: "error", code: "internal_error" } as CryptoReceiveResult + } + }) + + if (lockResult instanceof Error) { + baseLogger.warn( + { tx_hash, error: lockResult }, + "Lock acquisition failed or duplicate webhook", + ) + return res.status(200).json({ status: "already_processed" }) + } + + if (lockResult.status === "success") { + return res.status(200).json({ status: "success" }) + } + + const statusMap: Record = { + account_not_found: 404, + wallet_list_failed: 500, + usdt_wallet_not_found: 404, + invalid_amount: 400, + internal_error: 500, + } + + const statusCode = statusMap[lockResult.code || ""] || 500 + return res.status(statusCode).json({ error: lockResult.code }) +} + +router.post(paths.cryptoReceive, authenticate, logRequest, cryptoReceiveHandler) + +export { paths, router } diff --git a/src/services/ibex/webhook-server/routes/index.ts b/src/services/ibex/webhook-server/routes/index.ts index e41b6e40b..cb1759853 100644 --- a/src/services/ibex/webhook-server/routes/index.ts +++ b/src/services/ibex/webhook-server/routes/index.ts @@ -1,2 +1,3 @@ export * as onPay from "./on-pay" -export * as onReceive from "./on-receive" \ No newline at end of file +export * as onReceive from "./on-receive" +export * as cryptoReceive from "./crypto-receive" diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index 32d55ec4b..7e658daa6 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -173,6 +173,51 @@ export const AccountsRepository = (): IAccountsRepository => { } } + const updateBridgeFields = async ( + id: AccountId, + fields: { + bridgeCustomerId?: BridgeCustomerId + bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeTronAddress?: string + }, + ): Promise => { + try { + const result = await Account.findByIdAndUpdate( + toObjectId(id), + { $set: fields }, + { new: true }, + ) + if (!result) return new RepositoryError("Account not found") + return translateToAccount(result) + } catch (error) { + return parseRepositoryError(error) + } + } + + const findByBridgeTronAddress = async ( + address: string, + ): Promise => { + try { + const result = await Account.findOne({ bridgeTronAddress: address }) + if (!result) return new RepositoryError("Account not found for Tron address") + return translateToAccount(result) + } catch (error) { + return parseRepositoryError(error) + } + } + + const findByBridgeCustomerId = async ( + customerId: BridgeCustomerId, + ): Promise => { + try { + const result = await Account.findOne({ bridgeCustomerId: customerId }) + if (!result) return new RepositoryError("Account not found for Bridge customer ID") + return translateToAccount(result) + } catch (error) { + return parseRepositoryError(error) + } + } + return { persistNew, findByUserId, @@ -182,6 +227,9 @@ export const AccountsRepository = (): IAccountsRepository => { findByUsername, findByNpub, update, + updateBridgeFields, + findByBridgeTronAddress, + findByBridgeCustomerId, } } @@ -243,4 +291,7 @@ const translateToAccount = (result: AccountRecord): Account => ({ kratosUserId: result.kratosUserId as UserId, displayCurrency: (result.displayCurrency || UsdDisplayCurrency) as DisplayCurrency, + bridgeCustomerId: result.bridgeCustomerId as BridgeCustomerId | undefined, + bridgeKycStatus: result.bridgeKycStatus, + bridgeTronAddress: result.bridgeTronAddress, }) diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts new file mode 100644 index 000000000..caeb7867b --- /dev/null +++ b/src/services/mongoose/bridge-accounts.ts @@ -0,0 +1,138 @@ +import { BridgeVirtualAccount, BridgeExternalAccount, BridgeWithdrawal } from "./schema" +import { + BridgeVirtualAccountId, + BridgeExternalAccountId, + BridgeTransferId, +} from "@domain/primitives/bridge" +import { RepositoryError } from "@domain/errors" + +// ============ Virtual Accounts ============ + +export const createVirtualAccount = async (data: { + accountId: string + bridgeVirtualAccountId: string + bankName: string + routingNumber: string + accountNumberLast4: string +}) => { + try { + const record = await BridgeVirtualAccount.create(data) + return record + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findVirtualAccountByAccountId = async (accountId: string) => { + try { + const record = await BridgeVirtualAccount.findOne({ accountId }) + return record || new RepositoryError("Virtual account not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findVirtualAccountByBridgeId = async (bridgeId: BridgeVirtualAccountId) => { + try { + const record = await BridgeVirtualAccount.findOne({ + bridgeVirtualAccountId: bridgeId, + }) + return record || new RepositoryError("Virtual account not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +// ============ External Accounts ============ + +export const createExternalAccount = async (data: { + accountId: string + bridgeExternalAccountId: string + bankName: string + accountNumberLast4: string + status?: "pending" | "verified" | "failed" +}) => { + try { + const record = await BridgeExternalAccount.create(data) + return record + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findExternalAccountsByAccountId = async (accountId: string) => { + try { + const records = await BridgeExternalAccount.find({ accountId }) + return records + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const updateExternalAccountStatus = async ( + bridgeId: BridgeExternalAccountId, + status: "pending" | "verified" | "failed", +) => { + try { + const record = await BridgeExternalAccount.findOneAndUpdate( + { bridgeExternalAccountId: bridgeId }, + { status }, + { new: true }, + ) + return record || new RepositoryError("External account not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +// ============ Withdrawals ============ + +export const createWithdrawal = async (data: { + accountId: string + bridgeTransferId: string + amount: string + currency: string + externalAccountId: string + status?: "pending" | "completed" | "failed" +}) => { + try { + const record = await BridgeWithdrawal.create(data) + return record + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findWithdrawalsByAccountId = async (accountId: string) => { + try { + const records = await BridgeWithdrawal.find({ accountId }).sort({ createdAt: -1 }) + return records + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const updateWithdrawalStatus = async ( + bridgeTransferId: BridgeTransferId, + status: "pending" | "completed" | "failed", +) => { + try { + const record = await BridgeWithdrawal.findOneAndUpdate( + { bridgeTransferId }, + { status, updatedAt: new Date() }, + { new: true }, + ) + return record || new RepositoryError("Withdrawal not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findWithdrawalByBridgeTransferId = async (transferId: BridgeTransferId) => { + try { + const record = await BridgeWithdrawal.findOne({ bridgeTransferId: transferId }) + return record || new RepositoryError("Withdrawal not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index 6f490709b..d13cccada 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -17,6 +17,36 @@ import { WalletRecord } from "./wallets" const Schema = mongoose.Schema +// Bridge Record Interfaces +interface IBridgeVirtualAccountRecord { + accountId: string + bridgeVirtualAccountId: string + bankName: string + routingNumber: string + accountNumberLast4: string + createdAt: Date +} + +interface IBridgeExternalAccountRecord { + accountId: string + bridgeExternalAccountId: string + bankName: string + accountNumberLast4: string + status: "pending" | "verified" | "failed" + createdAt: Date +} + +interface IBridgeWithdrawalRecord { + accountId: string + bridgeTransferId: string + amount: string + currency: string + status: "pending" | "completed" | "failed" + externalAccountId: string + createdAt: Date + updatedAt: Date +} + const dbMetadataSchema = new Schema({ routingFeeLastEntry: Date, // TODO: rename to routingRevenueLastEntry }) @@ -295,6 +325,20 @@ const AccountSchema = new Schema( }, displayCurrency: String, // FIXME: should be an enum + + bridgeCustomerId: { + type: String, + required: false, + }, + bridgeKycStatus: { + type: String, + enum: ["pending", "approved", "rejected"], + required: false, + }, + bridgeTronAddress: { + type: String, + required: false, + }, }, { id: false }, ) @@ -304,6 +348,8 @@ AccountSchema.index({ coordinates: 1, }) +AccountSchema.index({ bridgeTronAddress: 1 }, { sparse: true }) + export const Account = mongoose.model("Account", AccountSchema) const QuizSchema = new Schema({ @@ -566,4 +612,47 @@ export const WalletOnChainPendingReceive = "WalletOnChainPendingReceive", WalletOnChainPendingReceiveSchema, ) - \ No newline at end of file + +const BridgeVirtualAccountSchema = new Schema({ + accountId: { type: String, required: true, index: true }, + bridgeVirtualAccountId: { type: String, required: true, unique: true }, + bankName: { type: String, required: true }, + routingNumber: { type: String, required: true }, + accountNumberLast4: { type: String, required: true }, + createdAt: { type: Date, default: Date.now }, +}) + +const BridgeExternalAccountSchema = new Schema({ + accountId: { type: String, required: true, index: true }, + bridgeExternalAccountId: { type: String, required: true, unique: true }, + bankName: { type: String, required: true }, + accountNumberLast4: { type: String, required: true }, + status: { type: String, enum: ["pending", "verified", "failed"], default: "pending" }, + createdAt: { type: Date, default: Date.now }, +}) + +const BridgeWithdrawalSchema = new Schema({ + accountId: { type: String, required: true, index: true }, + bridgeTransferId: { type: String, required: true, unique: true }, + amount: { type: String, required: true }, + currency: { type: String, required: true }, + status: { type: String, enum: ["pending", "completed", "failed"], default: "pending" }, + externalAccountId: { type: String, required: true }, + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now }, +}) + +export const BridgeVirtualAccount = mongoose.model( + "BridgeVirtualAccount", + BridgeVirtualAccountSchema, +) + +export const BridgeExternalAccount = mongoose.model( + "BridgeExternalAccount", + BridgeExternalAccountSchema, +) + +export const BridgeWithdrawal = mongoose.model( + "BridgeWithdrawal", + BridgeWithdrawalSchema, +) diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 1a9849006..57016ec0a 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -94,6 +94,11 @@ interface AccountRecord { title?: string coordinates?: CoordinateObjectForUser + // Bridge integration: + bridgeCustomerId?: string + bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeTronAddress?: string + // mongoose in-built functions save: () => Promise } diff --git a/yarn.lock b/yarn.lock index 5ede1b40d..d7e6ae9d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2122,6 +2122,11 @@ protobufjs "^7.5.3" yargs "^17.7.2" +"@hono/node-server@^1.19.9": + version "1.19.14" + resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.19.14.tgz#e30f844bc77e3ce7be442aac3b1f73ad8b58d181" + integrity sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw== + "@humanwhocodes/config-array@^0.13.0": version "0.13.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" @@ -2503,6 +2508,13 @@ dependencies: lodash "^4.17.21" +"@lnflash/bridge-mcp@github:lnflash/bridge-mcp": + version "0.1.0" + resolved "https://codeload.github.com/lnflash/bridge-mcp/tar.gz/b2168e2b80d8935daf444732529bc51e757e6786" + dependencies: + "@modelcontextprotocol/sdk" "^1.0.0" + zod "^3.22.0" + "@mapbox/node-pre-gyp@^1.0.5": version "1.0.11" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" @@ -2554,6 +2566,29 @@ dependencies: make-plural "^7.0.0" +"@modelcontextprotocol/sdk@^1.0.0": + version "1.29.0" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz#79786d8b525e269de850ac82b1f1f757f3915f44" + integrity sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ== + dependencies: + "@hono/node-server" "^1.19.9" + ajv "^8.17.1" + ajv-formats "^3.0.1" + content-type "^1.0.5" + cors "^2.8.5" + cross-spawn "^7.0.5" + eventsource "^3.0.2" + eventsource-parser "^3.0.0" + express "^5.2.1" + express-rate-limit "^8.2.1" + hono "^4.11.4" + jose "^6.1.3" + json-schema-typed "^8.0.2" + pkce-challenge "^5.0.0" + raw-body "^3.0.0" + zod "^3.25 || ^4.0" + zod-to-json-schema "^3.25.1" + "@mongodb-js/saslprep@^1.1.0", "@mongodb-js/saslprep@^1.3.0": version "1.3.2" resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz#51e5cad2f24b8759702d9cc185da0a3ef3784bad" @@ -4723,6 +4758,14 @@ accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-import-assertions@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" @@ -4767,6 +4810,13 @@ ajv-draft-04@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8" integrity sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw== +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -4777,6 +4827,16 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.17.1: + version "8.20.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.20.0.tgz#304b3636add88ba7d936760dd50ece006dea95f9" + integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ajv@^8.12.0: version "8.17.1" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" @@ -5550,6 +5610,21 @@ body-parser@1.20.3, body-parser@^1.19.0, body-parser@^1.20.1: type-is "~1.6.18" unpipe "1.0.0" +body-parser@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.2.tgz#1a32cdb966beaf68de50a9dfbe5b58f83cb8890c" + integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.1" + raw-body "^3.0.1" + type-is "^2.0.1" + body@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" @@ -5713,7 +5788,7 @@ bytes@1: resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" integrity sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ== -bytes@3.1.2: +bytes@3.1.2, bytes@^3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -6116,11 +6191,21 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" -content-type@~1.0.4, content-type@~1.0.5: +content-disposition@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.1.0.tgz#f3db789c752d45564cc7e9e1e0b31790d4a38e17" + integrity sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g== + +content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== +content-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-2.0.0.tgz#2fb3ede69dffa0af78ca7c4ce7589680638b56df" + integrity sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ== + continuable-cache@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" @@ -6144,6 +6229,11 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + cookie@0.5.0, cookie@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" @@ -6154,7 +6244,7 @@ cookie@0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== -cookie@0.7.2: +cookie@0.7.2, cookie@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== @@ -6215,7 +6305,7 @@ cross-inspect@1.0.1: dependencies: tslib "^2.4.0" -cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -6337,7 +6427,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@4.x, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.1, debug@^4.4.3: +debug@4, debug@4.x, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -6423,7 +6513,7 @@ denque@^2.1.0: resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== -depd@2.0.0, depd@~2.0.0: +depd@2.0.0, depd@^2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -6807,16 +6897,16 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +encodeurl@^2.0.0, encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - encoding-sniffer@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz#396ec97ac22ce5a037ba44af1992ac9d46a7b819" @@ -7029,7 +7119,7 @@ escalade@^3.1.1, escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== @@ -7266,7 +7356,7 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@~1.8.1: +etag@^1.8.1, etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== @@ -7299,6 +7389,18 @@ events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource-parser@^3.0.0, eventsource-parser@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.1.0.tgz#4e198eb91cd333d0a8ddcc036502b3618a25f449" + integrity sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg== + +eventsource@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.7.tgz#1157622e2f5377bb6aef2114372728ba0c156989" + integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + dependencies: + eventsource-parser "^3.0.1" + execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -7346,6 +7448,13 @@ express-jwt@^8.4.1: express-unless "^2.1.3" jsonwebtoken "^9.0.0" +express-rate-limit@^8.2.1: + version "8.5.2" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-8.5.2.tgz#5922dbf76df2124611cea955d93432b37514b2f3" + integrity sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A== + dependencies: + ip-address "^10.2.0" + express-unless@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-2.1.3.tgz#f951c6cca52a24da3de32d42cfd4db57bc0f9a2e" @@ -7425,6 +7534,40 @@ express@^4.17.1, express@^4.18.2: utils-merge "1.0.1" vary "~1.1.2" +express@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + ext@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" @@ -7640,6 +7783,18 @@ finalhandler@1.3.1: statuses "2.0.1" unpipe "~1.0.0" +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.1.tgz#a2c517a6559852bcdb06d1f8bd7f51b68fad8099" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" @@ -7817,6 +7972,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fs-extra@^10.0.1: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" @@ -8276,11 +8436,6 @@ graphql-middleware@^6.1.33: "@graphql-tools/delegate" "^8.8.1" "@graphql-tools/schema" "^8.5.1" -graphql-query-complexity-apollo-plugin@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/graphql-query-complexity-apollo-plugin/-/graphql-query-complexity-apollo-plugin-1.0.2.tgz#1eff2eb4ea455a3577c9a2f3580561a5e19b394a" - integrity sha512-TjDzIuAILNI0+wemtTpl47Vj/wCi8TGhq8tVcJQOsHUkiflrVhrHwKHiBX7V5KC5rgUAJtnsqSgVBDJqsAvwjg== - graphql-query-complexity@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/graphql-query-complexity/-/graphql-query-complexity-0.12.0.tgz#5f636ccc54da82225f31e898e7f27192fe074b4c" @@ -8676,6 +8831,11 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" +hono@^4.11.4: + version "4.12.23" + resolved "https://registry.yarnpkg.com/hono/-/hono-4.12.23.tgz#998b91651686149f0e6edbb8564d604da04f3cf8" + integrity sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA== + hooker@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/hooker/-/hooker-0.2.3.tgz#b834f723cc4a242aa65963459df6d984c5d3d959" @@ -8737,6 +8897,17 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + http-errors@~1.6.2: version "1.6.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" @@ -8833,6 +9004,13 @@ iconv-lite@0.6.3, iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.7.0, iconv-lite@~0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e" + integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -8904,7 +9082,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -8982,6 +9160,11 @@ ip-address@^10.0.1: resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.0.1.tgz#a8180b783ce7788777d796286d61bce4276818ed" integrity sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA== +ip-address@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.2.0.tgz#805fc178b20c518bd4c8548b24fe30892d7f3206" + integrity sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA== + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -9178,6 +9361,11 @@ is-promise@^2.2.2: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-regex@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" @@ -9829,6 +10017,11 @@ jose@^4.15.4: resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== +jose@^6.1.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.2.3.tgz#0975197ad973251221c658a3cddc4b951a250c2d" + integrity sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw== + joycon@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" @@ -9964,6 +10157,11 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema-typed@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-8.0.2.tgz#e98ee7b1899ff4a184534d1f167c288c66bbeff4" + integrity sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -10591,6 +10789,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + medici@^6.2.0: version "6.3.3" resolved "https://registry.yarnpkg.com/medici/-/medici-6.3.3.tgz#131b24e696eb7f7669966884151a67b04cbf6fd1" @@ -10627,6 +10830,11 @@ merge-descriptors@1.0.3: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -10677,7 +10885,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -"mime-db@>= 1.43.0 < 2": +"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0: version "1.54.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== @@ -10689,6 +10897,13 @@ mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.17, m dependencies: mime-db "1.52.0" +mime-types@^3.0.0, mime-types@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -10987,6 +11202,11 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + neo-async@^2.6.0, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -11376,7 +11596,7 @@ on-exit-leak-free@^2.1.0: resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== -on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -11704,6 +11924,11 @@ path-to-regexp@^6.2.0: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== +path-to-regexp@^8.0.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.4.2.tgz#795c420c4f7ca45c5b887366f622ee0c9852cccd" + integrity sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -11855,6 +12080,11 @@ pirates@^4.0.4, pirates@^4.0.7: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== +pkce-challenge@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz#3b4446865b17b1745e9ace2016a31f48ddf6230d" + integrity sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ== + pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -12113,7 +12343,7 @@ protoc-gen-js@^3.21.2: dependencies: adm-zip "0.5.10" -proxy-addr@~2.0.7: +proxy-addr@^2.0.7, proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -12199,6 +12429,13 @@ qs@^6.10.5, qs@^6.12.3, qs@^6.4.0, qs@^6.9.4: dependencies: side-channel "^1.1.0" +qs@^6.14.0, qs@^6.14.1: + version "6.15.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.2.tgz#fd55426d710403ddccc45e0f9eab16db7727ece9" + integrity sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw== + dependencies: + side-channel "^1.1.0" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -12233,7 +12470,7 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@~1.2.1: +range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== @@ -12263,6 +12500,16 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^3.0.0, raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.2.tgz#3e3ada5ae5568f9095d84376fd3a49b8fb000a51" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" + raw-body@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" @@ -12633,6 +12880,17 @@ ripemd160@^2.0.1: hash-base "^3.1.2" inherits "^2.0.4" +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -12783,6 +13041,23 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" +send@^1.1.0, send@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed" + integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + dependencies: + debug "^4.4.3" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.1" + mime-types "^3.0.2" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.2" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -12816,6 +13091,16 @@ serve-static@1.16.2, serve-static@^1.14.1: parseurl "~1.3.3" send "0.19.0" +serve-static@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.1.tgz#7f186a4a4e5f5b663ad7a4294ff1bf37cf0e98a9" + integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -12867,7 +13152,7 @@ setprototypeof@1.1.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.2.0: +setprototypeof@1.2.0, setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -13174,6 +13459,11 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + stop-iteration-iterator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" @@ -13660,7 +13950,7 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.1: +toidentifier@1.0.1, toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== @@ -13878,6 +14168,15 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-is@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.1.0.tgz#71d1a7053293582e16ac9f3ebaf1ab9aa49e5570" + integrity sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA== + dependencies: + content-type "^2.0.0" + media-typer "^1.1.0" + mime-types "^3.0.0" + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -14235,7 +14534,7 @@ varuint-bitcoin@1.1.2, varuint-bitcoin@^1.1.2: dependencies: safe-buffer "^5.1.1" -vary@^1, vary@~1.1.2: +vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== @@ -14613,7 +14912,17 @@ zen-observable@0.8.15: resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== -zod@^3.22.2: +zod-to-json-schema@^3.25.1: + version "3.25.2" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz#3fa799a7badd554541472fb65843fdc460b2e5aa" + integrity sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA== + +zod@^3.22.0, zod@^3.22.2: version "3.25.76" resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== + +"zod@^3.25 || ^4.0": + version "4.4.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356" + integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== From 9714626149257819605de7d5322cbc5a0170d4a4 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:16 -0400 Subject: [PATCH 02/43] ENG-278 ENG-280 ENG-281 ENG-282 ENG-283 ENG-284 ENG-285 ENG-349 ENG-363 ENG-297 fix(bridge): apply audit hardening and hosted KYC refinements --- dev/apollo-federation/supergraph.graphql | 140 +++++++++- .../admin/account-update-level.bru | 2 +- .../Flash GraphQL API/environments/local.bru | 2 + .../mutations/bridgeCreateVirtualAccount.bru | 37 +++ .../token/mutations/bridgeInitiateKyc.bru | 44 +++ .../token/queries/bridgeKycStatus.bru | 26 ++ .../token/queries/bridgeVirtualAccount.bru | 32 +++ dev/config/base-config.yaml | 29 +- package.json | 2 +- src/app/offers/Validator.ts | 2 +- src/app/wallets/send-on-chain.ts | 6 +- src/config/schema.types.d.ts | 1 + src/domain/accounts/index.types.d.ts | 6 +- .../bitcoin/lightning/invoice-expiration.ts | 6 +- src/domain/shared/index.ts | 1 + src/graphql/admin/schema.graphql | 1 + src/graphql/error-map.ts | 8 + .../mutation/bridge-add-external-account.ts | 3 +- .../mutation/bridge-create-virtual-account.ts | 3 +- .../root/mutation/bridge-initiate-kyc.ts | 20 +- .../mutation/bridge-initiate-withdrawal.ts | 24 +- .../root/mutation/onchain-payment-send-all.ts | 4 + .../root/query/bridge-virtual-account.ts | 15 +- src/graphql/public/schema.graphql | 117 +++++++- .../types/object/bridge-virtual-account.ts | 13 +- ...bridge-virtual-account-unique-accountid.ts | 103 +++++++ src/servers/bridge-webhook-server.ts | 13 +- src/services/bridge/client.ts | 116 +++++--- src/services/bridge/errors.ts | 12 + src/services/bridge/index.ts | 245 ++++++++++++----- .../middleware/verify-signature.ts | 24 +- .../bridge/webhook-server/routes/deposit.ts | 30 +- .../bridge/webhook-server/routes/kyc.ts | 36 ++- src/services/ibex/client.ts | 119 ++++++-- src/services/ibex/types.ts | 4 +- .../webhook-server/routes/crypto-receive.ts | 4 +- src/services/lock/index.ts | 7 +- src/services/mongoose/accounts.ts | 12 +- src/services/mongoose/bridge-accounts.ts | 48 +++- src/services/mongoose/schema.ts | 21 +- src/services/mongoose/schema.types.d.ts | 2 +- test/flash/unit/services/bridge/index.spec.ts | 260 ++++++++++++++++++ 42 files changed, 1397 insertions(+), 203 deletions(-) create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeCreateVirtualAccount.bru create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateKyc.bru create mode 100644 dev/bruno/Flash GraphQL API/token/queries/bridgeKycStatus.bru create mode 100644 dev/bruno/Flash GraphQL API/token/queries/bridgeVirtualAccount.bru create mode 100644 src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts create mode 100644 test/flash/unit/services/bridge/index.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 0b7a93596..7f2bcb3a7 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -264,6 +264,89 @@ input BankAccountInput currency: String! } +type BridgeAddExternalAccountPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + externalAccount: BridgeExternalAccount +} + +type BridgeCreateVirtualAccountPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + virtualAccount: BridgeVirtualAccount +} + +type BridgeExternalAccount + @join__type(graph: PUBLIC) +{ + accountNumberLast4: String! + bankName: String! + id: ID! + status: String! +} + +input BridgeInitiateKycInput + @join__type(graph: PUBLIC) +{ + email: String + full_name: String + type: String +} + +type BridgeInitiateKycPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + kycLink: BridgeKycLink +} + +input BridgeInitiateWithdrawalInput + @join__type(graph: PUBLIC) +{ + amount: String! + externalAccountId: ID! +} + +type BridgeInitiateWithdrawalPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + withdrawal: BridgeWithdrawal +} + +type BridgeKycLink + @join__type(graph: PUBLIC) +{ + kycLink: String! + tosLink: String! +} + +type BridgeVirtualAccount + @join__type(graph: PUBLIC) +{ + accountNumber: String + accountNumberLast4: String + bankName: String + id: ID + kycLink: String + message: String + pending: Boolean + routingNumber: String + tosLink: String +} + +type BridgeWithdrawal + @join__type(graph: PUBLIC) +{ + amount: String! + createdAt: String! + currency: String! + id: ID! + status: String! +} + """ A wallet belonging to an account which contains a BTC balance and a list of transactions. """ @@ -1101,6 +1184,10 @@ type Mutation accountEnableNotificationChannel(input: AccountEnableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload! accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! + bridgeAddExternalAccount: BridgeAddExternalAccountPayload! + bridgeCreateVirtualAccount: BridgeCreateVirtualAccountPayload! + bridgeInitiateKyc(input: BridgeInitiateKycInput!): BridgeInitiateKycPayload! + bridgeInitiateWithdrawal(input: BridgeInitiateWithdrawalInput!): BridgeInitiateWithdrawalPayload! businessAccountUpgradeRequest(input: BusinessAccountUpgradeRequestInput!): AccountUpgradePayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! @@ -1557,13 +1644,17 @@ type Query @join__type(graph: PUBLIC) { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! + accountUpgradeRequest: AccountUpgradeRequestPayload! + bridgeExternalAccounts: [BridgeExternalAccount] + bridgeKycStatus: String + bridgeVirtualAccount: BridgeVirtualAccount + bridgeWithdrawals: [BridgeWithdrawal] btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] businessMapMarkers: [MapMarker!]! currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload - latestAccountUpgradeRequest: AccountUpgradeRequestPayload! lnInvoicePaymentStatus(input: LnInvoicePaymentStatusInput!): LnInvoicePaymentStatusPayload! me: User mobileVersions: [MobileVersions] @@ -1940,6 +2031,52 @@ type UpgradePayload scalar USDCents @join__type(graph: PUBLIC) +""" +A wallet belonging to an account which contains a USDT balance and a list of transactions. +""" +type UsdtWallet implements Wallet + @join__implements(graph: PUBLIC, interface: "Wallet") + @join__type(graph: PUBLIC) +{ + accountId: ID! + balance: FractionalCentAmount! + id: ID! + lnurlp: Lnurl + + """An unconfirmed incoming onchain balance.""" + pendingIncomingBalance: SignedAmount! + transactions( + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + transactionsByAddress( + """Returns the items that include this address.""" + address: OnChainAddress! + + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + walletCurrency: WalletCurrency! +} + """ A wallet belonging to an account which contains a USD balance and a list of transactions. """ @@ -2333,6 +2470,7 @@ enum WalletCurrency { BTC @join__enumValue(graph: PUBLIC) USD @join__enumValue(graph: PUBLIC) + USDT @join__enumValue(graph: PUBLIC) } """Unique identifier of a wallet""" diff --git a/dev/bruno/Flash GraphQL API/admin/account-update-level.bru b/dev/bruno/Flash GraphQL API/admin/account-update-level.bru index 679cd0e8e..d9fb2ed1e 100644 --- a/dev/bruno/Flash GraphQL API/admin/account-update-level.bru +++ b/dev/bruno/Flash GraphQL API/admin/account-update-level.bru @@ -32,7 +32,7 @@ body:graphql { body:graphql:vars { { "input": { - "uid": "69a6f3aeaa8667d2c0d65922", + "uid": "69f636d77a39a2a9a1807aa9", "level": "THREE", "erpParty": "John Doe" } diff --git a/dev/bruno/Flash GraphQL API/environments/local.bru b/dev/bruno/Flash GraphQL API/environments/local.bru index 9ea0c4934..311dcfcbf 100644 --- a/dev/bruno/Flash GraphQL API/environments/local.bru +++ b/dev/bruno/Flash GraphQL API/environments/local.bru @@ -8,4 +8,6 @@ vars { token: walletId: walletIdUsd: c593736e-5a58-42e4-93fa-dc895856c1f1 + userEmail: maurientesol@gmail.com + userFullName: maurientesol } diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeCreateVirtualAccount.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeCreateVirtualAccount.bru new file mode 100644 index 000000000..740fb6ec7 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeCreateVirtualAccount.bru @@ -0,0 +1,37 @@ +meta { + name: bridgeCreateVirtualAccount + type: graphql + seq: 37 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeCreateVirtualAccount { + bridgeCreateVirtualAccount { + errors { + message + code + } + virtualAccount { + id + bankName + routingNumber + accountNumberLast4 + } + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} \ No newline at end of file diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateKyc.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateKyc.bru new file mode 100644 index 000000000..4d9db31b0 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateKyc.bru @@ -0,0 +1,44 @@ +meta { + name: bridgeInitiateKyc + type: graphql + seq: 36 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeInitiateKyc($input: BridgeInitiateKycInput!) { + bridgeInitiateKyc(input: $input) { + errors { + message + } + kycLink { + kycLink + tosLink + } + } + } +} + +body:graphql:vars { + { + "input": { + "email": "{{userEmail}}", + "type": "individual", + "full_name": "{{userFullName}}" + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/queries/bridgeKycStatus.bru b/dev/bruno/Flash GraphQL API/token/queries/bridgeKycStatus.bru new file mode 100644 index 000000000..c85ab9cb9 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/queries/bridgeKycStatus.bru @@ -0,0 +1,26 @@ +meta { + name: bridgeKycStatus + type: graphql + seq: 19 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + query BridgeKycStatus { + bridgeKycStatus + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/queries/bridgeVirtualAccount.bru b/dev/bruno/Flash GraphQL API/token/queries/bridgeVirtualAccount.bru new file mode 100644 index 000000000..e3e9c0f3e --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/queries/bridgeVirtualAccount.bru @@ -0,0 +1,32 @@ +meta { + name: bridgeVirtualAccount + type: graphql + seq: 18 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + query BridgeVirtualAccount { + bridgeVirtualAccount { + id + bankName + routingNumber + accountNumberLast4 + accountNumber + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index c19cbc19c..2924bef3d 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -13,14 +13,33 @@ ibex: secret: "not-so-secret" bridge: - enabled: false - apiKey: "" - baseUrl: "https://api.bridge.xyz" + enabled: true + apiKey: "sk-test-3bd6463c9cd77c3d8858c60b9997d0c6" + baseUrl: "https://api.sandbox.bridge.xyz/v0" + minWithdrawalAmount: 10 webhook: port: 4009 publicKeys: - kyc: "" - deposit: "" + kyc: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrV+s8CvC0+s1W6vZG52 + 5eozo6W6HzkTcLQMWDoEzQX+ulEoYH2fPuXeupi11MdVLpEqNqYas8LD3BIf/c9H + kK54V8vnXNwoHa5ROp/Gjp3B17q3wGfjLa8bQJoJZFWd9W+e3TjUohCDNpeD/qv+ + bkY2y3b1QixmXKK3REw35sfiEe5NkGMU4aEfXhZieIZ1mKXLsIgsgrIpv9BFwQr5 + +h3R7Vv3hGKVgSZHnRMa9F1/go8v5Au8gj+9w0LxxRJikoJCubI6igaTCivibxuo + QXWfFylw6m7eQTvZDQz70pnUEakofRlvKasetbyKmvLzMhuRHeqsxgi8C4ZCx7MP + dwIDAQAB + -----END PUBLIC KEY----- + deposit: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrV+s8CvC0+s1W6vZG52 + 5eozo6W6HzkTcLQMWDoEzQX+ulEoYH2fPuXeupi11MdVLpEqNqYas8LD3BIf/c9H + kK54V8vnXNwoHa5ROp/Gjp3B17q3wGfjLa8bQJoJZFWd9W+e3TjUohCDNpeD/qv+ + bkY2y3b1QixmXKK3REw35sfiEe5NkGMU4aEfXhZieIZ1mKXLsIgsgrIpv9BFwQr5 + +h3R7Vv3hGKVgSZHnRMa9F1/go8v5Au8gj+9w0LxxRJikoJCubI6igaTCivibxuo + QXWfFylw6m7eQTvZDQz70pnUEakofRlvKasetbyKmvLzMhuRHeqsxgi8C4ZCx7MP + dwIDAQAB + -----END PUBLIC KEY----- transfer: "" timestampSkewMs: 300000 diff --git a/package.json b/package.json index 831e4e618..a8a279b2b 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "start-main": ". ./.env && yarn run build && node --inspect -r ./lib/services/tracing.js ./lib/servers/graphql-main-server.js", "migrate-mongo-up": "migrate-mongo up -f './src/migrations/migrate-mongo-config.js'", "gen-test-jwt": "ts-node ./dev/bin/gen-test-jwt.ts", - "bridge-webhook": "ts-node src/servers/bridge-webhook-server.ts" + "bridge-webhook": ". ./.env && ts-node --transpile-only -r tsconfig-paths/register src/servers/bridge-webhook-server.ts --configPath dev/config/base-config.yaml" }, "engines": { "node": ">=20.18.1 <21" diff --git a/src/app/offers/Validator.ts b/src/app/offers/Validator.ts index 680e6538a..e16b97735 100644 --- a/src/app/offers/Validator.ts +++ b/src/app/offers/Validator.ts @@ -67,7 +67,7 @@ const verifyBankAccount = async (o: ValidationInputs): Promise([ isUsd, cashoutMin, cashoutMax, diff --git a/src/app/wallets/send-on-chain.ts b/src/app/wallets/send-on-chain.ts index 28deaa0e7..a50ed3034 100644 --- a/src/app/wallets/send-on-chain.ts +++ b/src/app/wallets/send-on-chain.ts @@ -75,7 +75,11 @@ export const payOnChainByWalletId = async ({ }) if (validationResult instanceof Error) return validationResult - const resp = await Ibex.sendOnchain(args) + const resp = await Ibex.sendOnchain({ + accountId: args.accountId, + address: args.address, + amount: amount.toIbex(), + }) if (resp instanceof IbexError) return resp let status = IbexAdaptor.toPaymentSendStatus(resp.status) diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index bd04a4e9c..f7703df16 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -45,6 +45,7 @@ type BridgeConfig = { enabled: boolean apiKey: string baseUrl: string + minWithdrawalAmount: number webhook: BridgeWebhook } diff --git a/src/domain/accounts/index.types.d.ts b/src/domain/accounts/index.types.d.ts index c2c11bd2a..3fed86428 100644 --- a/src/domain/accounts/index.types.d.ts +++ b/src/domain/accounts/index.types.d.ts @@ -89,7 +89,7 @@ type Account = { // Bridge integration: bridgeCustomerId?: BridgeCustomerId bridgeKycStatus?: "pending" | "approved" | "rejected" - bridgeTronAddress?: string + bridgeEthereumAddress?: string } // deprecated @@ -180,11 +180,11 @@ interface IAccountsRepository { fields: { bridgeCustomerId?: BridgeCustomerId bridgeKycStatus?: "pending" | "approved" | "rejected" - bridgeTronAddress?: string + bridgeEthereumAddress?: string }, ): Promise - findByBridgeTronAddress(address: string): Promise + findByBridgeEthereumAddress(address: string): Promise findByBridgeCustomerId(customerId: BridgeCustomerId): Promise } diff --git a/src/domain/bitcoin/lightning/invoice-expiration.ts b/src/domain/bitcoin/lightning/invoice-expiration.ts index 886d3bdc8..8608ebf18 100644 --- a/src/domain/bitcoin/lightning/invoice-expiration.ts +++ b/src/domain/bitcoin/lightning/invoice-expiration.ts @@ -15,7 +15,11 @@ export const DEFAULT_EXPIRATIONS = { JMD: { delay: defaultTimeToExpiryInSeconds, delayMinutes: (defaultTimeToExpiryInSeconds / SECS_PER_MIN) as Minutes, - } + }, + USDT: { + delay: defaultTimeToExpiryInSeconds, + delayMinutes: (defaultTimeToExpiryInSeconds / SECS_PER_MIN) as Minutes, + }, } export const invoiceExpirationForCurrency = ( diff --git a/src/domain/shared/index.ts b/src/domain/shared/index.ts index d7b3f887c..72ced5a95 100644 --- a/src/domain/shared/index.ts +++ b/src/domain/shared/index.ts @@ -9,6 +9,7 @@ export * from "./errors" export * from "./error-parsers" export * from "./error-parsers-unknown" export * from "./validation" +export { USDTAmount } from "./MoneyAmount" export const setErrorWarn = (error: DomainError): DomainError => { error.level = ErrorLevel.Warn diff --git a/src/graphql/admin/schema.graphql b/src/graphql/admin/schema.graphql index ab1413521..d858eaf79 100644 --- a/src/graphql/admin/schema.graphql +++ b/src/graphql/admin/schema.graphql @@ -614,6 +614,7 @@ interface Wallet { enum WalletCurrency { BTC USD + USDT } """Unique identifier of a wallet""" diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 67183ead0..95a461d66 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -476,6 +476,14 @@ export const mapError = (error: ApplicationError): CustomApolloError => { logger: baseLogger, }) + case "BridgeInvalidAmountError": + message = error.message || "Amount must be strictly positive with at most 6 decimal places" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeBelowMinimumWithdrawalError": + message = error.message || "Withdrawal amount is below the minimum" + return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeDisabledError": message = "Bridge integration is currently disabled" return new ValidationInternalError({ message, logger: baseLogger }) diff --git a/src/graphql/public/root/mutation/bridge-add-external-account.ts b/src/graphql/public/root/mutation/bridge-add-external-account.ts index 10e4e279b..0c4a2fd19 100644 --- a/src/graphql/public/root/mutation/bridge-add-external-account.ts +++ b/src/graphql/public/root/mutation/bridge-add-external-account.ts @@ -1,5 +1,6 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" import BridgeExternalAccount from "@graphql/public/types/object/bridge-external-account" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" @@ -8,7 +9,7 @@ import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/e const BridgeAddExternalAccountPayload = GT.Object({ name: "BridgeAddExternalAccountPayload", fields: () => ({ - errors: { type: GT.List(GT.NonNull(Error)) }, + errors: { type: GT.NonNullList(IError) }, externalAccount: { type: BridgeExternalAccount }, }), }) diff --git a/src/graphql/public/root/mutation/bridge-create-virtual-account.ts b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts index 55abd4998..487a732f8 100644 --- a/src/graphql/public/root/mutation/bridge-create-virtual-account.ts +++ b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts @@ -1,5 +1,6 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" @@ -8,7 +9,7 @@ import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/e const BridgeCreateVirtualAccountPayload = GT.Object({ name: "BridgeCreateVirtualAccountPayload", fields: () => ({ - errors: { type: GT.List(GT.NonNull(Error)) }, + errors: { type: GT.NonNullList(IError) }, virtualAccount: { type: BridgeVirtualAccount }, }), }) diff --git a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts index 9e3234ee3..89da4f77a 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts @@ -1,5 +1,6 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" import BridgeKycLink from "@graphql/public/types/object/bridge-kyc-link" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" @@ -8,14 +9,27 @@ import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/e const BridgeInitiateKycPayload = GT.Object({ name: "BridgeInitiateKycPayload", fields: () => ({ - errors: { type: GT.List(GT.NonNull(Error)) }, + errors: { type: GT.NonNullList(IError) }, kycLink: { type: BridgeKycLink }, }), }) +const BridgeInitiateKycInput = GT.Input({ + name: "BridgeInitiateKycInput", + fields: () => ({ + email: { type: GT.String }, + type: { type: GT.String }, + full_name: { type: GT.String }, + }), +}) + const bridgeInitiateKyc = GT.Field({ type: GT.NonNull(BridgeInitiateKycPayload), - resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + args: { + input: { type: GT.NonNull(BridgeInitiateKycInput) }, + }, + resolve: async (_, { input }, { domainAccount }: GraphQLPublicContextAuth) => { + const { email, type, full_name } = input if (!BridgeConfig.enabled) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } } @@ -24,7 +38,7 @@ const bridgeInitiateKyc = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } - const result = await BridgeService.initiateKyc(domainAccount.id) + const result = await BridgeService.initiateKyc({ accountId: domainAccount.id, email, type, full_name }) if (result instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(result)] } } diff --git a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts index 738c10c83..afef326e6 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts @@ -1,9 +1,15 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" -import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" +import { + BridgeDisabledError, + BridgeAccountLevelError, + BridgeInvalidAmountError, + BridgeBelowMinimumWithdrawalError, +} from "@services/bridge/errors" const BridgeInitiateWithdrawalInput = GT.Input({ name: "BridgeInitiateWithdrawalInput", @@ -16,7 +22,7 @@ const BridgeInitiateWithdrawalInput = GT.Input({ const BridgeInitiateWithdrawalPayload = GT.Object({ name: "BridgeInitiateWithdrawalPayload", fields: () => ({ - errors: { type: GT.List(GT.NonNull(Error)) }, + errors: { type: GT.NonNullList(IError) }, withdrawal: { type: BridgeWithdrawal }, }), }) @@ -29,6 +35,20 @@ const bridgeInitiateWithdrawal = GT.Field({ resolve: async (_, args, { domainAccount }: GraphQLPublicContextAuth) => { const { amount, externalAccountId } = args.input + // validate the amount is positive and has at most 6 decimal places + if (!/^\d+(\.\d{1,6})?$/.test(amount) || parseFloat(amount) <= 0) { + return { + errors: [mapAndParseErrorForGqlResponse(new BridgeInvalidAmountError())], + } + } + + // validate the amount is greater than the minimum withdrawal amount + if (parseFloat(amount) < BridgeConfig.minWithdrawalAmount) { + return { + errors: [mapAndParseErrorForGqlResponse(new BridgeBelowMinimumWithdrawalError(BridgeConfig.minWithdrawalAmount))], + } + } + if (!BridgeConfig.enabled) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } } diff --git a/src/graphql/public/root/mutation/onchain-payment-send-all.ts b/src/graphql/public/root/mutation/onchain-payment-send-all.ts index b6a2f2003..2f90ff8c9 100644 --- a/src/graphql/public/root/mutation/onchain-payment-send-all.ts +++ b/src/graphql/public/root/mutation/onchain-payment-send-all.ts @@ -10,6 +10,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import { Wallets } from "@app" import { getBalanceForWallet } from "@app/wallets" +import { USDAmount } from "@domain/shared" const OnChainPaymentSendAllInput = GT.Input({ name: "OnChainPaymentSendAllInput", @@ -64,6 +65,9 @@ const OnChainPaymentSendAllMutation = GT.Field< const amount = await getBalanceForWallet({ walletId }) if (amount instanceof Error) return amount + if (!(amount instanceof USDAmount)) { + return { errors: [{ message: "Onchain payments require a USD wallet" }] } + } const result = await Wallets.payOnChainByWalletId({ senderAccount: domainAccount, diff --git a/src/graphql/public/root/query/bridge-virtual-account.ts b/src/graphql/public/root/query/bridge-virtual-account.ts index a563273dd..eebcc6c84 100644 --- a/src/graphql/public/root/query/bridge-virtual-account.ts +++ b/src/graphql/public/root/query/bridge-virtual-account.ts @@ -3,7 +3,7 @@ import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" -import { BridgeDisabledError } from "@services/bridge/errors" +import { BridgeAccountLevelError, BridgeDisabledError } from "@services/bridge/errors" const bridgeVirtualAccount = GT.Field({ type: BridgeVirtualAccount, @@ -14,6 +14,19 @@ const bridgeVirtualAccount = GT.Field({ if (!domainAccount) return null + if (domainAccount.level < 2) { + throw mapAndParseErrorForGqlResponse(new BridgeAccountLevelError()) + } + + // KYC exists but not yet approved + if (domainAccount.bridgeKycStatus !== "approved") { + return { + pending: true, + message: "KYC verification is pending. Please wait for approval.", + } + } + + // KYC approved — return existing virtual account const result = await BridgeService.getVirtualAccount(domainAccount.id) if (result instanceof Error) { throw mapAndParseErrorForGqlResponse(result) diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index 2935c0472..a6e0a50a1 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -241,6 +241,69 @@ input BankAccountInput { currency: String! } +type BridgeAddExternalAccountPayload { + errors: [Error!]! + externalAccount: BridgeExternalAccount +} + +type BridgeCreateVirtualAccountPayload { + errors: [Error!]! + virtualAccount: BridgeVirtualAccount +} + +type BridgeExternalAccount { + accountNumberLast4: String! + bankName: String! + id: ID! + status: String! +} + +input BridgeInitiateKycInput { + email: String + full_name: String + type: String +} + +type BridgeInitiateKycPayload { + errors: [Error!]! + kycLink: BridgeKycLink +} + +input BridgeInitiateWithdrawalInput { + amount: String! + externalAccountId: ID! +} + +type BridgeInitiateWithdrawalPayload { + errors: [Error!]! + withdrawal: BridgeWithdrawal +} + +type BridgeKycLink { + kycLink: String! + tosLink: String! +} + +type BridgeVirtualAccount { + accountNumber: String + accountNumberLast4: String + bankName: String + id: ID + kycLink: String + message: String + pending: Boolean + routingNumber: String + tosLink: String +} + +type BridgeWithdrawal { + amount: String! + createdAt: String! + currency: String! + id: ID! + status: String! +} + type BuildInformation { commitHash: String helmRevision: Int @@ -853,6 +916,10 @@ type Mutation { accountEnableNotificationChannel(input: AccountEnableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload! accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! + bridgeAddExternalAccount: BridgeAddExternalAccountPayload! + bridgeCreateVirtualAccount: BridgeCreateVirtualAccountPayload! + bridgeInitiateKyc(input: BridgeInitiateKycInput!): BridgeInitiateKycPayload! + bridgeInitiateWithdrawal(input: BridgeInitiateWithdrawalInput!): BridgeInitiateWithdrawalPayload! businessAccountUpgradeRequest(input: BusinessAccountUpgradeRequestInput!): AccountUpgradePayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! @@ -1221,13 +1288,17 @@ type PublicWallet { type Query { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! + accountUpgradeRequest: AccountUpgradeRequestPayload! + bridgeExternalAccounts: [BridgeExternalAccount] + bridgeKycStatus: String + bridgeVirtualAccount: BridgeVirtualAccount + bridgeWithdrawals: [BridgeWithdrawal] btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] businessMapMarkers: [MapMarker!]! currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload - latestAccountUpgradeRequest: AccountUpgradeRequestPayload! lnInvoicePaymentStatus(input: LnInvoicePaymentStatusInput!): LnInvoicePaymentStatusPayload! me: User mobileVersions: [MobileVersions] @@ -1577,6 +1648,49 @@ type UsdWallet implements Wallet { walletCurrency: WalletCurrency! } +""" +A wallet belonging to an account which contains a USDT balance and a list of transactions. +""" +type UsdtWallet implements Wallet { + accountId: ID! + balance: FractionalCentAmount! + id: ID! + lnurlp: Lnurl + + """An unconfirmed incoming onchain balance.""" + pendingIncomingBalance: SignedAmount! + transactions( + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + transactionsByAddress( + """Returns the items that include this address.""" + address: OnChainAddress! + + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + walletCurrency: WalletCurrency! +} + type User { """Bank accounts available for cashout""" bankAccounts: [BankAccount!]! @@ -1849,6 +1963,7 @@ interface Wallet { enum WalletCurrency { BTC USD + USDT } """Unique identifier of a wallet""" diff --git a/src/graphql/public/types/object/bridge-virtual-account.ts b/src/graphql/public/types/object/bridge-virtual-account.ts index eef1c4468..f2c294b83 100644 --- a/src/graphql/public/types/object/bridge-virtual-account.ts +++ b/src/graphql/public/types/object/bridge-virtual-account.ts @@ -3,10 +3,15 @@ import { GT } from "@graphql/index" const BridgeVirtualAccount = GT.Object({ name: "BridgeVirtualAccount", fields: () => ({ - id: { type: GT.NonNullID }, - bankName: { type: GT.NonNull(GT.String) }, - routingNumber: { type: GT.NonNull(GT.String) }, - accountNumberLast4: { type: GT.NonNull(GT.String) }, + id: { type: GT.ID, resolve: (src) => src.virtualAccountId ?? src.bridgeVirtualAccountId }, + bankName: { type: GT.String }, + routingNumber: { type: GT.String }, + accountNumber: { type: GT.String }, + accountNumberLast4: { type: GT.String }, + pending: { type: GT.Boolean }, + message: { type: GT.String }, + kycLink: { type: GT.String }, + tosLink: { type: GT.String }, }), }) diff --git a/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts b/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts new file mode 100644 index 000000000..3f6f6f71a --- /dev/null +++ b/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts @@ -0,0 +1,103 @@ +// @ts-nocheck +/* eslint @typescript-eslint/no-var-requires: "off" */ + +/** + * Migration: unique index on bridgevirtualaccounts.accountId + * + * Background + * ---------- + * BridgeService.createVirtualAccount now carries an idempotency guard that + * uses a MongoDB upsert keyed on accountId. The upsert is only atomic against + * concurrent writes if a unique index backs the filter field. Without it two + * racing writes can both pass the findOneAndUpdate "find" phase, both insert, + * and produce duplicate VAs for the same account. + * + * What this migration does + * ------------------------ + * 1. Audits the collection for any existing accountId duplicates. + * 2. For each duplicate group, keeps the oldest document (earliest createdAt) + * and removes the rest. Removed _ids are logged so they can be reconciled + * against Bridge if necessary. + * 3. Drops any existing plain index on accountId (Mongoose created it as + * index:true before this change). + * 4. Creates the new unique index { accountId: 1 }. + * + * Rollback (down) + * --------------- + * Drops the unique index and restores a plain index so the app schema stays + * consistent with a pre-migration schema.ts. + */ + +const COLLECTION = "bridgevirtualaccounts" +const INDEX_NAME = "accountId_1" + +module.exports = { + async up(db) { + const col = db.collection(COLLECTION) + + // ── Step 1: find duplicate accountId groups ────────────────────────────── + const duplicates = await col + .aggregate([ + { $group: { _id: "$accountId", count: { $sum: 1 }, docs: { $push: "$$ROOT" } } }, + { $match: { count: { $gt: 1 } } }, + ]) + .toArray() + + if (duplicates.length > 0) { + console.log( + `[migration] Found ${duplicates.length} accountId(s) with duplicate VA records. Deduplicating...`, + ) + + for (const group of duplicates) { + // Sort ascending by createdAt — keep the winner (index 0), remove the rest + const sorted = group.docs.sort( + (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(), + ) + const [winner, ...losers] = sorted + const loserIds = losers.map((d) => d._id) + + console.log( + `[migration] accountId=${group._id} — keeping _id=${winner._id} bridgeVAId=${winner.bridgeVirtualAccountId}, removing ${loserIds.length} duplicate(s): ${loserIds.join(", ")}`, + ) + + await col.deleteMany({ _id: { $in: loserIds } }) + } + + console.log("[migration] Deduplication complete.") + } else { + console.log("[migration] No duplicate accountId records found. Proceeding.") + } + + // ── Step 2: drop stale plain index if it exists ────────────────────────── + const existingIndexes = await col.indexes() + const hasPlainIndex = existingIndexes.some( + (idx) => idx.name === INDEX_NAME && !idx.unique, + ) + if (hasPlainIndex) { + await col.dropIndex(INDEX_NAME) + console.log(`[migration] Dropped existing non-unique index "${INDEX_NAME}".`) + } + + // ── Step 3: create unique index ────────────────────────────────────────── + await col.createIndex({ accountId: 1 }, { unique: true, name: INDEX_NAME }) + console.log(`[migration] Created unique index "${INDEX_NAME}" on ${COLLECTION}.`) + }, + + async down(db) { + const col = db.collection(COLLECTION) + + // Drop the unique index + const existingIndexes = await col.indexes() + const hasUniqueIndex = existingIndexes.some( + (idx) => idx.name === INDEX_NAME && idx.unique, + ) + if (hasUniqueIndex) { + await col.dropIndex(INDEX_NAME) + console.log(`[migration] Dropped unique index "${INDEX_NAME}".`) + } + + // Restore plain index so Mongoose schema (pre-change) stays consistent + await col.createIndex({ accountId: 1 }, { name: INDEX_NAME }) + console.log(`[migration] Restored plain index "${INDEX_NAME}" on ${COLLECTION}.`) + }, +} diff --git a/src/servers/bridge-webhook-server.ts b/src/servers/bridge-webhook-server.ts index b05763936..6dd964ab2 100644 --- a/src/servers/bridge-webhook-server.ts +++ b/src/servers/bridge-webhook-server.ts @@ -1,8 +1,9 @@ -/** - * Bridge Webhook Server Entrypoint - * Starts the standalone Bridge webhook server - */ - import { startBridgeWebhookServer } from "@services/bridge/webhook-server" +import { baseLogger } from "@services/logger" +import { setupMongoConnection } from "@services/mongodb" -startBridgeWebhookServer() +if (require.main === module) { + setupMongoConnection() + .then(async () => startBridgeWebhookServer()) + .catch((err) => baseLogger.error(err, "bridge webhook server error")) +} diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index f8e64dfd2..6052cc983 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -3,7 +3,9 @@ * Ported from bridge-mcp and extended with Tron/USDT support */ +import crypto from "crypto" import { BridgeConfig } from "@config" + import { BridgeCustomerId, BridgeVirtualAccountId, @@ -36,16 +38,16 @@ export interface CreateIndividualCustomerRequest { last_name: string email: string phone?: string - address?: { + residential_address?: { street_line_1: string street_line_2?: string city: string - state?: string + subdivision?: string postal_code: string country: string } birth_date?: string - tax_identification_number?: string + signed_agreement_id?: string } export interface CreateBusinessCustomerRequest { @@ -53,15 +55,15 @@ export interface CreateBusinessCustomerRequest { business_name: string email: string phone?: string - address?: { + residential_address?: { street_line_1: string street_line_2?: string city: string - state?: string + subdivision?: string postal_code: string country: string } - ein?: string + signed_agreement_id?: string } export type CreateCustomerRequest = @@ -71,8 +73,8 @@ export type CreateCustomerRequest = export interface Customer { id: string type: "individual" | "business" - kyc_status: string - tos_status: string + status?: "active" | "awaiting_questionnaire" | "rejected" | "paused" | "under_review" | "offboarded" | "awaiting_ubo" | "incomplete" | "not_started" + has_accepted_terms_of_service?: string created_at: string updated_at: string first_name?: string @@ -88,44 +90,53 @@ export interface KycLink { } // Extended payment rails to include Tron -export type PaymentRail = - | "solana" - | "ethereum" - | "polygon" - | "base" - | "tron" - | "ach_push" - | "ach_pull" - | "wire" - | "sepa" - | "spei" - | "pix" +export type PaymentRail = "ach" | "wire" | "ach_push" | "ach_same_day" | "arbitrum" | "avalanche_c_chain" | "base" | "bre_b" | "co_bank_transfer" | "celo" | "ethereum" | "faster_payments" | "optimism" | "pix" | "polygon" | "sepa" | "solana" | "spei" | "stellar" | "swift" | "tempo" | "tron"; -// Extended currencies to include USDT -export type Currency = +export type VirtualAccountDestinationPaymentRail = "arbitrum" | "avalanche_c_chain" | "base" | "celo" | "ethereum" | "optimism" | "polygon" | "solana" | "stellar" | "tempo" | "tron" + + +export type SourceCurrency = | "usd" | "eur" | "mxn" | "brl" | "gbp" - | "usdc" - | "usdb" - | "eurc" - | "usdt" + | "cop" + +// Extended currencies to include USDT +export type Currency = "usdb" | "usdt" | "dai" | "pyusd" | "usdc" | "eurc" export interface CreateVirtualAccountRequest { developer_fee_percent?: string source: { - currency: "usd" | "eur" | "mxn" | "brl" | "gbp" + currency: SourceCurrency } destination: { currency: Currency - payment_rail: PaymentRail + payment_rail: VirtualAccountDestinationPaymentRail address?: string + blockchain_memo?: string bridge_wallet_id?: string } } +export interface CreateExternalAccountRequest { + account_owner_name: string + address: { + street_line_1: string + city: string + country: string + } + account_type: string | "us" | "iban" | "unknown" | "clabe" | "pix" | "gb" + currency: 'usd' | 'gbp' | 'brl' | 'eur' | string + account: { + account_number: string + routing_number: string + checking_or_savings?: "checking" | "savings" + } + bank_name?: string +} + export interface VirtualAccount { id: string status: string @@ -134,18 +145,17 @@ export interface VirtualAccount { source_deposit_instructions: { currency: string payment_rails: string[] - bank_name?: string - bank_address?: string - bank_beneficiary_name?: string - bank_account_number?: string - bank_routing_number?: string - iban?: string - bic?: string + bank_name: string + bank_beneficiary_address: string + bank_beneficiary_name: string + bank_account_number: string + bank_routing_number: string } destination: { currency: string payment_rail: string address?: string + blockchain_memo?: string bridge_wallet_id?: string } created_at: string @@ -154,6 +164,7 @@ export interface VirtualAccount { export interface ExternalAccount { id: string customer_id: string + account_owner_name: string account_type: string currency: string bank_name?: string @@ -174,6 +185,7 @@ export interface ListResponse { cursor?: string } +export type TrasfertSourceCurrency = "brl" | "cop" | "dai" | "eur" | "eurc" | "gbp" | "mxn" | "pyusd" | "usd" | "usdb" | "usdc" | "usdt" export interface CreateTransferRequest { amount?: string currency?: string @@ -182,7 +194,7 @@ export interface CreateTransferRequest { developer_fee_percent?: string source: { payment_rail: PaymentRail | "bridge_wallet" - currency: string + currency: TrasfertSourceCurrency from_address?: string external_account_id?: string bridge_wallet_id?: string @@ -240,6 +252,13 @@ export interface Transfer { updated_at: string } + +export interface BridgeIntiateKyc { + email: string + type: "individual" | "business" + full_name?: string +} + // ============ Bridge Client ============ export class BridgeClient { @@ -248,7 +267,7 @@ export class BridgeClient { constructor() { this.apiKey = BridgeConfig.apiKey - this.baseUrl = BridgeConfig.baseUrl || "https://api.bridge.xyz/v0" + this.baseUrl = BridgeConfig.baseUrl || "https://api.sandbox.bridge.xyz/v0" } private async request( @@ -265,8 +284,11 @@ export class BridgeClient { if (idempotencyKey) { headers["Idempotency-Key"] = idempotencyKey + } else { + headers["Idempotency-Key"] = crypto.randomUUID() } + const response = await fetch(url, { method, headers, @@ -301,8 +323,12 @@ export class BridgeClient { // ============ KYC ============ - async createKycLink(customerId: BridgeCustomerId): Promise { - return this.request("POST", "/kyc_links", { customer_id: customerId }) + async createKycLink(request: BridgeIntiateKyc, idempotencyKey?: string): Promise { + return this.request("POST", "/kyc_links", request, idempotencyKey) + } + + async getKycLatestLink(customerId: BridgeCustomerId): Promise { + return this.request("GET", `/customers/${customerId}/kyc_links/latest`) } // ============ Virtual Accounts ============ @@ -322,6 +348,18 @@ export class BridgeClient { // ============ External Accounts ============ + async createExternalAccount( + customerId: BridgeCustomerId, + data: CreateExternalAccountRequest, + idempotencyKey: string, + ): Promise { + return this.request( + "POST", + `/customers/${customerId}/external_accounts`, + data, + idempotencyKey); + } + async getExternalAccountLinkUrl( customerId: BridgeCustomerId, ): Promise { diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index 737869fba..69340d4b0 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -57,6 +57,18 @@ export class BridgeAccountLevelError extends BridgeError { } } +export class BridgeBelowMinimumWithdrawalError extends BridgeError { + constructor(minimum: number) { + super(`Withdrawal amount is below the minimum of ${minimum} USDT`) + } +} + +export class BridgeInvalidAmountError extends BridgeError { + constructor(message: string = "Amount must be strictly positive with at most 6 decimal places") { + super(message) + } +} + export class BridgeDisabledError extends BridgeError { constructor(message: string = "Bridge integration is currently disabled") { super(message) diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index fca80b89d..ff73924bd 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -4,6 +4,8 @@ * for USD on/off-ramp functionality via Bridge.xyz */ +import crypto from "crypto" + import { BridgeConfig } from "@config" import BridgeClient, { KycLink, @@ -16,6 +18,7 @@ import { AccountsRepository } from "@services/mongoose/accounts" import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" import { baseLogger } from "@services/logger" import { + BridgeError, BridgeDisabledError, BridgeAccountLevelError, BridgeKycPendingError, @@ -24,11 +27,18 @@ import { } from "./errors" import { RepositoryError } from "@domain/errors" import { toBridgeCustomerId, toBridgeExternalAccountId } from "@domain/primitives/bridge" +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import { USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletsRepository } from "@services/mongoose/wallets" +import { BridgeInsufficientFundsError } from "./errors" +import { IdentityRepository } from "@services/kratos" +import IbexClient from "@services/ibex/client" // ============ Types ============ type InitiateKycResult = { kycLink: string + customerId: string tosLink: string } @@ -36,6 +46,7 @@ type CreateVirtualAccountResult = { virtualAccountId: string bankName: string routingNumber: string + accountNumber: string accountNumberLast4: string } @@ -65,6 +76,7 @@ type VirtualAccountResult = { bridgeVirtualAccountId: string bankName: string routingNumber: string + accountNumber: string accountNumberLast4: string } | null @@ -75,8 +87,14 @@ type ExternalAccountResult = { status: "pending" | "verified" | "failed" } +// ============ Helpers ============ + +export const deriveWithdrawalIdempotencyKey = (rowId: string): string => + crypto.createHash("sha256").update(`withdrawal:${rowId}`).digest("hex") + // ============ Guards ============ + const checkBridgeEnabled = (): true | BridgeDisabledError => { if (!BridgeConfig.enabled) { return new BridgeDisabledError() @@ -102,7 +120,7 @@ const checkAccountLevel = async ( * - Creates Bridge customer if not exists * - Returns KYC and TOS links */ -const initiateKyc = async (accountId: AccountId): Promise => { +const initiateKyc = async ({ accountId, email, type, full_name }: { accountId: AccountId, email: string, type?: "individual" | "business", full_name: string }): Promise => { baseLogger.info({ accountId, operation: "initiateKyc" }, "Bridge operation started") const enabledCheck = checkBridgeEnabled() @@ -111,39 +129,45 @@ const initiateKyc = async (accountId: AccountId): Promise w.currency === WalletCurrency.Usdt) + if (!usdtWallet) { + return new BridgeInsufficientFundsError("No USDT wallet found on account") + } + const balance = await getBalanceForWallet({ + walletId: usdtWallet.id, + currency: WalletCurrency.Usdt, + }) + if (balance instanceof Error) return balance + + if (!(balance instanceof USDTAmount)) { + return new BridgeInsufficientFundsError("Invalid balance type") + } + const withdrawalAmount = parseFloat(amount) + if (isNaN(withdrawalAmount) || withdrawalAmount <= 0) { + return new BridgeInsufficientFundsError("Invalid withdrawal amount") + } + + const availableBalance = balance.toIbex(); + if (availableBalance < withdrawalAmount) { + baseLogger.warn( + { accountId, availableBalance, withdrawalAmount, operation: "initiateWithdrawal" }, + "Insufficient USDT balance for withdrawal", + ) + return new BridgeInsufficientFundsError( + `Insufficient USDT balance: available ${availableBalance}, requested ${withdrawalAmount}`, + ) } - // Verify external account exists + // CRIT-2 (ENG-281): Verify caller owns this external account (ownership enforced here + // and at DB level via compound index — see schema.ts BridgeExternalAccountSchema) const externalAccounts = await BridgeAccountsRepo.findExternalAccountsByAccountId( accountId as string, ) @@ -346,38 +438,41 @@ const initiateWithdrawal = async ( (acc) => acc.bridgeExternalAccountId === externalAccountId, ) if (!targetAccount) { + // Do not leak existence — return same error regardless of whether account exists return new Error("External account not found") } if (targetAccount.status !== "verified") { return new Error("External account is not verified") } + + // Store withdrawal record + const pendingWithdrawal = await BridgeAccountsRepo.createWithdrawal({ + accountId: accountId as string, + amount: amount, + currency: "usdt", + externalAccountId, + status: "pending", + }) + if (pendingWithdrawal instanceof Error) return pendingWithdrawal + + const idempotencyKey = deriveWithdrawalIdempotencyKey(pendingWithdrawal.id) + // Create transfer via Bridge const transfer = await BridgeClient.createTransfer(customerId, { amount, on_behalf_of: customerId, source: { - payment_rail: "tron", + payment_rail: "ethereum", currency: "usdt", - from_address: tronAddress, + from_address: ethereumAddress, }, destination: { payment_rail: "ach", currency: "usd", external_account_id: externalAccountId, }, - }) - - // Store withdrawal record - const withdrawalResult = await BridgeAccountsRepo.createWithdrawal({ - accountId: accountId as string, - bridgeTransferId: transfer.id, - amount: transfer.amount, - currency: transfer.currency, - externalAccountId, - status: "pending", - }) - if (withdrawalResult instanceof Error) return withdrawalResult + }, idempotencyKey) const result: InitiateWithdrawalResult = { transferId: transfer.id, @@ -386,6 +481,15 @@ const initiateWithdrawal = async ( state: transfer.state, } + const withdrawalResult = await BridgeAccountsRepo.updateWithdrawalTransferId( + pendingWithdrawal.id, + transfer.id, + transfer.amount, + transfer.currency, + ) + + if (withdrawalResult instanceof Error) return withdrawalResult + baseLogger.info( { accountId, operation: "initiateWithdrawal", transferId: transfer.id }, "Bridge operation completed", @@ -455,9 +559,10 @@ const getVirtualAccount = async ( } const result: VirtualAccountResult = { - bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId, + bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId!, bankName: virtualAccount.bankName, routingNumber: virtualAccount.routingNumber, + accountNumber: virtualAccount.accountNumber, accountNumberLast4: virtualAccount.accountNumberLast4, } @@ -465,7 +570,7 @@ const getVirtualAccount = async ( { accountId, operation: "getVirtualAccount", - virtualAccountId: result.bridgeVirtualAccountId, + virtualAccountId: result!.bridgeVirtualAccountId, }, "Bridge operation completed", ) @@ -545,13 +650,15 @@ const getWithdrawals = async ( ) if (withdrawals instanceof Error) return withdrawals - const result: WithdrawalResult[] = withdrawals.map((w) => ({ - transferId: w.bridgeTransferId, - amount: w.amount, - currency: w.currency, - state: w.status, - createdAt: w.createdAt.toISOString(), - })) + const result: WithdrawalResult[] = withdrawals + .filter((w) => w.bridgeTransferId !== null || w.bridgeTransferId !== undefined) + .map((w) => ({ + transferId: w.bridgeTransferId!, + amount: w.amount, + currency: w.currency, + state: w.status, + createdAt: w.createdAt.toISOString(), + })) baseLogger.info( { accountId, operation: "getWithdrawals", count: result.length }, diff --git a/src/services/bridge/webhook-server/middleware/verify-signature.ts b/src/services/bridge/webhook-server/middleware/verify-signature.ts index b00961288..c4a100ecb 100644 --- a/src/services/bridge/webhook-server/middleware/verify-signature.ts +++ b/src/services/bridge/webhook-server/middleware/verify-signature.ts @@ -30,12 +30,17 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf return res.status(401).json({ error: "Invalid signature format" }) } - const timestamp = timestampPart.split("=")[1] - const sig = signaturePart.split("=")[1] + const timestamp = timestampPart.slice("t=".length) + const sig = signaturePart.slice("v0=".length) // Check timestamp skew (default 5 minutes) const now = Date.now() const timestampMs = parseInt(timestamp, 10) + if (isNaN(timestampMs) || !isFinite(timestampMs)) { + baseLogger.warn({ timestamp }, "Invalid timestamp format in Bridge webhook signature") + return res.status(401).json({ error: "Invalid signature format" }) + } + const skew = Math.abs(now - timestampMs) if (skew > BridgeConfig.webhook.timestampSkewMs) { @@ -45,12 +50,21 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf // Verify signature using Bridge public key const publicKey = BridgeConfig.webhook.publicKeys[publicKeyType] - const rawBody = (req as any).rawBody || JSON.stringify(req.body) - const payload = `${timestamp}.${rawBody}` + const rawBody = (req as any).rawBody + + if (!rawBody) { + baseLogger.warn(`Missing raw body for webhook`) + return res.status(401).json({ error: "Missing raw body" }) + } + + const signedPayload = `${timestamp}.${rawBody}` + + const digest = crypto.createHash("sha256").update(signedPayload).digest() + baseLogger.debug({ signedPayload, digest: digest.toString("hex") }, "Verifying Bridge webhook signature") try { const verifier = crypto.createVerify("RSA-SHA256") - verifier.update(payload) + verifier.update(digest) const isValid = verifier.verify(publicKey, sig, "base64") if (!isValid) { diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index 79d64253a..ccc98866b 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -11,18 +11,18 @@ import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" export const depositHandler = async (req: Request, res: Response) => { - const { event, data } = req.body - const { transfer_id, amount, currency, tx_hash, customer_id } = data + const { event_id, event_object } = req.body + const { id, amount, deposit_id, currency, subtotal_amount, customer_id, receipt } = event_object - if (!transfer_id || !event) { + if (!id || !event_id) { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency check using transfer_id as lock key - const lockKey = `bridge-deposit:${transfer_id}` + // Idempotency check using deposit_id as lock key + const lockKey = `bridge-deposit:${deposit_id}` const lockResult = await LockService().lockIdempotencyKey(lockKey as any) if (lockResult instanceof Error) { - baseLogger.info({ transfer_id }, "Duplicate Bridge deposit webhook") + baseLogger.info({ event_id, id }, "Duplicate Bridge deposit webhook") return res.status(200).json({ status: "already_processed" }) } @@ -31,19 +31,29 @@ export const depositHandler = async (req: Request, res: Response) => { // The actual balance crediting happens via IBEX crypto webhook baseLogger.info( { - transfer_id, + id, amount, + initial_amount: receipt.initial_amount, currency, - tx_hash, + deposit_id, + receipt: { + url: receipt.url, + initial_amount: receipt.initial_amount, + subtotal_amount: receipt.subtotal_amount, + final_amount: receipt.final_amount, + exchange_fee: receipt.exchange_fee, + gas_fee: receipt.gas_fee, + }, customer_id, - event, + event_id, + }, "Bridge deposit completed", ) return res.status(200).json({ status: "success" }) } catch (error) { - baseLogger.error({ error, transfer_id }, "Error processing Bridge deposit webhook") + baseLogger.error({ error, id, event_id, deposit_id }, "Error processing Bridge deposit webhook") return res.status(500).json({ error: "Internal server error" }) } } diff --git a/src/services/bridge/webhook-server/routes/kyc.ts b/src/services/bridge/webhook-server/routes/kyc.ts index b69dfda08..7a8783742 100644 --- a/src/services/bridge/webhook-server/routes/kyc.ts +++ b/src/services/bridge/webhook-server/routes/kyc.ts @@ -8,34 +8,35 @@ import { AccountsRepository } from "@services/mongoose/accounts" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" import { toBridgeCustomerId } from "@domain/primitives/bridge" +import BridgeService from "@services/bridge" export const kycHandler = async (req: Request, res: Response) => { - const { event, data } = req.body - const { customer_id, kyc_status, reason } = data - if (!customer_id || !event) { + const { event_id, event_object } = req.body + const { customer_id, kyc_status, rejection_reasons } = event_object + + if (!customer_id || !event_id) { return res.status(400).json({ error: "Invalid payload" }) } // Idempotency check using customer_id + event as lock key - const lockKey = `bridge-kyc:${customer_id}:${event}` + const lockKey = `bridge-kyc:${customer_id}:${event_object.kyc_status}:${event_object.id}` const lockResult = await LockService().lockIdempotencyKey(lockKey as any) if (lockResult instanceof Error) { - baseLogger.info({ customer_id, event }, "Duplicate Bridge KYC webhook") + baseLogger.info({ customer_id, event_id }, "Duplicate Bridge KYC webhook") return res.status(200).json({ status: "already_processed" }) } try { const bridgeCustomerId = toBridgeCustomerId(customer_id) const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) - if (account instanceof Error) { - baseLogger.error({ customer_id }, "Account not found for Bridge customer") - return res.status(404).json({ error: "Account not found" }) + baseLogger.warn({ customer_id }, "Account not found for Bridge customer — may be a timing issue, Bridge will retry") + return res.status(503).json({ error: "Account not ready" }) } // Update KYC status based on event - if (event === "kyc.approved") { + if (kyc_status === "approved") { const result = await AccountsRepository().updateBridgeFields(account.id, { bridgeKycStatus: "approved", }) @@ -49,7 +50,20 @@ export const kycHandler = async (req: Request, res: Response) => { } baseLogger.info({ accountId: account.id, customer_id }, "Bridge KYC approved") - } else if (event === "kyc.rejected") { + + const vaResult = await BridgeService.createVirtualAccount(account.id) + if (vaResult instanceof Error) { + baseLogger.error( + { accountId: account.id, error: vaResult }, + "Failed to auto-create virtual account after KYC approval", + ) + } else { + baseLogger.info( + { accountId: account.id, virtualAccountId: vaResult.virtualAccountId }, + "Virtual account auto-created after KYC approval", + ) + } + } else if (kyc_status === "rejected") { const result = await AccountsRepository().updateBridgeFields(account.id, { bridgeKycStatus: "rejected", }) @@ -66,7 +80,7 @@ export const kycHandler = async (req: Request, res: Response) => { { accountId: account.id, customer_id, - reason, + rejection_reasons, }, "Bridge KYC rejected", ) diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index 2630e6def..c4e243584 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -226,15 +226,75 @@ const payToLnurl = async ( }).then(errorHandler) } +const getIbexToken = async (): Promise => { + const cached = await Ibex.authentication.storage.getAccessToken() + if (typeof cached === "string") return `Bearer ${cached}` + + // The SDK uses a single base URL for all calls, but the sandbox auth domain is separate + const resp = await fetch(`${IbexConfig.authUrl}/auth/signin`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email: IbexConfig.email, password: IbexConfig.password }), + }).catch((err: unknown) => new IbexError(err instanceof Error ? err : new Error(String(err)))) + + if (resp instanceof IbexError) return resp + if (!resp.ok) { + const body = await resp.text().catch(() => "") + return new IbexError(new Error(`IBEX sign-in failed: ${resp.status} — ${body}`)) + } + + const data = await resp.json() as { + accessToken?: string + accessTokenExpiresAt?: number + refreshToken?: string + refreshTokenExpiresAt?: number + } + if (!data.accessToken) return new IbexError(new Error("IBEX sign-in: no access token in response")) + + await Ibex.authentication.storage.setAccessToken(data.accessToken, data.accessTokenExpiresAt) + if (data.refreshToken) { + await Ibex.authentication.storage.setRefreshToken(data.refreshToken, data.refreshTokenExpiresAt) + } + + return `Bearer ${data.accessToken}` +} + +const ibexFetch = async ( + token: string, + path: string, + init: RequestInit = {}, +): Promise => { + const url = `${IbexConfig.url}${path}` + const resp = await fetch(url, { + ...init, + headers: { Authorization: token, "Content-Type": "application/json", ...init.headers }, + }) + if (!resp.ok) { + const body = await resp.text().catch(() => "") + baseLogger.error({ url, status: resp.status, body }, "IBEX request failed") + return new IbexError(new Error(`IBEX ${path} failed: ${resp.status} — ${body}`)) + } + return resp.json() as Promise +} + +const ibexGet = (token: string, path: string) => + ibexFetch(token, path, { method: "GET" }) + +const ibexPost = (token: string, path: string, body: unknown) => + ibexFetch(token, path, { method: "POST", body: JSON.stringify(body) }) + const getCryptoReceiveBalance = async ( receiveInfoId: string, ): Promise => { try { - const resp = await (Ibex as any).getCryptoReceiveBalance({ receiveInfoId }) - if (resp instanceof Error) return new IbexError(resp) - if (resp.balance === null || resp.balance === undefined) - return new UnexpectedIbexResponse("Balance not found") - const balance = USDTAmount.smallestUnits(resp.balance.toString()) + const token = await getIbexToken() + if (token instanceof IbexError) return token + const data = await ibexGet<{ balance: number }>( + token, + `/crypto/receive-infos/${receiveInfoId}/balance`, + ) + if (data instanceof IbexError) return data + const balance = USDTAmount.smallestUnits(data.balance?.toString()) if (balance instanceof Error) return new IbexError(balance) return balance } catch (err) { @@ -244,26 +304,34 @@ const getCryptoReceiveBalance = async ( const getCryptoReceiveOptions = async (): Promise => { try { - const resp = await (Ibex as any).getCryptoReceiveOptions() - if (resp instanceof Error) return new IbexError(resp) - return resp.options || [] + const token = await getIbexToken() + if (token instanceof IbexError) return token + const data = await ibexGet<{ options: CryptoReceiveOption[] }>( + token, + "/crypto/receive-infos/options", + ) + if (data instanceof IbexError) return data + return data.options || [] } catch (err) { return new IbexError(err instanceof Error ? err : new Error(String(err))) } } const createCryptoReceiveInfo = async ( - walletId: IbexAccountId, - optionId: string, + accountId: IbexAccountId, + option: Pick, ): Promise => { try { - const resp = await (Ibex as any).createCryptoReceiveInfo({ - wallet_id: walletId, - option_id: optionId, - } as CreateCryptoReceiveInfoRequest) - if (resp instanceof Error) return new IbexError(resp) - if (!resp.address) return new UnexpectedIbexResponse("Address not found") - return resp + const token = await getIbexToken() + if (token instanceof IbexError) return token + const data = await ibexPost( + token, + `/accounts/${accountId}/crypto/receive-infos`, + { name: option.name, network: option.network } as CreateCryptoReceiveInfoRequest, + ) + if (data instanceof IbexError) return data + if (!data.address) return new UnexpectedIbexResponse("Address not found") + return data } catch (err) { return new IbexError(err instanceof Error ? err : new Error(String(err))) } @@ -285,6 +353,22 @@ const getTronUsdtOption = async (): Promise => { return tronUsdt.id } +const getEthereumUsdtOption = async (): Promise => { + const options = await getCryptoReceiveOptions() + if (options instanceof IbexError) return options + + const ethereumUsdt = options.find( + (opt) => + opt.currency.toLowerCase() === "usdt" && opt.network.toLowerCase() === "ethereum", + ) + + if (!ethereumUsdt) { + return new IbexError(new Error("Ethereum USDT option not found")) + } + + return ethereumUsdt +} + // const sendBetweenAccounts = async ( // sender: IbexAccount, // receiver: IbexAccount, @@ -326,5 +410,6 @@ export default wrapAsyncFunctionsToRunInSpan({ getCryptoReceiveOptions, createCryptoReceiveInfo, getTronUsdtOption, + getEthereumUsdtOption, }, }) diff --git a/src/services/ibex/types.ts b/src/services/ibex/types.ts index 145cfa1d7..4a02bf260 100644 --- a/src/services/ibex/types.ts +++ b/src/services/ibex/types.ts @@ -55,6 +55,6 @@ export interface CryptoReceiveInfo { } export interface CreateCryptoReceiveInfoRequest { - wallet_id: string - option_id: string + name: string + network: string } diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index 7fe2283a5..914b12f7c 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -30,9 +30,9 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { const lockResult = await LockService().lockPaymentHash(tx_hash as any, async () => { try { - const account = await AccountsRepository().findByBridgeTronAddress(address) + const account = await AccountsRepository().findByBridgeEthereumAddress(address) if (account instanceof Error) { - baseLogger.error({ address, tx_hash }, "Account not found for Tron address") + baseLogger.error({ address, tx_hash }, "Account not found for Ethereum address") return { status: "error", code: "account_not_found" } as CryptoReceiveResult } diff --git a/src/services/lock/index.ts b/src/services/lock/index.ts index 9d7acea34..9b72e9d41 100644 --- a/src/services/lock/index.ts +++ b/src/services/lock/index.ts @@ -155,7 +155,12 @@ export const LockService = (): ILockService => { ): Promise => { const path = getIdempotencyKeyLockResource(idempotencyKey) - await timelock({ resource: path, duration: durationLockIdempotencyKey }) + try { + await timelock({ resource: path, duration: durationLockIdempotencyKey }) + } catch (error) { + if (error instanceof ExecutionError) return error + throw error + } } return wrapAsyncFunctionsToRunInSpan({ diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index 7e658daa6..9a8842bcf 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -178,7 +178,7 @@ export const AccountsRepository = (): IAccountsRepository => { fields: { bridgeCustomerId?: BridgeCustomerId bridgeKycStatus?: "pending" | "approved" | "rejected" - bridgeTronAddress?: string + bridgeEthereumAddress?: string }, ): Promise => { try { @@ -194,12 +194,12 @@ export const AccountsRepository = (): IAccountsRepository => { } } - const findByBridgeTronAddress = async ( + const findByBridgeEthereumAddress = async ( address: string, ): Promise => { try { - const result = await Account.findOne({ bridgeTronAddress: address }) - if (!result) return new RepositoryError("Account not found for Tron address") + const result = await Account.findOne({ bridgeEthereumAddress: address }) + if (!result) return new RepositoryError("Account not found for Ethereum address") return translateToAccount(result) } catch (error) { return parseRepositoryError(error) @@ -228,7 +228,7 @@ export const AccountsRepository = (): IAccountsRepository => { findByNpub, update, updateBridgeFields, - findByBridgeTronAddress, + findByBridgeEthereumAddress, findByBridgeCustomerId, } } @@ -293,5 +293,5 @@ const translateToAccount = (result: AccountRecord): Account => ({ displayCurrency: (result.displayCurrency || UsdDisplayCurrency) as DisplayCurrency, bridgeCustomerId: result.bridgeCustomerId as BridgeCustomerId | undefined, bridgeKycStatus: result.bridgeKycStatus, - bridgeTronAddress: result.bridgeTronAddress, + bridgeEthereumAddress: result.bridgeEthereumAddress, }) diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index caeb7867b..6877d092e 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -13,10 +13,17 @@ export const createVirtualAccount = async (data: { bridgeVirtualAccountId: string bankName: string routingNumber: string + accountNumber: string accountNumberLast4: string }) => { try { - const record = await BridgeVirtualAccount.create(data) + // Atomic upsert: if a doc for this accountId already exists (concurrent call won the + // race), $setOnInsert is skipped and we get back the winner's record — no duplicate. + const record = await BridgeVirtualAccount.findOneAndUpdate( + { accountId: data.accountId }, + { $setOnInsert: data }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) return record } catch (error) { return new RepositoryError(String(error)) @@ -89,7 +96,7 @@ export const updateExternalAccountStatus = async ( export const createWithdrawal = async (data: { accountId: string - bridgeTransferId: string + bridgeTransferId?: string amount: string currency: string externalAccountId: string @@ -103,6 +110,43 @@ export const createWithdrawal = async (data: { } } +export const findPendingWithdrawalWithoutTransfer = async ( + accountId: string, + externalAccountId: string, + amount: string, +) => { + try { + const record = await BridgeWithdrawal.findOne({ + accountId, + externalAccountId, + amount, + bridgeTransferId: { $exists: false }, + status: "pending", + }) + return record // null when no in-flight row exists + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const updateWithdrawalTransferId = async ( + id: string, + bridgeTransferId: string, + amount: string, + currency: string, +) => { + try { + const record = await BridgeWithdrawal.findByIdAndUpdate( + id, + { bridgeTransferId, amount, currency, updatedAt: new Date() }, + { new: true }, + ) + return record || new RepositoryError("Withdrawal not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + export const findWithdrawalsByAccountId = async (accountId: string) => { try { const records = await BridgeWithdrawal.find({ accountId }).sort({ createdAt: -1 }) diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index d13cccada..dd0b9aac9 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -23,6 +23,7 @@ interface IBridgeVirtualAccountRecord { bridgeVirtualAccountId: string bankName: string routingNumber: string + accountNumber: string accountNumberLast4: string createdAt: Date } @@ -38,7 +39,7 @@ interface IBridgeExternalAccountRecord { interface IBridgeWithdrawalRecord { accountId: string - bridgeTransferId: string + bridgeTransferId?: string amount: string currency: string status: "pending" | "completed" | "failed" @@ -335,7 +336,7 @@ const AccountSchema = new Schema( enum: ["pending", "approved", "rejected"], required: false, }, - bridgeTronAddress: { + bridgeEthereumAddress: { type: String, required: false, }, @@ -348,7 +349,7 @@ AccountSchema.index({ coordinates: 1, }) -AccountSchema.index({ bridgeTronAddress: 1 }, { sparse: true }) +AccountSchema.index({ bridgeEthereumAddress: 1 }, { sparse: true }) export const Account = mongoose.model("Account", AccountSchema) @@ -614,10 +615,12 @@ export const WalletOnChainPendingReceive = ) const BridgeVirtualAccountSchema = new Schema({ - accountId: { type: String, required: true, index: true }, + // unique: true enforces one VA per account at the DB layer — idempotency guard + accountId: { type: String, required: true, unique: true }, bridgeVirtualAccountId: { type: String, required: true, unique: true }, bankName: { type: String, required: true }, routingNumber: { type: String, required: true }, + accountNumber: { type: String, required: true }, accountNumberLast4: { type: String, required: true }, createdAt: { type: Date, default: Date.now }, }) @@ -631,9 +634,17 @@ const BridgeExternalAccountSchema = new Schema({ createdAt: { type: Date, default: Date.now }, }) +// CRIT-2 (ENG-281): Compound index enforces that a given bridgeExternalAccountId +// can only be associated with one accountId at the DB layer, preventing cross-account +// withdrawal attacks even if application-layer ownership checks are bypassed. +BridgeExternalAccountSchema.index( + { accountId: 1, bridgeExternalAccountId: 1 }, + { unique: true }, +) + const BridgeWithdrawalSchema = new Schema({ accountId: { type: String, required: true, index: true }, - bridgeTransferId: { type: String, required: true, unique: true }, + bridgeTransferId: { type: String, unique: true, sparse: true }, amount: { type: String, required: true }, currency: { type: String, required: true }, status: { type: String, enum: ["pending", "completed", "failed"], default: "pending" }, diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 57016ec0a..51c86757c 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -97,7 +97,7 @@ interface AccountRecord { // Bridge integration: bridgeCustomerId?: string bridgeKycStatus?: "pending" | "approved" | "rejected" - bridgeTronAddress?: string + bridgeEthereumAddress?: string // mongoose in-built functions save: () => Promise diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts new file mode 100644 index 000000000..20aa5f9ad --- /dev/null +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -0,0 +1,260 @@ +import crypto from "crypto" + +jest.mock("@services/tracing", () => ({ + wrapAsyncFunctionsToRunInSpan: ({ + fns, + }: { + namespace: string + fns: Record unknown> + }) => fns, +})) + +jest.mock("@config", () => ({ + BridgeConfig: { enabled: true, minWithdrawalAmount: 10 }, +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/bridge-accounts", () => ({ + createWithdrawal: jest.fn(), + findPendingWithdrawalWithoutTransfer: jest.fn(), + findExternalAccountsByAccountId: jest.fn(), + updateWithdrawalTransferId: jest.fn(), +})) + +jest.mock("@services/bridge/client", () => ({ + __esModule: true, + default: { createTransfer: jest.fn() }, +})) + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/wallets", () => ({ + WalletsRepository: jest.fn(), +})) + +jest.mock("@app/wallets/get-balance-for-wallet", () => ({ + getBalanceForWallet: jest.fn(), +})) + +jest.mock("@services/kratos", () => ({ + IdentityRepository: jest.fn(), +})) + +jest.mock("@domain/primitives/bridge", () => ({ + toBridgeCustomerId: (id: string) => id, + toBridgeExternalAccountId: (id: string) => id, +})) + +// USDTAmount is not re-exported from @domain/shared — provide a minimal stand-in so the +// service's `instanceof USDTAmount` guard is satisfied during tests. +// The class is defined inside the factory because jest.mock factories are hoisted before +// variable declarations; access it at runtime via require("@domain/shared").USDTAmount. +// USDTAmount is not re-exported from @domain/shared/index.ts (pre-existing issue). +// Spread the real module and inject a minimal stand-in so the service's +// `instanceof USDTAmount` guard is satisfied without breaking other domain exports. +jest.mock("@domain/shared", () => { + class USDTAmount { + constructor(private readonly ibexValue: number) {} + toIbex() { + return this.ibexValue + } + } + return { ...jest.requireActual("@domain/shared"), USDTAmount } +}) + +import BridgeService, { deriveWithdrawalIdempotencyKey } from "@services/bridge" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import BridgeClient from "@services/bridge/client" +import { AccountsRepository } from "@services/mongoose/accounts" +import { WalletsRepository } from "@services/mongoose/wallets" +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" + +// ── Fixtures ────────────────────────────────────────────────────────────────── + +const ACCOUNT_ID = "account-001" as AccountId +const EXTERNAL_ACCOUNT_ID = "ext-account-001" +const AMOUNT = "50" +const CUSTOMER_ID = "cust-001" +const ETHEREUM_ADDRESS = "ETH_ADDR_001" +const TRANSFER_ID = "transfer-bridge-001" + +const mockAccount = { + id: ACCOUNT_ID, + level: 2, + bridgeCustomerId: CUSTOMER_ID, + bridgeEthereumAddress: ETHEREUM_ADDRESS, + bridgeKycStatus: "approved", + kratosUserId: "kratos-001", +} + +const makeRow = (id: string) => ({ + id, + accountId: ACCOUNT_ID as string, + amount: AMOUNT, + currency: "usdt", + externalAccountId: EXTERNAL_ACCOUNT_ID, + status: "pending" as const, + bridgeTransferId: undefined, +}) + +const mockTransfer = { + id: TRANSFER_ID, + amount: AMOUNT, + currency: "usd", + state: "pending", +} + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const setupGuards = () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const { USDTAmount } = require("@domain/shared") + const balance = new USDTAmount(1000) // 1000 USDT — well above minWithdrawalAmount + + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + }) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest.fn().mockResolvedValue([{ id: "wallet-001", currency: "USDT" }]), + }) + ;(getBalanceForWallet as jest.Mock).mockResolvedValue(balance) + ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ + { bridgeExternalAccountId: EXTERNAL_ACCOUNT_ID, status: "verified" }, + ]) + ;(BridgeAccountsRepo.updateWithdrawalTransferId as jest.Mock).mockResolvedValue({ + ...makeRow("any"), + bridgeTransferId: TRANSFER_ID, + }) + ;(BridgeClient.createTransfer as jest.Mock).mockResolvedValue(mockTransfer) +} + +// ── Tests ───────────────────────────────────────────────────────────────────── + +describe("deriveWithdrawalIdempotencyKey", () => { + it("returns sha256(\"withdrawal:\") as a hex string", () => { + const rowId = "507f1f77bcf86cd799439011" + const expected = crypto + .createHash("sha256") + .update(`withdrawal:${rowId}`) + .digest("hex") + + expect(deriveWithdrawalIdempotencyKey(rowId)).toBe(expected) + }) + + it("produces distinct keys for distinct row IDs", () => { + expect(deriveWithdrawalIdempotencyKey("id-alpha")).not.toBe( + deriveWithdrawalIdempotencyKey("id-beta"), + ) + }) + + it("is deterministic — same input always returns same output", () => { + const rowId = "507f1f77bcf86cd799439011" + expect(deriveWithdrawalIdempotencyKey(rowId)).toBe(deriveWithdrawalIdempotencyKey(rowId)) + }) + + it("output is a 64-character lowercase hex string (sha256)", () => { + const key = deriveWithdrawalIdempotencyKey("any-id") + expect(key).toMatch(/^[0-9a-f]{64}$/) + }) +}) + +describe("initiateWithdrawal — idempotency key wiring", () => { + beforeEach(() => { + jest.clearAllMocks() + setupGuards() + }) + + describe("fresh request (no in-flight row)", () => { + it("creates a pending withdrawal row before calling Bridge", async () => { + const row = makeRow("fresh-row-001") + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + null, + ) + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + const createOrder = (BridgeAccountsRepo.createWithdrawal as jest.Mock).mock + .invocationCallOrder[0] + const transferOrder = (BridgeClient.createTransfer as jest.Mock).mock.invocationCallOrder[0] + expect(createOrder).toBeLessThan(transferOrder) + }) + + it("derives the idempotency key from the newly created row's id", async () => { + const rowId = "fresh-row-id-abc" + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + null, + ) + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(makeRow(rowId)) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + const expectedKey = deriveWithdrawalIdempotencyKey(rowId) + expect(BridgeClient.createTransfer).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.any(Object), + expectedKey, + ) + }) + }) + + describe("retry — in-flight row already exists", () => { + it("does not create a second withdrawal row", async () => { + const existingRow = makeRow("existing-row-001") + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + existingRow, + ) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + }) + + it("derives the key from the existing row's id — identical to the first attempt's key", async () => { + const rowId = "existing-row-001" + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + makeRow(rowId), + ) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + const expectedKey = deriveWithdrawalIdempotencyKey(rowId) + expect(BridgeClient.createTransfer).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.any(Object), + expectedKey, + ) + }) + }) + + describe("two rapid calls for the same request", () => { + it("pass the same idempotency key to Bridge — collapsing into one transfer", async () => { + // Call 1: no in-flight row → creates row A + // Call 2: finds row A (created by call 1) → reuses its id + const rowId = "shared-row-concurrent" + const row = makeRow(rowId) + + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock) + .mockResolvedValueOnce(null) // call 1: nothing in-flight yet + .mockResolvedValueOnce(row) // call 2: row A now visible + + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + const calls = (BridgeClient.createTransfer as jest.Mock).mock.calls + expect(calls).toHaveLength(2) + + const key1 = calls[0][2] + const key2 = calls[1][2] + expect(key1).toBe(key2) + expect(key1).toBe(deriveWithdrawalIdempotencyKey(rowId)) + }) + }) +}) From 05739f55ff733a4e5b28b8c97c8af138a1a51a73 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:22 -0400 Subject: [PATCH 03/43] ENG-276 feat(bridge): add reconciliation and replay tooling --- .../admin/account-update-level.bru | 2 +- .../Flash GraphQL API/environments/local.bru | 4 +- dev/config/base-config.yaml | 1 + .../ENG-276-IMPLEMENTATION-RESUME.md | 308 ++++++++++++++++++ package.json | 6 +- src/config/schema.ts | 1 + src/config/schema.types.d.ts | 1 + src/scripts/reconcile-bridge-ibex-deposits.ts | 34 ++ src/scripts/replay-bridge-events.ts | 195 +++++++++++ src/servers/cron.ts | 11 +- src/services/bridge/client.ts | 104 +++++- src/services/bridge/reconciliation.ts | 155 +++++++++ src/services/bridge/webhook-server/index.ts | 6 + .../bridge/webhook-server/routes/deposit.ts | 66 ++-- .../bridge/webhook-server/routes/replay.ts | 239 ++++++++++++++ .../webhook-server/routes/crypto-receive.ts | 107 +++--- src/services/mongoose/bridge-deposit-log.ts | 22 ++ .../mongoose/bridge-reconciliation-orphan.ts | 27 ++ src/services/mongoose/bridge-replay-log.ts | 42 +++ .../mongoose/ibex-crypto-receive-log.ts | 51 +++ src/services/mongoose/schema.ts | 90 ++++- .../flash/unit/services/bridge/client.spec.ts | 152 +++++++++ .../bridge/webhook-server/deposit.spec.ts | 225 +++++++++++++ .../bridge/webhook-server/replay.spec.ts | 296 +++++++++++++++++ tsconfig-build.json | 8 +- 25 files changed, 2077 insertions(+), 76 deletions(-) create mode 100644 docs/bridge-integration/ENG-276-IMPLEMENTATION-RESUME.md create mode 100644 src/scripts/reconcile-bridge-ibex-deposits.ts create mode 100644 src/scripts/replay-bridge-events.ts create mode 100644 src/services/bridge/reconciliation.ts create mode 100644 src/services/bridge/webhook-server/routes/replay.ts create mode 100644 src/services/mongoose/bridge-deposit-log.ts create mode 100644 src/services/mongoose/bridge-reconciliation-orphan.ts create mode 100644 src/services/mongoose/bridge-replay-log.ts create mode 100644 src/services/mongoose/ibex-crypto-receive-log.ts create mode 100644 test/flash/unit/services/bridge/client.spec.ts create mode 100644 test/flash/unit/services/bridge/webhook-server/deposit.spec.ts create mode 100644 test/flash/unit/services/bridge/webhook-server/replay.spec.ts diff --git a/dev/bruno/Flash GraphQL API/admin/account-update-level.bru b/dev/bruno/Flash GraphQL API/admin/account-update-level.bru index d9fb2ed1e..244ae7d0f 100644 --- a/dev/bruno/Flash GraphQL API/admin/account-update-level.bru +++ b/dev/bruno/Flash GraphQL API/admin/account-update-level.bru @@ -32,7 +32,7 @@ body:graphql { body:graphql:vars { { "input": { - "uid": "69f636d77a39a2a9a1807aa9", + "uid": "69fb995b012e5336d69a0f1f", "level": "THREE", "erpParty": "John Doe" } diff --git a/dev/bruno/Flash GraphQL API/environments/local.bru b/dev/bruno/Flash GraphQL API/environments/local.bru index 311dcfcbf..2c8d6c6bd 100644 --- a/dev/bruno/Flash GraphQL API/environments/local.bru +++ b/dev/bruno/Flash GraphQL API/environments/local.bru @@ -8,6 +8,6 @@ vars { token: walletId: walletIdUsd: c593736e-5a58-42e4-93fa-dc895856c1f1 - userEmail: maurientesol@gmail.com - userFullName: maurientesol + userEmail: maurienteso@gmail.com + userFullName: maurienteso } diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index 2924bef3d..cc4a36249 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -19,6 +19,7 @@ bridge: minWithdrawalAmount: 10 webhook: port: 4009 + replaySecret: "also-not-so-secret" publicKeys: kyc: | -----BEGIN PUBLIC KEY----- diff --git a/docs/bridge-integration/ENG-276-IMPLEMENTATION-RESUME.md b/docs/bridge-integration/ENG-276-IMPLEMENTATION-RESUME.md new file mode 100644 index 000000000..52c0ef316 --- /dev/null +++ b/docs/bridge-integration/ENG-276-IMPLEMENTATION-RESUME.md @@ -0,0 +1,308 @@ +# ENG-276 Implementation Resume + +## Context + +`ENG-276` targets operational reliability for Bridge on-ramp deposits by closing the gap between: + +- Bridge transfer/deposit lifecycle events +- IBEX crypto receive settlement events +- Operator tooling needed to replay and triage failures + +The issue also folds in two related reliability needs: + +- Persist Bridge fee on every deposit log row +- Provide replay capability for stuck webhook handlers + +In practice, Flash receives information from two systems that represent different parts of the same real-world flow: + +1. Bridge emits webhook events for transfer state transitions. +2. IBEX emits crypto receive webhooks when USDT settlement is observed. + +Without reconciliation and replay tooling, operations cannot quickly identify: + +- Bridge events that never materialized in IBEX +- IBEX settlements with no corresponding Bridge event +- Stuck handlers needing replay + +--- + +## What We Are Solving + +### Primary objective (ENG-276 acceptance scope) + +1. Orphan events must be surfaced with triage context (ops visibility). +2. Replay CLI must rerun stuck handlers for a chosen transfer id. +3. Bridge fee value must be persisted on every deposit row. + +### Supporting objective + +Ensure replay handling matches Bridge webhook envelope format documented by Bridge: + +- `event_type` can be generic (`updated.status_transitioned`) +- `event_object_status` carries status transitions like `funds_received` +- `event_object` carries the resource payload +- `event_created_at` carries event timestamp + +Reference: +- [Bridge webhook event structure](https://apidocs.bridge.xyz/platform/additional-information/webhooks/structure) +- [Bridge list webhook events](https://apidocs.bridge.xyz/api-reference/webhooks/list-webhook-events) + +--- + +## How It Was Solved + +## 1) AC3: Fee persisted on every deposit row + +### Changes + +- Deposit handler now always computes `developerFee` with fallback chain: + 1. `receipt.developer_fee` + 2. `event_object.developer_fee` + 3. `"0"` +- `developerFee` made required/defaulted in persistence path. + +### Result + +Every `BridgeDepositLog` row has a fee value, including null/missing upstream fee cases. + +--- + +## 2) AC1: Orphan detection and ops surfacing + +### Changes + +- Added IBEX receive log persistence. +- Added reconciliation service comparing 24h Bridge deposits vs IBEX receives. +- Added orphan persistence model with rich triage context. +- Added reconciliation execution in cron and manual script entrypoint. + +### Result + +Ops can now see explicit orphan records with: + +- orphan type (`bridge_without_ibex` / `ibex_without_bridge`) +- correlation keys (transfer id / tx hash) +- detection window +- reason and context required for triage + +--- + +## 3) AC2: Replay by chosen transfer id + +### Changes + +- Replay CLI supports `--transfer-id`. +- Replay filtering extracts transfer id from supported object shapes. +- Replay route accepts canonical Bridge envelope and maps it to existing handlers. + +### Result + +Ops can target replay for a specific transfer id rather than replaying all events in a window. + +--- + +## 4) Bridge documentation alignment fixes + +### Event list API fix + +Bridge webhook events listing uses: + +- `GET /webhook_events` +- query params like `starting_after`, `limit`, `category` + +Client was updated to use this endpoint and parameter mapping while preserving internal caller interface. + +### GET idempotency fix + +Bridge rejects `Idempotency-Key` on certain GET endpoints (including webhook events list). +Client request logic now sends `Idempotency-Key` only on non-GET methods. + +--- + +## Reconciliation Sequence Diagram (Step-by-Step) + +```mermaid +sequenceDiagram + autonumber + participant Cron as Cron Job / Manual Script + participant Rec as reconcileBridgeAndIbexDeposits + participant BDL as BridgeDepositLog (Mongo) + participant IBL as IbexCryptoReceiveLog (Mongo) + participant ORP as BridgeReconciliationOrphan (Mongo) + participant Ops as Ops Tooling / Operator + + Cron->>Rec: Start reconciliation(window=24h) + Rec->>BDL: Query Bridge deposits in window (state=funds_received) + BDL-->>Rec: Bridge deposit set + Rec->>IBL: Query IBEX receives in same window + IBL-->>Rec: IBEX receive set + + Note over Rec: Build hash maps by tx hash for fast matching + + loop For each Bridge deposit + alt Missing destinationTxHash + Rec->>ORP: Upsert orphan (bridge_without_ibex)
reason=no destinationTxHash + else destinationTxHash not found in IBEX set + Rec->>ORP: Upsert orphan (bridge_without_ibex)
reason=no IBEX match in window + else Matched + Rec-->>Rec: No orphan + end + end + + loop For each IBEX receive + alt tx hash not found in Bridge set + Rec->>ORP: Upsert orphan (ibex_without_bridge)
reason=no Bridge match in window + else Matched + Rec-->>Rec: No orphan + end + end + + Rec-->>Cron: Return summary counts + Cron-->>Ops: Log reconciliation summary + Ops->>ORP: Inspect orphans with triage context +``` + +--- + +## Manual Validation Summary + +## Curl Samples (Manual Console Tests) + +Set base variables: + +```bash +BASE_URL="http://localhost:4009" +REPLAY_SECRET="also-not-so-secret" +``` + +AC3 Case A (receipt fee present; expected persisted `developerFee = "0.5"`): + +```bash +curl -s -X POST "$BASE_URL/internal/replay" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $REPLAY_SECRET" \ + -d '{ + "event_id":"wh_ac3_a_101", + "event_type":"updated.status_transitioned", + "event_object_status":"funds_received", + "event_object":{ + "id":"tr_ac3_a_101", + "state":"funds_received", + "amount":"10.00", + "currency":"usd", + "developer_fee":"0.2", + "on_behalf_of":"cust_a", + "receipt":{"developer_fee":"0.5","initial_amount":"10.00","subtotal_amount":"9.50","final_amount":"9.50","destination_tx_hash":"tx_ac3_a_101"} + }, + "event_created_at":"2026-05-06T10:00:00.000Z", + "operator":"manual-test", + "time_window_start":"2026-05-06T00:00:00.000Z", + "time_window_end":"2026-05-06T23:59:59.000Z", + "dry_run":false + }' +``` + +AC3 Case B (receipt fee null; fallback to `event_object.developer_fee`, expected `"0.7"`): + +```bash +curl -s -X POST "$BASE_URL/internal/replay" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $REPLAY_SECRET" \ + -d '{ + "event_id":"wh_ac3_b_101", + "event_type":"updated.status_transitioned", + "event_object_status":"funds_received", + "event_object":{ + "id":"tr_ac3_b_101", + "state":"funds_received", + "amount":"20.00", + "currency":"usd", + "developer_fee":"0.7", + "on_behalf_of":"cust_b", + "receipt":{"developer_fee":null,"initial_amount":"20.00","subtotal_amount":"19.30","final_amount":"19.30","destination_tx_hash":"tx_ac3_b_101"} + }, + "event_created_at":"2026-05-06T10:05:00.000Z", + "operator":"manual-test", + "time_window_start":"2026-05-06T00:00:00.000Z", + "time_window_end":"2026-05-06T23:59:59.000Z", + "dry_run":false + }' +``` + +AC3 Case C (both missing/null; expected default `"0"`): + +```bash +curl -s -X POST "$BASE_URL/internal/replay" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $REPLAY_SECRET" \ + -d '{ + "event_id":"wh_ac3_c_101", + "event_type":"updated.status_transitioned", + "event_object_status":"funds_received", + "event_object":{ + "id":"tr_ac3_c_101", + "state":"funds_received", + "amount":"30.00", + "currency":"usd", + "on_behalf_of":"cust_c", + "receipt":{"developer_fee":null,"initial_amount":"30.00","subtotal_amount":"30.00","final_amount":"30.00","destination_tx_hash":"tx_ac3_c_101"} + }, + "event_created_at":"2026-05-06T10:10:00.000Z", + "operator":"manual-test", + "time_window_start":"2026-05-06T00:00:00.000Z", + "time_window_end":"2026-05-06T23:59:59.000Z", + "dry_run":false + }' +``` + +AC2 replay command (transfer-id targeted replay): + +```bash +BRIDGE_WEBHOOK_REPLAY_SECRET="$REPLAY_SECRET" BRIDGE_WEBHOOK_URL="$BASE_URL" \ +yarn replay-bridge-events --configPath dev/config/base-config.yaml \ + --start 2026-05-01T00:00:00Z --end 2026-05-07T00:00:00Z \ + --event-type transfer --transfer-id tr_ac3_b_101 +``` + +AC1 reconciliation command: + +```bash +. ./.env && yarn reconcile-bridge-ibex-deposits --configPath dev/config/base-config.yaml --window-hours 24 +``` + +DB verification query (without `mongosh`, using node + mongoose): + +```bash +. ./.env && node -e "const mongoose=require('mongoose'); (async()=>{await mongoose.connect(process.env.MONGODB_CON); const db=mongoose.connection.db; const deposit=await db.collection('bridgedepositlogs').find({transferId:{\$in:['tr_ac3_a_101','tr_ac3_b_101','tr_ac3_c_101']}},{projection:{_id:0,transferId:1,developerFee:1,destinationTxHash:1,createdAt:1}}).sort({createdAt:-1}).toArray(); const orphans=await db.collection('bridgereconciliationorphans').find({txHash:{\$in:['tx_ac3_a_101','tx_ac3_b_101','tx_ac3_c_101']}},{projection:{_id:0,orphanType:1,orphanKey:1,transferId:1,txHash:1,detectedAt:1}}).sort({detectedAt:-1}).toArray(); console.log(JSON.stringify({deposit, orphans}, null, 2)); await mongoose.disconnect(); })().catch(async(e)=>{console.error(e); try{await mongoose.disconnect();}catch{} process.exit(1);});" +``` + +### AC3 checks + +Three manual replay cases confirmed persisted values: + +- Case A: receipt fee present -> persisted that receipt fee +- Case B: receipt fee null -> persisted fallback `event_object.developer_fee` +- Case C: both missing/null -> persisted `"0"` + +### AC1 checks + +Reconciliation run produced orphan rows and summary counts, confirming surfacing path. + +### AC2 checks + +Replay command path works with transfer-id filter; endpoint-level issues were resolved by: + +- switching to documented webhook events listing endpoint +- removing idempotency header on GET + +--- + +## Final State + +`ENG-276` goals are implemented with: + +- deterministic fee persistence +- reconciliation + orphan triage surface +- replay targeting for specific transfers +- Bridge API/documentation-aligned event retrieval and envelope handling + diff --git a/package.json b/package.json index a8a279b2b..8f7903bd9 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,9 @@ "start-main": ". ./.env && yarn run build && node --inspect -r ./lib/services/tracing.js ./lib/servers/graphql-main-server.js", "migrate-mongo-up": "migrate-mongo up -f './src/migrations/migrate-mongo-config.js'", "gen-test-jwt": "ts-node ./dev/bin/gen-test-jwt.ts", - "bridge-webhook": ". ./.env && ts-node --transpile-only -r tsconfig-paths/register src/servers/bridge-webhook-server.ts --configPath dev/config/base-config.yaml" + "bridge-webhook": ". ./.env && ts-node --transpile-only -r tsconfig-paths/register src/servers/bridge-webhook-server.ts --configPath dev/config/base-config.yaml", + "replay-bridge-events": "yarn build && node lib/scripts/replay-bridge-events.js", + "reconcile-bridge-ibex-deposits": "yarn build && node lib/scripts/reconcile-bridge-ibex-deposits.js" }, "engines": { "node": ">=20.18.1 <21" @@ -210,4 +212,4 @@ "**/**/mongoose": "~7.5.1" }, "private": true -} +} \ No newline at end of file diff --git a/src/config/schema.ts b/src/config/schema.ts index 479bc0081..80124a74e 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -662,6 +662,7 @@ export const configSchema = { required: ["kyc", "deposit", "transfer"], }, timestampSkewMs: { type: "integer" }, + replaySecret: { type: "string" } }, required: ["port", "publicKeys", "timestampSkewMs"], }, diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index f7703df16..f13477a2d 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -39,6 +39,7 @@ type BridgeWebhook = { port: number publicKeys: BridgeWebhookPublicKeys timestampSkewMs: number + replaySecret?: string } type BridgeConfig = { diff --git a/src/scripts/reconcile-bridge-ibex-deposits.ts b/src/scripts/reconcile-bridge-ibex-deposits.ts new file mode 100644 index 000000000..7e643bc0f --- /dev/null +++ b/src/scripts/reconcile-bridge-ibex-deposits.ts @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +import yargs from "yargs" +import { hideBin } from "yargs/helpers" +import { setupMongoConnection } from "@services/mongodb" +import { baseLogger } from "@services/logger" +import { reconcileBridgeAndIbexDeposits } from "@services/bridge/reconciliation" + +const args = yargs(hideBin(process.argv)) + .option("window-hours", { + type: "number", + default: 24, + describe: "Reconciliation window in hours", + }) + .option("configPath", { type: "string", demandOption: true }) + .parseSync() + +const main = async () => { + const windowMs = Math.max(1, Math.floor(args["window-hours"])) * 60 * 60 * 1000 + const result = await reconcileBridgeAndIbexDeposits({ windowMs }) + if (result instanceof Error) throw result + baseLogger.info(result, "Bridge↔IBEX reconciliation finished") +} + +setupMongoConnection() + .then(async (mongoose) => { + await main() + await mongoose?.connection.close() + process.exit(0) + }) + .catch((error) => { + baseLogger.error({ error }, "Bridge↔IBEX reconciliation failed") + process.exit(1) + }) diff --git a/src/scripts/replay-bridge-events.ts b/src/scripts/replay-bridge-events.ts new file mode 100644 index 000000000..915e46bb0 --- /dev/null +++ b/src/scripts/replay-bridge-events.ts @@ -0,0 +1,195 @@ +#!/usr/bin/env node + +/** + * Operator tool: replay missed Bridge webhook events. + * + * Usage: + * BRIDGE_WEBHOOK_REPLAY_SECRET= BRIDGE_WEBHOOK_URL=http://localhost:4009 \ + * node lib/scripts/replay-bridge-events.js \ + * --configPath dev/config/base-config.yaml \ + * --start 2026-05-01T00:00:00Z \ + * --end 2026-05-02T00:00:00Z \ + * [--event-type kyc|deposit|transfer] \ + * [--dry-run] \ + * [--operator "ops@example.com"] + */ + +import { listAllEvents } from "@services/bridge/client" +import { baseLogger } from "@services/logger" +import { setupMongoConnection } from "@services/mongodb" +import yargs from "yargs" +import { hideBin } from "yargs/helpers" + +const args = yargs(hideBin(process.argv)) + .option("start", { type: "string", demandOption: true }) + .option("end", { type: "string", demandOption: true }) + .option("event-type", { type: "string", choices: ["kyc", "deposit", "transfer"] }) + .option("transfer-id", { + type: "string", + describe: "Replay only events for this transfer ID", + }) + .option("dry-run", { type: "boolean", default: false }) + .option("operator", { type: "string", default: "unknown" }) + .option("configPath", { type: "string", demandOption: true }) + .parseSync() + +const REPLAY_SECRET = process.env.BRIDGE_WEBHOOK_REPLAY_SECRET +const WEBHOOK_URL = process.env.BRIDGE_WEBHOOK_URL ?? "http://localhost:4009" + +if (!REPLAY_SECRET) { + console.error("Error: BRIDGE_WEBHOOK_REPLAY_SECRET environment variable is required") + process.exit(1) +} + +const EVENT_TYPE_FILTER: Record = { + kyc: "kyc.approved", + deposit: "deposit.completed", + transfer: "transfer.completed", +} + +type BridgeReplayEventEnvelope = { + event_id: string + event_type: string + event_object: unknown + event_created_at: string +} + +const toRouteKey = (eventType: string): string | null => { + if (eventType.startsWith("kyc")) return "kyc" + if (eventType.startsWith("deposit")) return "deposit" + if (eventType.startsWith("transfer")) return "transfer" + return null +} + +const extractTransferId = (payload: unknown): string | undefined => { + if (!payload || typeof payload !== "object") return undefined + const candidate = payload as Record + const fromTransferId = candidate.transfer_id + const fromId = candidate.id + const orchestration = candidate.orchestration as Record | undefined + const fromOrchestrationTransferId = orchestration?.transfer_id + + if (typeof fromTransferId === "string") return fromTransferId + if (typeof fromId === "string") return fromId + if (typeof fromOrchestrationTransferId === "string") return fromOrchestrationTransferId + return undefined +} + +const replayEvent = async ( + event: BridgeReplayEventEnvelope, +): Promise<{ status: number; body: unknown }> => { + const routeKey = toRouteKey(event.event_type) + + if (!routeKey) { + baseLogger.warn( + { eventType: event.event_type }, + "Skipping unsupported event type for replay", + ) + return { status: 0, body: { skipped: true, reason: "unsupported event type" } } + } + + const response = await fetch(`${WEBHOOK_URL}/internal/replay`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${REPLAY_SECRET}`, + }, + body: JSON.stringify({ + event_type: event.event_type, + event_object: event.event_object, + event_created_at: event.event_created_at, + operator: args.operator, + time_window_start: args.start, + time_window_end: args.end, + dry_run: args["dry-run"], + }), + }) + + const body = await response.json().catch(() => null) + + return { status: response.status, body } +} + +const main = async () => { + let fetched = 0, + success = 0, + failed = 0, + skipped = 0 + baseLogger.info( + { + start: args.start, + end: args.end, + eventType: args["event-type"], + dryRun: args["dry-run"], + operator: args.operator, + }, + "Starting Bridge webhook replay", + ) + + const bridgeFilter = args["event-type"] + ? EVENT_TYPE_FILTER[args["event-type"] as string] + : undefined + + for await (const event of listAllEvents({ + start_date: args.start, + end_date: args.end, + event_type: bridgeFilter, + })) { + const replayEventObject: BridgeReplayEventEnvelope = { + event_id: event.id, + event_type: event.event_type, + event_created_at: event.created_at, + event_object: event.payload, + } + + if (args["transfer-id"]) { + const transferId = extractTransferId(replayEventObject.event_object) + if (transferId !== args["transfer-id"]) { + skipped++ + continue + } + } + + fetched++ + + const { status, body } = await replayEvent(replayEventObject) + + if (status >= 200 && status < 300) { + success++ + baseLogger.info( + { eventId: event.id, eventType: event.event_type, status, response: body }, + "Successfully replayed event", + ) + } else if (status === 0) { + skipped++ + baseLogger.info( + { eventId: event.id, eventType: event.event_type, status, response: body }, + "Skipped event", + ) + } else { + failed++ + baseLogger.error( + { eventId: event.id, eventType: event.event_type, status, response: body }, + "Failed to replay event", + ) + } + + await new Promise((resolve) => setTimeout(resolve, 100)) // small delay to avoid overwhelming the webhook server + } + + baseLogger.info( + { fetched, success, failed, skipped }, + "Completed Bridge webhook replay", + ) +} + +setupMongoConnection() + .then(async (mongoose) => { + await main() + await mongoose?.connection.close() + process.exit(0) + }) + .catch((error) => { + baseLogger.error({ error }, "Error in Bridge webhook replay") + process.exit(1) + }) diff --git a/src/servers/cron.ts b/src/servers/cron.ts index e139fd212..19511890e 100644 --- a/src/servers/cron.ts +++ b/src/servers/cron.ts @@ -1,6 +1,6 @@ import { OnChain, Lightning, Wallets, Payments, Swap } from "@app" -import { getCronConfig, TWO_MONTHS_IN_MS } from "@config" +import { BridgeConfig, getCronConfig, TWO_MONTHS_IN_MS } from "@config" import { ErrorLevel } from "@domain/shared" import { OperationInterruptedError } from "@domain/errors" @@ -20,6 +20,7 @@ import { import { baseLogger } from "@services/logger" import { setupMongoConnection } from "@services/mongodb" import { activateLndHealthCheck, checkAllLndHealth } from "@services/lnd/health" +import { reconcileBridgeAndIbexDeposits } from "@services/bridge/reconciliation" import { elapsedSinceTimestamp, sleep } from "@utils" import { rebalancingInternalChannels } from "@services/lnd/rebalancing" @@ -64,6 +65,13 @@ const swapOutJob = async () => { if (swapResult instanceof Error) throw swapResult } +const reconcileBridgeDepositsJob = async () => { + if (!BridgeConfig.enabled) return + + const result = await reconcileBridgeAndIbexDeposits() + if (result instanceof Error) throw result +} + const main = async () => { console.log("cronjob started") const start = new Date() @@ -84,6 +92,7 @@ const main = async () => { updateLegacyOnChainReceipt, ...(cronConfig.rebalanceEnabled ? [rebalance] : []), ...(cronConfig.swapEnabled ? [swapOutJob] : []), + reconcileBridgeDepositsJob, deleteExpiredPaymentFlows, deleteExpiredInvoices, deleteLndPaymentsBefore2Months, diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index 6052cc983..51f4ec882 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -259,6 +259,39 @@ export interface BridgeIntiateKyc { full_name?: string } +export type BridgeWebhookEventType = "kyc" | "transfer" | "virtual_account" | "external_account" + +export interface BridgeWebhookEvent { + id: string + event_type: string + payload: unknown + created_at: string +} + +export interface ListEventsParams { + start_date?: string + end_date?: string + event_type?: string + after?: string + page_size?: number +} + +type WebhookEventsApiResponse = { + data: Array<{ + event_id?: string + event_type?: string + event_created_at?: string + event_object?: unknown + id?: string + created_at?: string + payload?: unknown + }> + count?: number + has_more?: boolean + cursor?: string +} + + // ============ Bridge Client ============ export class BridgeClient { @@ -282,10 +315,13 @@ export class BridgeClient { "Content-Type": "application/json", } - if (idempotencyKey) { - headers["Idempotency-Key"] = idempotencyKey - } else { - headers["Idempotency-Key"] = crypto.randomUUID() + // Bridge rejects Idempotency-Key on some GET endpoints (e.g. /webhook_events). + if (method.toUpperCase() !== "GET") { + if (idempotencyKey) { + headers["Idempotency-Key"] = idempotencyKey + } else { + headers["Idempotency-Key"] = crypto.randomUUID() + } } @@ -400,6 +436,66 @@ export class BridgeClient { // Note: Bridge API uses /transfers/{id} not /customers/{id}/transfers/{id} return this.request("GET", `/transfers/${transferId}`) } + + + // ============ List Events ============ + + async listEvents(params?: ListEventsParams): Promise> { + const queryParams = new URLSearchParams() + + // Bridge webhook events endpoint uses cursor pagination via starting_after. + if (params?.after) queryParams.append("starting_after", params.after) + if (params?.page_size) queryParams.append("limit", params.page_size.toString()) + // Preserve call-site compatibility: derive category from event_type when possible. + if (params?.event_type) { + const category = params.event_type.split(".")[0] + if (category) queryParams.append("category", category) + } + + const suffix = queryParams.toString() ? `?${queryParams.toString()}` : "" + + const response = await this.request( + "GET", + `/webhook_events${suffix}`, + ) + + const mappedData: BridgeWebhookEvent[] = (response.data ?? []).map((event) => ({ + id: event.event_id ?? event.id ?? "", + event_type: event.event_type ?? "", + created_at: event.event_created_at ?? event.created_at ?? "", + payload: event.event_object ?? event.payload ?? {}, + })) + + // Keep legacy ListResponse shape for existing callers. + const limit = params?.page_size ?? 100 + const hasMoreFromCount = typeof response.count === "number" && response.count >= limit + const hasMore = response.has_more ?? hasMoreFromCount + const cursor = response.cursor ?? mappedData[mappedData.length - 1]?.id + + return { + data: mappedData, + has_more: Boolean(hasMore && cursor), + cursor, + } + } + } export default new BridgeClient() + +export async function* listAllEvents( + params?: Omit, +): AsyncGenerator { + const client = new BridgeClient() + + let cursor: string | undefined + do { + const page = await client.listEvents({ ...params, after: cursor, page_size: 100 }) + + for (const event of page.data) { + yield event + } + + cursor = page.has_more ? page.cursor : undefined + } while (cursor) +} \ No newline at end of file diff --git a/src/services/bridge/reconciliation.ts b/src/services/bridge/reconciliation.ts new file mode 100644 index 000000000..ec964ba00 --- /dev/null +++ b/src/services/bridge/reconciliation.ts @@ -0,0 +1,155 @@ +import { baseLogger } from "@services/logger" +import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" +import { upsertBridgeReconciliationOrphan } from "@services/mongoose/bridge-reconciliation-orphan" +import { BridgeDepositLog } from "@services/mongoose/schema" + +const ONE_DAY_MS = 24 * 60 * 60 * 1000 + +type BridgeDepositLike = { + eventId: string + transferId: string + customerId: string + amount: string + currency: string + destinationTxHash?: string + state: string + createdAt: Date +} + +type IbexReceiveLike = { + txHash: string + address: string + amount: string + currency: string + network: string + accountId?: string + receivedAt: Date +} + +const toOrphanKey = (prefix: string, value: string) => `${prefix}:${value.toLowerCase()}` + +export const reconcileBridgeAndIbexDeposits = async ({ + windowMs = ONE_DAY_MS, +}: { + windowMs?: number +} = {}): Promise< + | { + scannedBridge: number + scannedIbex: number + bridgeWithoutIbex: number + ibexWithoutBridge: number + } + | Error +> => { + try { + const now = new Date() + const since = new Date(now.getTime() - windowMs) + + const bridgeDeposits = (await BridgeDepositLog.find({ + createdAt: { $gte: since, $lte: now }, + state: "funds_received", + }) + .lean() + .exec()) as BridgeDepositLike[] + + const ibexReceivesResult = await findIbexCryptoReceiveLogsSince({ since, until: now }) + if (ibexReceivesResult instanceof Error) return ibexReceivesResult + const ibexReceives = ibexReceivesResult as IbexReceiveLike[] + + const ibexByTxHash = new Map() + for (const record of ibexReceives) { + ibexByTxHash.set(record.txHash.toLowerCase(), record) + } + + const bridgeByTxHash = new Map() + for (const deposit of bridgeDeposits) { + if (!deposit.destinationTxHash) continue + bridgeByTxHash.set(deposit.destinationTxHash.toLowerCase(), deposit) + } + + let bridgeWithoutIbex = 0 + let ibexWithoutBridge = 0 + + for (const deposit of bridgeDeposits) { + if (!deposit.destinationTxHash) { + bridgeWithoutIbex++ + await upsertBridgeReconciliationOrphan({ + orphanKey: toOrphanKey("bridge-no-tx", deposit.transferId), + orphanType: "bridge_without_ibex", + transferId: deposit.transferId, + bridgeEventId: deposit.eventId, + customerId: deposit.customerId, + amount: deposit.amount, + currency: deposit.currency, + triageContext: { + reason: "Bridge funds_received has no destinationTxHash", + windowStart: since.toISOString(), + windowEnd: now.toISOString(), + depositState: deposit.state, + createdAt: deposit.createdAt.toISOString(), + }, + }) + continue + } + + const matchedIbex = ibexByTxHash.get(deposit.destinationTxHash.toLowerCase()) + if (matchedIbex) continue + + bridgeWithoutIbex++ + await upsertBridgeReconciliationOrphan({ + orphanKey: toOrphanKey("bridge", deposit.destinationTxHash), + orphanType: "bridge_without_ibex", + transferId: deposit.transferId, + txHash: deposit.destinationTxHash, + bridgeEventId: deposit.eventId, + customerId: deposit.customerId, + amount: deposit.amount, + currency: deposit.currency, + triageContext: { + reason: + "No IBEX crypto.receive found for Bridge destinationTxHash within 24h window", + windowStart: since.toISOString(), + windowEnd: now.toISOString(), + depositState: deposit.state, + createdAt: deposit.createdAt.toISOString(), + }, + }) + } + + for (const receive of ibexReceives) { + const matchedBridge = bridgeByTxHash.get(receive.txHash.toLowerCase()) + if (matchedBridge) continue + + ibexWithoutBridge++ + await upsertBridgeReconciliationOrphan({ + orphanKey: toOrphanKey("ibex", receive.txHash), + orphanType: "ibex_without_bridge", + txHash: receive.txHash, + amount: receive.amount, + currency: receive.currency, + triageContext: { + reason: + "No Bridge deposit funds_received found for IBEX tx hash within 24h window", + windowStart: since.toISOString(), + windowEnd: now.toISOString(), + address: receive.address, + network: receive.network, + accountId: receive.accountId, + receivedAt: receive.receivedAt.toISOString(), + }, + }) + } + + const summary = { + scannedBridge: bridgeDeposits.length, + scannedIbex: ibexReceives.length, + bridgeWithoutIbex, + ibexWithoutBridge, + } + + baseLogger.info(summary, "Bridge↔IBEX reconciliation completed") + return summary + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/bridge/webhook-server/index.ts b/src/services/bridge/webhook-server/index.ts index 79b8778a5..22e75154c 100644 --- a/src/services/bridge/webhook-server/index.ts +++ b/src/services/bridge/webhook-server/index.ts @@ -13,6 +13,7 @@ import { verifyBridgeSignature } from "./middleware/verify-signature" import { kycHandler } from "./routes/kyc" import { depositHandler } from "./routes/deposit" import { transferHandler } from "./routes/transfer" +import { replayAuthMiddleware, replayHandler } from "./routes/replay" export const startBridgeWebhookServer = () => { const app = express() @@ -35,6 +36,11 @@ export const startBridgeWebhookServer = () => { app.post("/kyc", verifyBridgeSignature("kyc"), kycHandler) app.post("/deposit", verifyBridgeSignature("deposit"), depositHandler) app.post("/transfer", verifyBridgeSignature("transfer"), transferHandler) + app.post("/internal/replay", replayAuthMiddleware, replayHandler) + + if (!BridgeConfig.webhook.replaySecret && !process.env.BRIDGE_WEBHOOK_REPLAY_SECRET) { + baseLogger.warn("replaySecret not configured (neither BridgeConfig.webhook.replaySecret nor BRIDGE_WEBHOOK_REPLAY_SECRET) — /internal/replay will reject all requests with 503") + } // Start server const port = BridgeConfig.webhook.port diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index ccc98866b..e2217c7b7 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -1,6 +1,6 @@ /** * Bridge Deposit Webhook Handler - * Handles deposit.completed events from Bridge.xyz + * Handles transfer state-transition events (deposit flow) from Bridge.xyz * * NOTE: This handler only logs the deposit event. * The actual balance crediting happens when IBEX sends its crypto.received webhook. @@ -9,51 +9,77 @@ import { Request, Response } from "express" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" +import { createBridgeDepositLog } from "@services/mongoose/bridge-deposit-log" export const depositHandler = async (req: Request, res: Response) => { const { event_id, event_object } = req.body - const { id, amount, deposit_id, currency, subtotal_amount, customer_id, receipt } = event_object + const { id, state, amount, currency, on_behalf_of, receipt } = event_object ?? {} if (!id || !event_id) { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency check using deposit_id as lock key - const lockKey = `bridge-deposit:${deposit_id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + // Idempotency: lock on the transfer id + state so each state transition is processed once + const lockKey = `bridge-deposit:${id}:${state}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) if (lockResult instanceof Error) { - baseLogger.info({ event_id, id }, "Duplicate Bridge deposit webhook") + baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") return res.status(200).json({ status: "already_processed" }) } try { - // Log deposit event - // The actual balance crediting happens via IBEX crypto webhook baseLogger.info( { id, + state, amount, - initial_amount: receipt.initial_amount, currency, - deposit_id, + on_behalf_of, receipt: { - url: receipt.url, - initial_amount: receipt.initial_amount, - subtotal_amount: receipt.subtotal_amount, - final_amount: receipt.final_amount, - exchange_fee: receipt.exchange_fee, - gas_fee: receipt.gas_fee, + initial_amount: receipt?.initial_amount, + subtotal_amount: receipt?.subtotal_amount, + final_amount: receipt?.final_amount, + developer_fee: receipt?.developer_fee, + destination_tx_hash: receipt?.destination_tx_hash, }, - customer_id, event_id, - }, - "Bridge deposit completed", + "Bridge deposit event", ) + const depositLog = await createBridgeDepositLog({ + eventId: event_id, + transferId: id, + customerId: on_behalf_of ?? "", + state, + amount: String(amount), + currency, + developerFee: + receipt?.developer_fee != null + ? String(receipt.developer_fee) + : event_object?.developer_fee != null + ? String(event_object.developer_fee) + : "0", + subtotalAmount: + receipt?.subtotal_amount != null ? String(receipt.subtotal_amount) : undefined, + initialAmount: + receipt?.initial_amount != null ? String(receipt.initial_amount) : undefined, + finalAmount: + receipt?.final_amount != null ? String(receipt.final_amount) : undefined, + destinationTxHash: receipt?.destination_tx_hash, + }) + + if (depositLog instanceof Error) { + baseLogger.error( + { error: depositLog, event_id, id }, + "Failed to persist bridge deposit log", + ) + return res.status(500).json({ error: "Failed to persist deposit log" }) + } + return res.status(200).json({ status: "success" }) } catch (error) { - baseLogger.error({ error, id, event_id, deposit_id }, "Error processing Bridge deposit webhook") + baseLogger.error({ error, id, event_id }, "Error processing Bridge deposit webhook") return res.status(500).json({ error: "Internal server error" }) } } diff --git a/src/services/bridge/webhook-server/routes/replay.ts b/src/services/bridge/webhook-server/routes/replay.ts new file mode 100644 index 000000000..6316e2c8c --- /dev/null +++ b/src/services/bridge/webhook-server/routes/replay.ts @@ -0,0 +1,239 @@ +import crypto from "crypto" + +import { Request, Response } from "express" + +import { BridgeConfig } from "@config" + +import { baseLogger } from "@services/logger" + +import { createBridgeReplayLog } from "@services/mongoose/bridge-replay-log" + +import { depositHandler } from "./deposit" +import { kycHandler } from "./kyc" +import { transferHandler } from "./transfer" +type RouteKey = "kyc" | "deposit" | "transfer" + +const HANDLERS: Record Promise> = { + kyc: kycHandler, + deposit: depositHandler, + transfer: transferHandler, +} + +const DEPOSIT_EVENT_TYPES = new Set([ + "funds_scheduled", + "funds_received", + "payment_submitted", + "payment_processed", + "in_review", + "microdeposit", + "refund_in_flight", + "refunded", + "refund_failed", +]) + +const toRouteKey = (bridgeEventType: string): RouteKey | null => { + if (bridgeEventType.startsWith("kyc")) return "kyc" + if (bridgeEventType.startsWith("transfer")) return "transfer" + if (DEPOSIT_EVENT_TYPES.has(bridgeEventType)) return "deposit" + return null +} + +const resolveReplayEventType = ({ + eventType, + eventObjectStatus, +}: { + eventType: string + eventObjectStatus?: string +}): string => { + const routeFromEventType = toRouteKey(eventType) + if (routeFromEventType) return eventType + + if (eventObjectStatus && DEPOSIT_EVENT_TYPES.has(eventObjectStatus)) { + return eventObjectStatus + } + + if (eventObjectStatus === "approved" || eventObjectStatus === "rejected") { + return `kyc.${eventObjectStatus}` + } + + if (eventObjectStatus === "completed" || eventObjectStatus === "failed") { + return `transfer.${eventObjectStatus}` + } + + return eventType +} + +const toHandlerBody = ({ + routeKey, + eventId, + eventType, + eventObject, +}: { + routeKey: RouteKey + eventId: string + eventType: string + eventObject: Record +}): Record => { + if (routeKey === "transfer") { + return { + event: eventType, + data: { + transfer_id: eventObject.transfer_id ?? eventObject.id, + state: eventObject.state, + amount: eventObject.amount, + currency: eventObject.currency, + }, + } + } + + return { + event_id: eventId, + event_object: eventObject, + } +} + +export const replayAuthMiddleware = (req: Request, res: Response, next: () => void) => { + const secret = + BridgeConfig.webhook.replaySecret ?? process.env.BRIDGE_WEBHOOK_REPLAY_SECRET + if (!secret) { + baseLogger.warn("Replay secret not configured, rejecting replay request") + return res.status(503).json({ error: "Replay secret not configured" }) + } + + const token = (req.headers.authorization ?? "").replace(/^Bearer /, "") + + const valid = + token.length === secret.length && + crypto.timingSafeEqual(Buffer.from(token), Buffer.from(secret)) + + if (!valid) { + baseLogger.warn("Invalid replay token provided") + return res.status(401).json({ error: "Unauthorized" }) + } + + next() +} + +export const replayHandler = async (req: Request, res: Response) => { + const { + event_id, + event_type, + event_object_status, + event_object, + event_created_at, + operator, + time_window_start, + time_window_end, + dry_run = false, + } = req.body + + if (!event_type || !event_object || !event_created_at) { + return res.status(400).json({ + error: + "Missing required fields: event_type, event_object, event_created_at, operator, time_window_start, time_window_end", + }) + } + + if (typeof event_object !== "object" || event_object === null) { + return res.status(400).json({ error: "event_object must be an object" }) + } + + const normalizedEventType = resolveReplayEventType({ + eventType: event_type, + eventObjectStatus: + typeof event_object_status === "string" ? event_object_status : undefined, + }) + + const routeKey = toRouteKey(normalizedEventType) + + if (!routeKey) { + return res.status(400).json({ error: "Unsupported event_type for replay" }) + } + + const eventObjectTyped = event_object as Record + const eventId: string = + typeof event_id === "string" + ? event_id + : typeof eventObjectTyped.event_id === "string" + ? eventObjectTyped.event_id + : typeof eventObjectTyped.id === "string" + ? eventObjectTyped.id + : crypto.randomUUID() + + const logBase = { + eventId, + eventType: routeKey, + eventPayload: event_object, + bridgeEventCreatedAt: new Date(event_created_at), + replayedAt: new Date(), + operator, + timeWindowStart: new Date(time_window_start), + timeWindowEnd: new Date(time_window_end), + dryRun: dry_run, + } + + if (dry_run) { + const dryRunLog = await createBridgeReplayLog({ + ...logBase, + httpStatus: 0, + httpResponse: { dry_run: true }, + }) + if (dryRunLog instanceof Error) { + baseLogger.error( + { error: dryRunLog }, + "Failed to log bridge dry-run replay attempt", + ) + return res.status(500).json({ error: "Failed to log dry-run replay attempt" }) + } + return res.status(200).json({ + message: "Dry run successful, event not replayed", + log: logBase, + event_id: eventId, + }) + } + + let handlerStatus = 500 + let handlerBody: Record = {} + + const fakeReq = { + body: toHandlerBody({ + routeKey, + eventId, + eventType: normalizedEventType, + eventObject: eventObjectTyped, + }), + headers: {}, + } as unknown as Request + const fakeRes = { + status: function (code: number) { + handlerStatus = code + return this + }, + json: function (body: unknown) { + handlerBody = body as Record + return this + }, + } as unknown as Response + + await HANDLERS[routeKey](fakeReq, fakeRes) + + const logResult = await createBridgeReplayLog({ + ...logBase, + httpStatus: handlerStatus, + httpResponse: handlerBody, + }) + + if (logResult instanceof Error) { + baseLogger.error({ error: logResult }, "Failed to log bridge replay attempt") + return res + .status(500) + .json({ error: "Failed to log replay attempt", details: logResult.message }) + } + return res.status(handlerStatus).json({ + status: "replayed", + event_id: eventId, + handler_status: handlerStatus, + handler_response: handlerBody, + log_id: logResult.id, + }) +} diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index 914b12f7c..e104a52df 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -1,9 +1,11 @@ import express, { Request, Response } from "express" import { AccountsRepository } from "@services/mongoose/accounts" +import { createIbexCryptoReceiveLog } from "@services/mongoose/ibex-crypto-receive-log" import { listWalletsByAccountId } from "@app/wallets" import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" import { LockService } from "@services/lock" + import { authenticate, logRequest } from "../middleware" const paths = { @@ -28,52 +30,71 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return res.status(400).json({ error: "Invalid payload" }) } - const lockResult = await LockService().lockPaymentHash(tx_hash as any, async () => { - try { - const account = await AccountsRepository().findByBridgeEthereumAddress(address) - if (account instanceof Error) { - baseLogger.error({ address, tx_hash }, "Account not found for Ethereum address") - return { status: "error", code: "account_not_found" } as CryptoReceiveResult - } - - const wallets = await listWalletsByAccountId(account.id) - if (wallets instanceof Error) { - baseLogger.error( - { accountId: account.id, error: wallets }, - "Failed to list wallets", + const lockResult = await LockService().lockPaymentHash( + tx_hash as PaymentHash, + async () => { + try { + const account = await AccountsRepository().findByBridgeEthereumAddress(address) + if (account instanceof Error) { + baseLogger.error({ address, tx_hash }, "Account not found for Ethereum address") + return { status: "error", code: "account_not_found" } as CryptoReceiveResult + } + + const ibexLog = await createIbexCryptoReceiveLog({ + txHash: String(tx_hash), + address: String(address), + amount: String(amount), + currency: String(currency), + network: String(network), + accountId: account.id, + }) + if (ibexLog instanceof Error) { + baseLogger.error( + { error: ibexLog, tx_hash }, + "Failed to persist IBEX crypto receive log", + ) + return { status: "error", code: "internal_error" } as CryptoReceiveResult + } + + const wallets = await listWalletsByAccountId(account.id) + if (wallets instanceof Error) { + baseLogger.error( + { accountId: account.id, error: wallets }, + "Failed to list wallets", + ) + return { status: "error", code: "wallet_list_failed" } as CryptoReceiveResult + } + + const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) + if (!usdtWallet) { + baseLogger.error({ accountId: account.id }, "USDT wallet not found") + return { status: "error", code: "usdt_wallet_not_found" } as CryptoReceiveResult + } + + const usdtAmount = USDTAmount.fromNumber(amount) + if (usdtAmount instanceof Error) { + baseLogger.error({ amount, error: usdtAmount }, "Invalid USDT amount") + return { status: "error", code: "invalid_amount" } as CryptoReceiveResult + } + + baseLogger.info( + { + accountId: account.id, + walletId: usdtWallet.id, + amount: usdtAmount.asNumber(), + tx_hash, + address, + }, + "USDT deposit received", ) - return { status: "error", code: "wallet_list_failed" } as CryptoReceiveResult - } - const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) - if (!usdtWallet) { - baseLogger.error({ accountId: account.id }, "USDT wallet not found") - return { status: "error", code: "usdt_wallet_not_found" } as CryptoReceiveResult + return { status: "success" } as CryptoReceiveResult + } catch (error) { + baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") + return { status: "error", code: "internal_error" } as CryptoReceiveResult } - - const usdtAmount = USDTAmount.fromNumber(amount) - if (usdtAmount instanceof Error) { - baseLogger.error({ amount, error: usdtAmount }, "Invalid USDT amount") - return { status: "error", code: "invalid_amount" } as CryptoReceiveResult - } - - baseLogger.info( - { - accountId: account.id, - walletId: usdtWallet.id, - amount: usdtAmount.asNumber(), - tx_hash, - address, - }, - "USDT deposit received", - ) - - return { status: "success" } as CryptoReceiveResult - } catch (error) { - baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") - return { status: "error", code: "internal_error" } as CryptoReceiveResult - } - }) + }, + ) if (lockResult instanceof Error) { baseLogger.warn( diff --git a/src/services/mongoose/bridge-deposit-log.ts b/src/services/mongoose/bridge-deposit-log.ts new file mode 100644 index 000000000..f71f37abd --- /dev/null +++ b/src/services/mongoose/bridge-deposit-log.ts @@ -0,0 +1,22 @@ +import { BridgeDepositLog } from "./schema" + +export const createBridgeDepositLog = async (data: { + eventId: string + transferId: string + customerId: string + state: string + amount: string + currency: string + developerFee: string + subtotalAmount?: string + initialAmount?: string + finalAmount?: string + destinationTxHash?: string +}): Promise<{ id: string } | Error> => { + try { + const log = await BridgeDepositLog.create(data) + return { id: log._id.toString() } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/bridge-reconciliation-orphan.ts b/src/services/mongoose/bridge-reconciliation-orphan.ts new file mode 100644 index 000000000..0150333d2 --- /dev/null +++ b/src/services/mongoose/bridge-reconciliation-orphan.ts @@ -0,0 +1,27 @@ +import { BridgeReconciliationOrphan } from "./schema" + +type OrphanType = "bridge_without_ibex" | "ibex_without_bridge" + +export const upsertBridgeReconciliationOrphan = async (data: { + orphanKey: string + orphanType: OrphanType + transferId?: string + txHash?: string + bridgeEventId?: string + customerId?: string + amount?: string + currency?: string + triageContext: Record +}): Promise<{ id: string } | Error> => { + try { + const orphan = await BridgeReconciliationOrphan.findOneAndUpdate( + { orphanKey: data.orphanKey }, + { ...data, detectedAt: new Date() }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) + + return { id: orphan._id.toString() } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/bridge-replay-log.ts b/src/services/mongoose/bridge-replay-log.ts new file mode 100644 index 000000000..5782471cf --- /dev/null +++ b/src/services/mongoose/bridge-replay-log.ts @@ -0,0 +1,42 @@ +import { BridgeReplayLog } from "./schema" + +export const createBridgeReplayLog = async (data: { + eventId: string + eventType: string, + eventPayload: Record, + bridgeEventCreatedAt: Date, + replayedAt: Date, + operator: string, + timeWindowStart: Date, + timeWindowEnd: Date, + httpStatus: number, + httpResponse: Record, + dryRun?: boolean +}): Promise<{ id: string } | Error> => { + + try { + const log = await BridgeReplayLog.create(data) + + return { id: log._id.toString() } + + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} + + +export const findBridgeReplayLogs = async (filter?: { + eventType?: string + dryRun?: boolean + limit?: number +}): Promise => { + try { + const queryFilter: Record = {} + if (filter?.eventType !== undefined) queryFilter.eventType = filter.eventType + if (filter?.dryRun !== undefined) queryFilter.dryRun = filter.dryRun + + return await BridgeReplayLog.find(queryFilter).sort({ replayedAt: -1 }).limit(filter?.limit ?? 100).lean() + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/ibex-crypto-receive-log.ts b/src/services/mongoose/ibex-crypto-receive-log.ts new file mode 100644 index 000000000..e657c9e89 --- /dev/null +++ b/src/services/mongoose/ibex-crypto-receive-log.ts @@ -0,0 +1,51 @@ +import { IbexCryptoReceiveLog } from "./schema" + +export const createIbexCryptoReceiveLog = async (data: { + txHash: string + address: string + amount: string + currency: string + network: string + accountId?: string +}): Promise<{ id: string } | Error> => { + try { + const log = await IbexCryptoReceiveLog.findOneAndUpdate( + { txHash: data.txHash }, + { ...data, receivedAt: new Date() }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) + + return { id: log._id.toString() } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} + +export const findIbexCryptoReceiveLogsSince = async ({ + since, + until = new Date(), +}: { + since: Date + until?: Date +}): Promise< + | Array<{ + txHash: string + address: string + amount: string + currency: string + network: string + accountId?: string + receivedAt: Date + }> + | Error +> => { + try { + return await IbexCryptoReceiveLog.find({ + receivedAt: { $gte: since, $lte: until }, + }) + .sort({ receivedAt: -1 }) + .lean() + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index dd0b9aac9..2feb91d87 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -1,6 +1,11 @@ import crypto from "crypto" -import { getDefaultAccountsConfig, getFeesConfig, getDefaultFCMTopics, Levels } from "@config" +import { + getDefaultAccountsConfig, + getFeesConfig, + getDefaultFCMTopics, + Levels, +} from "@config" import { AccountStatus, UsernameRegex } from "@domain/accounts" import { WalletIdRegex, WalletType } from "@domain/wallets" import { WalletCurrency } from "@domain/shared" @@ -653,6 +658,89 @@ const BridgeWithdrawalSchema = new Schema({ updatedAt: { type: Date, default: Date.now }, }) +const BridgeDepositLogSchema = new Schema({ + eventId: { type: String, required: true, unique: true }, + transferId: { type: String, required: true }, + customerId: { type: String, required: true }, + state: { type: String, required: true }, + amount: { type: String, required: true }, + currency: { type: String, required: true }, + subtotalAmount: { type: String }, + developerFee: { type: String, required: true, default: "0" }, + initialAmount: { type: String }, + finalAmount: { type: String }, + destinationTxHash: { type: String }, + createdAt: { type: Date, default: Date.now }, +}) + +BridgeDepositLogSchema.index({ transferId: 1 }) +BridgeDepositLogSchema.index({ customerId: 1, createdAt: -1 }) + +export const BridgeDepositLog = mongoose.model("BridgeDepositLog", BridgeDepositLogSchema) + +const IbexCryptoReceiveLogSchema = new Schema({ + txHash: { type: String, required: true, unique: true }, + address: { type: String, required: true }, + amount: { type: String, required: true }, + currency: { type: String, required: true }, + network: { type: String, required: true }, + accountId: { type: String }, + receivedAt: { type: Date, default: Date.now }, +}) + +IbexCryptoReceiveLogSchema.index({ receivedAt: -1 }) +IbexCryptoReceiveLogSchema.index({ address: 1, receivedAt: -1 }) + +export const IbexCryptoReceiveLog = mongoose.model( + "IbexCryptoReceiveLog", + IbexCryptoReceiveLogSchema, +) + +const BridgeReconciliationOrphanSchema = new Schema({ + orphanKey: { type: String, required: true, unique: true }, + orphanType: { + type: String, + enum: ["bridge_without_ibex", "ibex_without_bridge"], + required: true, + }, + transferId: { type: String }, + txHash: { type: String }, + bridgeEventId: { type: String }, + customerId: { type: String }, + amount: { type: String }, + currency: { type: String }, + triageContext: { type: Schema.Types.Mixed, required: true }, + detectedAt: { type: Date, default: Date.now }, +}) + +BridgeReconciliationOrphanSchema.index({ orphanType: 1, detectedAt: -1 }) +BridgeReconciliationOrphanSchema.index({ detectedAt: -1 }) + +export const BridgeReconciliationOrphan = mongoose.model( + "BridgeReconciliationOrphan", + BridgeReconciliationOrphanSchema, +) + +const BridgeReplayLogSchema = new Schema({ + eventId: { type: String, required: true }, + eventType: { type: String, required: true }, + eventPayload: { type: Schema.Types.Mixed, required: true }, + bridgeEventCreatedAt: { type: Date, required: true }, + replayedAt: { type: Date, required: true, default: Date.now }, + operator: { type: String, required: true }, + timeWindowStart: { type: Date, required: true }, + timeWindowEnd: { type: Date, required: true }, + httpStatus: { type: Number, required: true }, + httpResponse: { type: Schema.Types.Mixed, required: true }, + dryRun: { type: Boolean, required: true, default: false }, +}) + +BridgeReplayLogSchema.index({ eventId: 1 }) +BridgeReplayLogSchema.index({ replayedAt: -1 }) +BridgeReplayLogSchema.index({ eventType: 1, replayedAt: -1 }) + +export const BridgeReplayLog = mongoose.model("BridgeReplayLog", BridgeReplayLogSchema) + export const BridgeVirtualAccount = mongoose.model( "BridgeVirtualAccount", BridgeVirtualAccountSchema, diff --git a/test/flash/unit/services/bridge/client.spec.ts b/test/flash/unit/services/bridge/client.spec.ts new file mode 100644 index 000000000..a4fc194f5 --- /dev/null +++ b/test/flash/unit/services/bridge/client.spec.ts @@ -0,0 +1,152 @@ +// AC1: listAllEvents surfaces orphan events for ops triage tooling + +jest.mock("@config", () => ({ + BridgeConfig: { + apiKey: "test-api-key", + baseUrl: "https://api.sandbox.bridge.xyz/v0", + }, +})) + +import { BridgeClient, listAllEvents, BridgeWebhookEvent } from "@services/bridge/client" + +// ── Fixtures ────────────────────────────────────────────────────────────────── + +const makeEvent = (id: string): BridgeWebhookEvent => ({ + id, + event_type: "transfer.completed", + payload: { transfer_id: id }, + created_at: "2026-05-01T10:00:00Z", +}) + +// ── listAllEvents ───────────────────────────────────────────────────────────── + +describe("listAllEvents", () => { + let listEventsSpy: jest.SpyInstance + + beforeEach(() => { + listEventsSpy = jest.spyOn(BridgeClient.prototype, "listEvents") + }) + + afterEach(() => { + listEventsSpy.mockRestore() + }) + + it("yields all events from a single page", async () => { + listEventsSpy.mockResolvedValue({ + data: [makeEvent("e1"), makeEvent("e2")], + has_more: false, + cursor: undefined, + }) + + const events: BridgeWebhookEvent[] = [] + for await (const e of listAllEvents()) { + events.push(e) + } + + expect(events).toHaveLength(2) + expect(events.map((e) => e.id)).toEqual(["e1", "e2"]) + expect(listEventsSpy).toHaveBeenCalledTimes(1) + }) + + it("paginates across multiple pages until has_more is false", async () => { + listEventsSpy + .mockResolvedValueOnce({ + data: [makeEvent("e1"), makeEvent("e2")], + has_more: true, + cursor: "cursor-page-2", + }) + .mockResolvedValueOnce({ + data: [makeEvent("e3"), makeEvent("e4")], + has_more: true, + cursor: "cursor-page-3", + }) + .mockResolvedValueOnce({ + data: [makeEvent("e5")], + has_more: false, + cursor: undefined, + }) + + const events: BridgeWebhookEvent[] = [] + for await (const e of listAllEvents()) { + events.push(e) + } + + expect(events).toHaveLength(5) + expect(events.map((e) => e.id)).toEqual(["e1", "e2", "e3", "e4", "e5"]) + expect(listEventsSpy).toHaveBeenCalledTimes(3) + }) + + it("passes the cursor from page N as 'after' on page N+1", async () => { + listEventsSpy + .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "cur-abc" }) + .mockResolvedValueOnce({ data: [makeEvent("e2")], has_more: false, cursor: undefined }) + + for await (const _ of listAllEvents()) { /* drain */ } + + expect(listEventsSpy).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ after: undefined }), + ) + expect(listEventsSpy).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ after: "cur-abc" }), + ) + }) + + it("always requests page_size 100", async () => { + listEventsSpy.mockResolvedValue({ data: [], has_more: false, cursor: undefined }) + + for await (const _ of listAllEvents()) { /* drain */ } + + expect(listEventsSpy).toHaveBeenCalledWith( + expect.objectContaining({ page_size: 100 }), + ) + }) + + it("forwards start_date, end_date and event_type filters to every page", async () => { + listEventsSpy + .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "c1" }) + .mockResolvedValueOnce({ data: [makeEvent("e2")], has_more: false, cursor: undefined }) + + const params = { + start_date: "2026-05-01T00:00:00Z", + end_date: "2026-05-02T00:00:00Z", + event_type: "transfer.completed", + } + + for await (const _ of listAllEvents(params)) { /* drain */ } + + expect(listEventsSpy).toHaveBeenCalledTimes(2) + for (const call of listEventsSpy.mock.calls) { + expect(call[0]).toMatchObject(params) + } + }) + + it("yields nothing and makes one call when the first page is empty", async () => { + listEventsSpy.mockResolvedValue({ data: [], has_more: false, cursor: undefined }) + + const events: BridgeWebhookEvent[] = [] + for await (const e of listAllEvents()) { + events.push(e) + } + + expect(events).toHaveLength(0) + expect(listEventsSpy).toHaveBeenCalledTimes(1) + }) + + it("stops immediately if has_more is false even when cursor is present", async () => { + listEventsSpy.mockResolvedValue({ + data: [makeEvent("e1")], + has_more: false, + cursor: "stale-cursor", + }) + + const events: BridgeWebhookEvent[] = [] + for await (const e of listAllEvents()) { + events.push(e) + } + + expect(events).toHaveLength(1) + expect(listEventsSpy).toHaveBeenCalledTimes(1) + }) +}) diff --git a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts new file mode 100644 index 000000000..fe2756e3d --- /dev/null +++ b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts @@ -0,0 +1,225 @@ +// AC3: Bridge fee value persisted on every deposit row + +jest.mock("@services/lock", () => ({ + LockService: jest.fn(), +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/bridge-deposit-log", () => ({ + createBridgeDepositLog: jest.fn(), +})) + +import { Request, Response } from "express" +import { LockService } from "@services/lock" +import * as DepositLog from "@services/mongoose/bridge-deposit-log" +import { depositHandler } from "@services/bridge/webhook-server/routes/deposit" + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const makeRes = () => { + const res = { status: jest.fn(), json: jest.fn() } as unknown as Response + ;(res.status as jest.Mock).mockReturnValue(res) + ;(res.json as jest.Mock).mockReturnValue(res) + return res +} + +const makeReq = (body: Record) => + ({ body } as unknown as Request) + +// Real Bridge deposit event shape +const VALID_EVENT_OBJECT = { + id: "tr_xyz789abc123", + state: "funds_received", + amount: "1.00", + currency: "usd", + developer_fee: "0.0", + on_behalf_of: "cust_bob", + source: { + currency: "usdb", + payment_rail: "bridge_wallet", + bridge_wallet_id: "wallet_bob_usdb", + }, + receipt: { + developer_fee: "0.0", + initial_amount: "1.00", + subtotal_amount: "1.00", + final_amount: "1.00", + destination_tx_hash: "4gJH6oXpZUNgC1QLh8mXNPF92LtLKzHZj5eHuQrdQAgB", + }, + created_at: "2025-06-11T21:27:00.000Z", + updated_at: "2025-06-11T21:27:01.000Z", +} + +const VALID_BODY = { + api_version: "v0", + event_id: "wh_789xyz654mno", + event_category: "transfer", + event_type: "updated.status_transitioned", + event_object_id: "tr_xyz789abc123", + event_object_status: "funds_received", + event_object: VALID_EVENT_OBJECT, + event_object_changes: { state: ["payment_submitted", "funds_received"] }, + event_created_at: "2025-06-11T21:27:00.000Z", +} + +// ── Setup ───────────────────────────────────────────────────────────────────── + +const mockLockService = (acquired = true) => { + ;(LockService as jest.Mock).mockReturnValue({ + lockIdempotencyKey: jest + .fn() + .mockResolvedValue(acquired ? {} : new Error("already locked")), + }) +} + +beforeEach(() => { + jest.clearAllMocks() + mockLockService(true) +}) + +// ── Invalid payload ─────────────────────────────────────────────────────────── + +describe("depositHandler — invalid payload", () => { + it("returns 400 when event_id is missing", async () => { + const res = makeRes() + const { event_id: _, ...body } = VALID_BODY + await depositHandler(makeReq(body), res) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) + expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + }) + + it("returns 400 when event_object.id is missing", async () => { + const res = makeRes() + const { id: _, ...objWithoutId } = VALID_EVENT_OBJECT + await depositHandler(makeReq({ ...VALID_BODY, event_object: objWithoutId }), res) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) + expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + }) +}) + +// ── Idempotency ─────────────────────────────────────────────────────────────── + +describe("depositHandler — idempotency", () => { + it("returns already_processed without persisting on duplicate state transition", async () => { + mockLockService(false) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(res.json as jest.Mock).toHaveBeenCalledWith({ status: "already_processed" }) + expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + }) + + it("locks on transfer id + state so different states for same transfer are processed", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ id: "log-1" }) + + const lockFn = jest.fn().mockResolvedValue({}) + ;(LockService as jest.Mock).mockReturnValue({ lockIdempotencyKey: lockFn }) + + await depositHandler(makeReq(VALID_BODY), makeRes()) + expect(lockFn).toHaveBeenCalledWith("bridge-deposit:tr_xyz789abc123:funds_received") + + await depositHandler( + makeReq({ + ...VALID_BODY, + event_object: { ...VALID_EVENT_OBJECT, state: "payment_processed" }, + }), + makeRes(), + ) + expect(lockFn).toHaveBeenCalledWith( + "bridge-deposit:tr_xyz789abc123:payment_processed", + ) + }) +}) + +// ── AC3: Fee persistence ────────────────────────────────────────────────────── + +describe("depositHandler — fee persistence (AC3)", () => { + it("persists developer_fee from the receipt on every deposit event", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + id: "log-fee-001", + }) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledTimes(1) + expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect.objectContaining({ + developerFee: "0.0", + }), + ) + }) + + it("persists the full deposit record with transfer id, customer, state and receipt breakdown", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + id: "log-fee-002", + }) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect.objectContaining({ + eventId: "wh_789xyz654mno", + transferId: "tr_xyz789abc123", + customerId: "cust_bob", + state: "funds_received", + amount: "1.00", + currency: "usd", + subtotalAmount: "1.00", + initialAmount: "1.00", + finalAmount: "1.00", + destinationTxHash: "4gJH6oXpZUNgC1QLh8mXNPF92LtLKzHZj5eHuQrdQAgB", + }), + ) + }) + + it("returns 200 success after persisting the deposit log", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + id: "log-fee-003", + }) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + expect(res.json as jest.Mock).toHaveBeenCalledWith({ status: "success" }) + }) + + it("returns 500 when log persistence fails", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue( + new Error("mongo timeout"), + ) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) + }) + + it("handles null developer_fee gracefully", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + id: "log-fee-004", + }) + + const body = { + ...VALID_BODY, + event_object: { + ...VALID_EVENT_OBJECT, + receipt: { ...VALID_EVENT_OBJECT.receipt, developer_fee: null }, + }, + } + + const res = makeRes() + await depositHandler(makeReq(body), res) + + expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect.objectContaining({ developerFee: "0.0" }), + ) + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + }) +}) diff --git a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts new file mode 100644 index 000000000..32d6e7302 --- /dev/null +++ b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts @@ -0,0 +1,296 @@ +import crypto from "crypto" + +// AC1: Orphan event surfaces in ops tooling with triage context +// AC2: Replay CLI re-runs a stuck handler against a chosen transfer-id + +jest.mock("@config", () => ({ + BridgeConfig: { + webhook: { replaySecret: "super-secret-replay-token-xyz" }, + }, +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/bridge-replay-log", () => ({ + createBridgeReplayLog: jest.fn(), +})) + +jest.mock("@services/bridge/webhook-server/routes/deposit", () => ({ + depositHandler: jest.fn(), +})) +jest.mock("@services/bridge/webhook-server/routes/kyc", () => ({ + kycHandler: jest.fn(), +})) +jest.mock("@services/bridge/webhook-server/routes/transfer", () => ({ + transferHandler: jest.fn(), +})) + +import { Request, Response } from "express" +import { replayAuthMiddleware, replayHandler } from "@services/bridge/webhook-server/routes/replay" +import * as ReplayLog from "@services/mongoose/bridge-replay-log" +import { depositHandler } from "@services/bridge/webhook-server/routes/deposit" +import { kycHandler } from "@services/bridge/webhook-server/routes/kyc" +import { transferHandler } from "@services/bridge/webhook-server/routes/transfer" + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const makeRes = () => { + const res = { status: jest.fn(), json: jest.fn() } as unknown as Response + ;(res.status as jest.Mock).mockReturnValue(res) + ;(res.json as jest.Mock).mockReturnValue(res) + return res +} + +const makeReq = (body: Record = {}, headers: Record = {}) => + ({ body, headers } as unknown as Request) + +const BASE_BODY = { + event_type: "funds_received", + event_object: { id: "evt-001", transfer_id: "xfer-001" }, + event_created_at: "2026-05-01T12:00:00Z", + operator: "ops@example.com", + time_window_start: "2026-05-01T00:00:00Z", + time_window_end: "2026-05-01T23:59:59Z", +} + +// ── replayAuthMiddleware ────────────────────────────────────────────────────── + +describe("replayAuthMiddleware", () => { + beforeEach(() => jest.clearAllMocks()) + + it("returns 503 when replaySecret is not configured", () => { + const { BridgeConfig } = jest.requireMock("@config") + const saved = BridgeConfig.webhook.replaySecret + BridgeConfig.webhook.replaySecret = undefined + + const res = makeRes() + const next = jest.fn() + replayAuthMiddleware(makeReq({}, {}), res, next) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(503) + expect(next).not.toHaveBeenCalled() + + BridgeConfig.webhook.replaySecret = saved + }) + + it("returns 401 for a wrong token", () => { + const res = makeRes() + const next = jest.fn() + replayAuthMiddleware( + makeReq({}, { authorization: "Bearer wrong-token" }), + res, + next, + ) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(next).not.toHaveBeenCalled() + }) + + it("returns 401 when Authorization header is absent", () => { + const res = makeRes() + const next = jest.fn() + replayAuthMiddleware(makeReq({}, {}), res, next) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(next).not.toHaveBeenCalled() + }) + + it("calls next() for the correct Bearer token", () => { + const res = makeRes() + const next = jest.fn() + replayAuthMiddleware( + makeReq({}, { authorization: "Bearer super-secret-replay-token-xyz" }), + res, + next, + ) + + expect(next).toHaveBeenCalledTimes(1) + expect((res.status as jest.Mock)).not.toHaveBeenCalled() + }) + + it("uses timing-safe comparison (different-length token is rejected)", () => { + const res = makeRes() + const next = jest.fn() + // A prefix of the real secret — same content up to length, but different length + replayAuthMiddleware( + makeReq({}, { authorization: "Bearer super-secret-replay-token-xy" }), + res, + next, + ) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(next).not.toHaveBeenCalled() + }) +}) + +// ── replayHandler ───────────────────────────────────────────────────────────── + +describe("replayHandler", () => { + beforeEach(() => jest.clearAllMocks()) + + describe("input validation", () => { + it("returns 400 when event_type is missing", async () => { + const res = makeRes() + const { event_type: _et, ...body } = BASE_BODY + await replayHandler(makeReq(body), res) + expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + }) + + it("returns 400 when event_object is missing", async () => { + const res = makeRes() + const { event_object: _eo, ...body } = BASE_BODY + await replayHandler(makeReq(body), res) + expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + }) + + it("returns 400 when event_created_at is missing", async () => { + const res = makeRes() + const { event_created_at: _ec, ...body } = BASE_BODY + await replayHandler(makeReq(body), res) + expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + }) + + it("returns 400 for an unrecognised event_type", async () => { + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, event_type: "unknown_event" }), res) + expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + }) + }) + + describe("event_type → handler routing", () => { + const cases: Array<[string, jest.Mock]> = [ + ["funds_received", depositHandler as jest.Mock], + ["funds_scheduled", depositHandler as jest.Mock], + ["payment_processed", depositHandler as jest.Mock], + ["kyc.approved", kycHandler as jest.Mock], + ["kyc.rejected", kycHandler as jest.Mock], + ["transfer.completed", transferHandler as jest.Mock], + ["transfer.failed", transferHandler as jest.Mock], + ] + + beforeEach(() => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-001" }) + }) + + test.each(cases)("%s is routed to the correct handler", async (eventType, handler) => { + handler.mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }) + + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, event_type: eventType }), res) + + expect(handler).toHaveBeenCalledTimes(1) + }) + }) + + describe("dry_run mode", () => { + it("returns 200 without calling any handler", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-dry-001" }) + + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) + + expect(depositHandler).not.toHaveBeenCalled() + expect(kycHandler).not.toHaveBeenCalled() + expect(transferHandler).not.toHaveBeenCalled() + expect((res.status as jest.Mock)).toHaveBeenCalledWith(200) + }) + + it("persists a dry-run log entry with httpStatus 0", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-dry-002" }) + + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) + + expect(ReplayLog.createBridgeReplayLog).toHaveBeenCalledWith( + expect.objectContaining({ httpStatus: 0, dryRun: true }), + ) + }) + + it("returns 500 when dry-run log creation fails", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue(new Error("db error")) + + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(500) + }) + }) + + describe("live replay", () => { + beforeEach(() => { + ;(depositHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }) + }) + + it("returns the handler's status code and response body", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-live-001" }) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(200) + const jsonArg = (res.json as jest.Mock).mock.calls[0][0] + expect(jsonArg).toMatchObject({ status: "replayed", handler_status: 200 }) + }) + + it("persists a replay log with triage context (operator + time window)", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-live-002" }) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + expect(ReplayLog.createBridgeReplayLog).toHaveBeenCalledWith( + expect.objectContaining({ + operator: "ops@example.com", + timeWindowStart: new Date("2026-05-01T00:00:00Z"), + timeWindowEnd: new Date("2026-05-01T23:59:59Z"), + eventId: "evt-001", + httpStatus: 200, + dryRun: false, + }), + ) + }) + + it("includes log_id in the response so ops can trace the replay", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-trace-007" }) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + const jsonArg = (res.json as jest.Mock).mock.calls[0][0] + expect(jsonArg.log_id).toBe("log-trace-007") + }) + + it("returns 500 when log creation fails after a successful handler run", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue(new Error("mongo down")) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(500) + }) + + it("propagates a handler 4xx back to the caller", async () => { + ;(depositHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(422) + ;(res.json as jest.Mock)({ error: "Unprocessable" }) + return Promise.resolve(res) + }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-4xx" }) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(422) + }) + }) +}) diff --git a/tsconfig-build.json b/tsconfig-build.json index aa3e79ccb..9ce1b13ac 100644 --- a/tsconfig-build.json +++ b/tsconfig-build.json @@ -1,7 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["src/servers/**/*.ts", "src/**/*.d.ts"], + "include": [ + "src/servers/**/*.ts", + "src/scripts/**/*.ts", + "src/**/*.d.ts" + ], "compilerOptions": { "noImplicitAny": true } -} +} \ No newline at end of file From 1818b7c9b9a1114b24aeb019e19db92bba83a171 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:26 -0400 Subject: [PATCH 04/43] ENG-394 feat(accounts): create ETH-USDT Cash Wallet for new accounts --- src/app/accounts/create-account.ts | 26 ++-- src/app/wallets/get-balance-for-wallet.ts | 16 +-- src/config/schema.ts | 4 +- src/domain/shared/MoneyAmount.ts | 11 +- src/domain/wallets/index.types.d.ts | 15 ++- src/services/ibex/client.ts | 91 +++++++------ src/services/ibex/types.ts | 8 +- src/services/mongoose/wallets.ts | 31 +++-- .../unit/app/accounts/create-account.spec.ts | 122 ++++++++++++++++++ .../wallets/get-balance-for-wallet.spec.ts | 38 ++++++ .../unit/services/mongoose/wallets.spec.ts | 82 ++++++++++++ 11 files changed, 368 insertions(+), 76 deletions(-) create mode 100644 test/flash/unit/app/accounts/create-account.spec.ts create mode 100644 test/flash/unit/app/wallets/get-balance-for-wallet.spec.ts create mode 100644 test/flash/unit/services/mongoose/wallets.spec.ts diff --git a/src/app/accounts/create-account.ts b/src/app/accounts/create-account.ts index 67b28fc04..17ea2c1f2 100644 --- a/src/app/accounts/create-account.ts +++ b/src/app/accounts/create-account.ts @@ -10,8 +10,13 @@ import { } from "@services/mongoose" import { recordExceptionInCurrentSpan } from "@services/tracing" -import { ErrorLevel } from "@domain/shared" -import { RepositoryError } from "@domain/errors" +import { ErrorLevel, WalletCurrency } from "@domain/shared" + +const requiredCashWalletCurrencies: WalletCurrency[] = [ + WalletCurrency.Usd, + WalletCurrency.Usdt, +] +const defaultCashWalletCurrency = WalletCurrency.Usdt const initializeCreatedAccount = async ({ account, @@ -29,24 +34,29 @@ const initializeCreatedAccount = async ({ currency, }) - const walletsEnabledConfig = config.initialWallets + const walletsEnabledConfig = Array.from( + new Set([...config.initialWallets, ...requiredCashWalletCurrencies]), + ) // Create all wallets const enabledWallets: Partial> = {} for (const currency of walletsEnabledConfig) { const wallet = await newWallet(currency) - if (wallet instanceof RepositoryError) { + if (wallet instanceof Error) { recordExceptionInCurrentSpan({ error: wallet, level: ErrorLevel.Critical, - attributes: { accountId: account.id } + attributes: { accountId: account.id, currency }, }) + if (requiredCashWalletCurrencies.includes(currency)) return wallet + continue } - else enabledWallets[currency] = wallet + + enabledWallets[currency] = wallet } - // Set default wallet to USD - const defaultWalletId = enabledWallets[walletsEnabledConfig[0]]?.id + // Set ETH-USDT as the active Cash Wallet while preserving USD for migration. + const defaultWalletId = enabledWallets[defaultCashWalletCurrency]?.id if (defaultWalletId === undefined) { return new ConfigError("NoWalletsEnabledInConfigError") diff --git a/src/app/wallets/get-balance-for-wallet.ts b/src/app/wallets/get-balance-for-wallet.ts index 30aaff7a4..46ddf6cad 100644 --- a/src/app/wallets/get-balance-for-wallet.ts +++ b/src/app/wallets/get-balance-for-wallet.ts @@ -11,19 +11,11 @@ export const getBalanceForWallet = async ({ currency?: WalletCurrency }): Promise => { try { - if (currency === WalletCurrency.Usdt) { - const resp = await Ibex.getCryptoReceiveBalance(walletId) - if (resp instanceof IbexError) { - if (resp.httpCode === 404) return USDTAmount.ZERO - return resp - } - if (resp === undefined) return new UnexpectedIbexResponse("Balance not found") - return resp - } - - const resp = await Ibex.getAccountDetails(walletId) + const resp = await Ibex.getAccountDetails(walletId, currency) if (resp instanceof IbexError) { - if (resp.httpCode === 404) return USDAmount.ZERO + if (resp.httpCode === 404) { + return currency === WalletCurrency.Usdt ? USDTAmount.ZERO : USDAmount.ZERO + } return resp } if (resp.balance === undefined) return new UnexpectedIbexResponse("Balance not found") diff --git a/src/config/schema.ts b/src/config/schema.ts index 80124a74e..c97482021 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -385,7 +385,7 @@ export const configSchema = { additionalProperties: false, default: { initialStatus: "active", - initialWallets: ["USD"], + initialWallets: ["USD", "USDT"], enablePhoneCheck: false, enableIpCheck: false, enableIpProxyCheck: false, @@ -662,7 +662,7 @@ export const configSchema = { required: ["kyc", "deposit", "transfer"], }, timestampSkewMs: { type: "integer" }, - replaySecret: { type: "string" } + replaySecret: { type: "string" }, }, required: ["port", "publicKeys", "timestampSkewMs"], }, diff --git a/src/domain/shared/MoneyAmount.ts b/src/domain/shared/MoneyAmount.ts index 8818c20aa..7ad965663 100644 --- a/src/domain/shared/MoneyAmount.ts +++ b/src/domain/shared/MoneyAmount.ts @@ -1,6 +1,6 @@ import { Money, Round, PRECISION_M } from "./bigint-money" import { BigIntConversionError, UnsupportedCurrencyError } from "./errors" -import { ExchangeCurrencyUnit, WalletCurrency } from "./primitives" +import { WalletCurrency } from "./primitives" export abstract class MoneyAmount { readonly money: Money @@ -60,7 +60,8 @@ export abstract class MoneyAmount { static from(amount: number | string, currency: WalletCurrency): MoneyAmount | Error { if (currency === WalletCurrency.Usd) return USDAmount.cents(amount.toString()) else if (currency === WalletCurrency.Jmd) return JMDAmount.cents(amount.toString()) - else if (currency === WalletCurrency.Usdt) return USDTAmount.smallestUnits(amount.toString()) + else if (currency === WalletCurrency.Usdt) + return USDTAmount.smallestUnits(amount.toString()) else return new UnsupportedCurrencyError(`Could not read currency: ${currency}`) } } @@ -177,7 +178,9 @@ export class BtcAmount extends MoneyAmount { try { return new BtcAmount(c) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } } @@ -191,7 +194,7 @@ export class BtcAmount extends MoneyAmount { } export class USDTAmount extends MoneyAmount { - static currencyId: IbexCurrencyId = 4 as IbexCurrencyId + static currencyId: IbexCurrencyId = 29 as IbexCurrencyId private constructor(amount: Money | bigint | string | number) { super(amount, WalletCurrency.Usdt) diff --git a/src/domain/wallets/index.types.d.ts b/src/domain/wallets/index.types.d.ts index 6aa876f61..ee5e1fb23 100644 --- a/src/domain/wallets/index.types.d.ts +++ b/src/domain/wallets/index.types.d.ts @@ -189,7 +189,7 @@ interface IWalletsRepository { accountId, type, currency, - }: NewWalletInfo): Promise + }: NewWalletInfo): Promise findById(walletId: WalletId): Promise listByAccountId(accountId: AccountId): Promise @@ -200,7 +200,7 @@ interface IWalletsRepository { walletCurrency: WalletCurrency, ): Promise - upsertExternal({ accountId, currency, lnurlp }: { accountId: AccountId, currency: WalletCurrency, lnurlp: Lnurl }): Promise + upsertExternal({ accountId, currency, lnurlp }: { accountId: AccountId, currency?: WalletCurrency, lnurlp: Lnurl }): Promise findExternalByAccountId(accountId: AccountId): Promise } @@ -239,3 +239,14 @@ type OnChainFeeCalculator = { } intraLedgerFees(): PaymentAmountInAllCurrencies } + +type PaymentInputValidatorConfig = ( + walletId: WalletId, +) => Promise + +type PaymentInputValidator = { + validatePaymentInput: ( + args: ValidatePaymentInputArgs, + ) => Promise | ValidationError | RepositoryError> +} + diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index c4e243584..aae1a775a 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -1,7 +1,4 @@ -import IbexClient, { GetFeeEstimateResponse200, IbexClientError } from "ibex-client" -import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" -import { IbexConfig } from "@config" -import { +import IbexClient, { AddInvoiceBodyParam, AddInvoiceResponse201, CreateAccountResponse201, @@ -9,32 +6,29 @@ import { CreateLnurlPayResponse201, DecodeLnurlMetadataParam, DecodeLnurlResponse200, - EstimateFeeCopyMetadataParam, EstimateFeeCopyResponse200, - GenerateBitcoinAddressBodyParam, GenerateBitcoinAddressResponse201, - GetAccountDetailsMetadataParam, - GetAccountDetailsResponse200, - GetFeeEstimationMetadataParam, - GetFeeEstimationResponse200, - GetTransactionDetails1MetadataParam, GetTransactionDetails1Response200, GMetadataParam, GResponse200, - InvoiceFromHashMetadataParam, InvoiceFromHashResponse200, PayInvoiceV2BodyParam, PayInvoiceV2Response200, - PayToALnurlPayBodyParam, PayToALnurlPayResponse201, SendToAddressCopyBodyParam, SendToAddressCopyResponse200, } from "ibex-client" + +import { IbexConfig } from "@config" import { addAttributesToCurrentSpan, wrapAsyncFunctionsToRunInSpan, } from "@services/tracing" -import WebhookServer from "./webhook-server" + +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +import { baseLogger } from "@services/logger" + import { Redis } from "./cache" import { GetFeeEstimateArgs, @@ -46,8 +40,9 @@ import { CryptoReceiveInfo, CreateCryptoReceiveInfoRequest, } from "./types" -import { USDAmount, USDTAmount } from "@domain/shared" -import { baseLogger } from "@services/logger" + +import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" +import WebhookServer from "./webhook-server" const Ibex = new IbexClient( { @@ -65,22 +60,33 @@ const createAccount = async ( return Ibex.createAccount({ name, currencyId }).then(errorHandler) } +const parseAccountBalance = ( + balance: number | undefined, + currency: WalletCurrency, +): IbexAccountDetails["balance"] => { + if (balance === undefined) return undefined + + const amount = + currency === WalletCurrency.Usdt + ? USDTAmount.fromNumber(balance.toString()) + : USDAmount.dollars(balance.toString()) + + return amount instanceof Error ? undefined : amount +} + const getAccountDetails = async ( accountId: IbexAccountId, + currency: WalletCurrency = WalletCurrency.Usd, ): Promise => { return Ibex.getAccountDetails({ accountId }) - .then((r) => { - if (r instanceof Error) return r - else { - let balance = - r.balance !== undefined ? USDAmount.dollars(r.balance.toString()) : undefined - if (balance instanceof Error) balance = undefined - return { - id: r.id, - userId: r.userId, - name: r.name, - balance, - } + .then((resp) => { + if (resp instanceof Error) return resp + + return { + id: resp.id, + userId: resp.userId, + name: resp.name, + balance: parseAccountBalance(resp.balance, currency), } }) .then(errorHandler) @@ -146,9 +152,9 @@ const getLnFeeEstimation = async ( else if (resp.invoiceAmount === null || resp.invoiceAmount === undefined) return new UnexpectedIbexResponse("invoiceAmount not found.") else { - let fee = USDAmount.dollars(resp.amount) + const fee = USDAmount.dollars(resp.amount) if (fee instanceof Error) return new ParseError(fee) - let invoiceAmount = USDAmount.dollars(resp.invoiceAmount) + const invoiceAmount = USDAmount.dollars(resp.invoiceAmount) if (invoiceAmount instanceof Error) return new ParseError(invoiceAmount) return { fee, @@ -235,7 +241,9 @@ const getIbexToken = async (): Promise => { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: IbexConfig.email, password: IbexConfig.password }), - }).catch((err: unknown) => new IbexError(err instanceof Error ? err : new Error(String(err)))) + }).catch( + (err: unknown) => new IbexError(err instanceof Error ? err : new Error(String(err))), + ) if (resp instanceof IbexError) return resp if (!resp.ok) { @@ -243,17 +251,24 @@ const getIbexToken = async (): Promise => { return new IbexError(new Error(`IBEX sign-in failed: ${resp.status} — ${body}`)) } - const data = await resp.json() as { + const data = (await resp.json()) as { accessToken?: string accessTokenExpiresAt?: number refreshToken?: string refreshTokenExpiresAt?: number } - if (!data.accessToken) return new IbexError(new Error("IBEX sign-in: no access token in response")) + if (!data.accessToken) + return new IbexError(new Error("IBEX sign-in: no access token in response")) - await Ibex.authentication.storage.setAccessToken(data.accessToken, data.accessTokenExpiresAt) + await Ibex.authentication.storage.setAccessToken( + data.accessToken, + data.accessTokenExpiresAt, + ) if (data.refreshToken) { - await Ibex.authentication.storage.setRefreshToken(data.refreshToken, data.refreshTokenExpiresAt) + await Ibex.authentication.storage.setRefreshToken( + data.refreshToken, + data.refreshTokenExpiresAt, + ) } return `Bearer ${data.accessToken}` @@ -267,7 +282,11 @@ const ibexFetch = async ( const url = `${IbexConfig.url}${path}` const resp = await fetch(url, { ...init, - headers: { Authorization: token, "Content-Type": "application/json", ...init.headers }, + headers: { + "Authorization": token, + "Content-Type": "application/json", + ...init.headers, + }, }) if (!resp.ok) { const body = await resp.text().catch(() => "") diff --git a/src/services/ibex/types.ts b/src/services/ibex/types.ts index 4a02bf260..ec3d55015 100644 --- a/src/services/ibex/types.ts +++ b/src/services/ibex/types.ts @@ -1,4 +1,4 @@ -import { USDAmount } from "@domain/shared" +import { USDAmount, USDTAmount } from "@domain/shared" export type PayInvoiceArgs = { accountId: IbexAccountId @@ -7,8 +7,8 @@ export type PayInvoiceArgs = { } export type SendOnchainArgs = { - accountId: IbexAccountId, // source of funds - address: OnChainAddress, // destination + accountId: IbexAccountId // source of funds + address: OnChainAddress // destination amount: USDAmount } @@ -27,7 +27,7 @@ export type IbexAccountDetails = { id: string | undefined userId: string | undefined name: string | undefined - balance: USDAmount | undefined + balance: USDAmount | USDTAmount | undefined } export type IbexInvoiceArgs = { diff --git a/src/services/mongoose/wallets.ts b/src/services/mongoose/wallets.ts index 30f5444f5..300be6fbd 100644 --- a/src/services/mongoose/wallets.ts +++ b/src/services/mongoose/wallets.ts @@ -16,11 +16,13 @@ import Ibex from "@services/ibex/client" import { IbexError } from "@services/ibex/errors" +import { recordExceptionInCurrentSpan } from "@services/tracing" + +import { ErrorLevel, USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + import { toObjectId, fromObjectId, parseRepositoryError } from "./utils" import { Wallet } from "./schema" import { AccountsRepository } from "./accounts" -import { recordExceptionInCurrentSpan } from "@services/tracing" -import { ErrorLevel, USDAmount, WalletCurrency } from "@domain/shared" import { WalletType } from "@domain/wallets" export interface WalletRecord { @@ -32,6 +34,19 @@ export interface WalletRecord { lnurlp: string } +const getIbexCurrencyId = ( + currency: WalletCurrency, +): IbexCurrencyId | UnsupportedCurrencyError => { + switch (currency) { + case WalletCurrency.Usd: + return USDAmount.currencyId + case WalletCurrency.Usdt: + return USDTAmount.currencyId + default: + return new UnsupportedCurrencyError(`Unsupported IBEX wallet currency: ${currency}`) + } +} + export const WalletsRepository = (): IWalletsRepository => { const persistNew = async ({ accountId, @@ -42,7 +57,8 @@ export const WalletsRepository = (): IWalletsRepository => { if (account instanceof Error) return account try { - let currencyId = USDAmount.currencyId + const currencyId = getIbexCurrencyId(currency) + if (currencyId instanceof Error) return currencyId const resp = await Ibex.createAccount(accountId, currencyId) if (resp instanceof IbexError) return resp @@ -63,8 +79,7 @@ export const WalletsRepository = (): IWalletsRepository => { ibexAccountId, }, }) - } - else lnurlp = lnurlResp.lnurl + } else lnurlp = lnurlResp.lnurl } const wallet = new Wallet({ @@ -72,7 +87,7 @@ export const WalletsRepository = (): IWalletsRepository => { id: ibexAccountId, type, currency, - lnurlp + lnurlp, }) await wallet.save() return resultToWallet(wallet) @@ -162,7 +177,7 @@ export const WalletsRepository = (): IWalletsRepository => { lnurlp, }: { accountId: AccountId - currency: WalletCurrency + currency?: WalletCurrency lnurlp: Lnurl }): Promise => { if (!lnurlp.toLowerCase().startsWith("lnurl1")) { @@ -173,7 +188,7 @@ export const WalletsRepository = (): IWalletsRepository => { { _accountId: toObjectId(accountId), type: WalletType.External }, { $set: { lnurlp }, - $setOnInsert: { id: randomUUID(), currency: currency }, + $setOnInsert: { id: randomUUID(), currency: currency ?? WalletCurrency.Btc }, }, { upsert: true, new: true }, ) diff --git a/test/flash/unit/app/accounts/create-account.spec.ts b/test/flash/unit/app/accounts/create-account.spec.ts new file mode 100644 index 000000000..4c006e9d1 --- /dev/null +++ b/test/flash/unit/app/accounts/create-account.spec.ts @@ -0,0 +1,122 @@ +import { createAccountWithPhoneIdentifier } from "@app/accounts/create-account" +import { AccountLevel } from "@domain/accounts" +import { WalletCurrency } from "@domain/shared" +import { PersistError } from "@domain/errors" +import { WalletType } from "@domain/wallets" +import { + AccountsRepository, + UsersRepository, + WalletsRepository, +} from "@services/mongoose" + +jest.mock("@config", () => ({ + getAdminAccounts: jest.fn(() => []), +})) + +jest.mock("@services/tracing", () => ({ + recordExceptionInCurrentSpan: jest.fn(), +})) + +jest.mock("@services/mongoose", () => ({ + AccountsRepository: jest.fn(), + UsersRepository: jest.fn(), + WalletsRepository: jest.fn(), +})) + +const mockedAccountsRepository = AccountsRepository as jest.MockedFunction< + typeof AccountsRepository +> +const mockedUsersRepository = UsersRepository as jest.MockedFunction< + typeof UsersRepository +> +const mockedWalletsRepository = WalletsRepository as jest.MockedFunction< + typeof WalletsRepository +> + +describe("createAccountWithPhoneIdentifier", () => { + let persistNew: jest.Mock + + const account = { + id: "account-id" as AccountId, + defaultWalletId: undefined, + } as unknown as Account + + const config = { + initialWallets: [WalletCurrency.Usd], + initialStatus: "active", + initialLevel: AccountLevel.One, + } as AccountsConfig + + beforeEach(() => { + jest.clearAllMocks() + + mockedUsersRepository.mockReturnValue({ + update: jest.fn().mockResolvedValue({ id: "user-id" }), + } as unknown as ReturnType) + + mockedAccountsRepository.mockReturnValue({ + persistNew: jest.fn().mockResolvedValue({ ...account }), + update: jest + .fn() + .mockImplementation(async (updatedAccount: Account) => updatedAccount), + } as unknown as ReturnType) + + persistNew = jest.fn().mockImplementation(async ({ accountId, type, currency }) => ({ + id: `${currency}-wallet-id`, + accountId, + type, + currency, + })) + + mockedWalletsRepository.mockReturnValue({ + persistNew, + } as unknown as ReturnType) + }) + + it("creates both USD and USDT cash wallets and defaults new accounts to USDT", async () => { + const result = await createAccountWithPhoneIdentifier({ + newAccountInfo: { + kratosUserId: "kratos-user-id" as UserId, + phone: "+15551234567" as PhoneNumber, + }, + config, + }) + + expect(result).not.toBeInstanceOf(Error) + + expect(persistNew).toHaveBeenCalledWith({ + accountId: account.id, + type: WalletType.Checking, + currency: WalletCurrency.Usd, + }) + expect(persistNew).toHaveBeenCalledWith({ + accountId: account.id, + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }) + expect(persistNew).toHaveBeenCalledTimes(2) + expect((result as Account).defaultWalletId).toBe(`${WalletCurrency.Usdt}-wallet-id`) + }) + + it("does not create an account with a USD fallback default if the USDT wallet is missing", async () => { + persistNew.mockImplementation(async ({ accountId, type, currency }) => { + if (currency === WalletCurrency.Usdt) return new PersistError("USDT wallet failed") + return { + id: `${currency}-wallet-id`, + accountId, + type, + currency, + } + }) + + const result = await createAccountWithPhoneIdentifier({ + newAccountInfo: { + kratosUserId: "kratos-user-id" as UserId, + phone: "+15551234567" as PhoneNumber, + }, + config, + }) + + expect(result).toBeInstanceOf(Error) + }) +}) diff --git a/test/flash/unit/app/wallets/get-balance-for-wallet.spec.ts b/test/flash/unit/app/wallets/get-balance-for-wallet.spec.ts new file mode 100644 index 000000000..4c7fe2680 --- /dev/null +++ b/test/flash/unit/app/wallets/get-balance-for-wallet.spec.ts @@ -0,0 +1,38 @@ +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import { USDTAmount, WalletCurrency } from "@domain/shared" +import Ibex from "@services/ibex/client" + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + getAccountDetails: jest.fn(), + getCryptoReceiveBalance: jest.fn(), + }, +})) + +describe("getBalanceForWallet", () => { + beforeEach(() => { + jest.mocked(Ibex.getAccountDetails).mockReset() + jest.mocked(Ibex.getCryptoReceiveBalance).mockReset() + }) + + it("loads USDT balances from the IBEX account id, not a crypto receive-info id", async () => { + const balance = USDTAmount.ZERO + jest.mocked(Ibex.getAccountDetails).mockResolvedValue({ + id: "ibex-account-id", + balance, + } as never) + + const result = await getBalanceForWallet({ + walletId: "ibex-account-id" as WalletId, + currency: WalletCurrency.Usdt, + }) + + expect(Ibex.getAccountDetails).toHaveBeenCalledWith( + "ibex-account-id", + WalletCurrency.Usdt, + ) + expect(Ibex.getCryptoReceiveBalance).not.toHaveBeenCalled() + expect(result).toBe(balance) + }) +}) diff --git a/test/flash/unit/services/mongoose/wallets.spec.ts b/test/flash/unit/services/mongoose/wallets.spec.ts new file mode 100644 index 000000000..9707694cd --- /dev/null +++ b/test/flash/unit/services/mongoose/wallets.spec.ts @@ -0,0 +1,82 @@ +import { UnsupportedCurrencyError } from "@domain/errors" +import { WalletCurrency } from "@domain/shared" +import Ibex from "@services/ibex/client" +import { AccountsRepository } from "@services/mongoose/accounts" +import { WalletsRepository } from "@services/mongoose/wallets" + +const save = jest.fn() +const walletConstructor = jest.fn().mockImplementation((record) => ({ + ...record, + save, +})) + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + createAccount: jest.fn(), + createLnurlPay: jest.fn(), + }, +})) + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/schema", () => ({ + Wallet: jest.fn().mockImplementation((record) => walletConstructor(record)), +})) + +jest.mock("@services/mongoose/utils", () => ({ + toObjectId: jest.fn((id) => id), + fromObjectId: jest.fn((id) => id), + parseRepositoryError: jest.fn((err) => err), +})) + +describe("WalletsRepository.persistNew", () => { + beforeEach(() => { + save.mockReset().mockResolvedValue(undefined) + walletConstructor.mockClear() + jest.mocked(AccountsRepository).mockReturnValue({ + findById: jest.fn().mockResolvedValue({ id: "account-id" }), + } as never) + jest + .mocked(Ibex.createAccount) + .mockReset() + .mockResolvedValue({ + id: "ibex-account-id", + } as never) + jest + .mocked(Ibex.createLnurlPay) + .mockReset() + .mockResolvedValue({ + lnurl: "lnurlp", + } as never) + }) + + it("rejects currencies without an IBEX account currency id", async () => { + const result = await WalletsRepository().persistNew({ + accountId: "account-id" as AccountId, + type: "checking" as WalletType, + currency: WalletCurrency.Btc, + }) + + expect(result).toBeInstanceOf(UnsupportedCurrencyError) + expect(Ibex.createAccount).not.toHaveBeenCalled() + expect(Ibex.createLnurlPay).not.toHaveBeenCalled() + }) + + it("creates USDT wallets as IBEX currency 29 accounts", async () => { + const result = await WalletsRepository().persistNew({ + accountId: "account-id" as AccountId, + type: "checking" as WalletType, + currency: WalletCurrency.Usdt, + }) + + expect(result).not.toBeInstanceOf(Error) + expect(Ibex.createAccount).toHaveBeenCalledWith("account-id", 29) + expect(Ibex.createLnurlPay).toHaveBeenCalledWith({ + accountId: "ibex-account-id", + currencyId: 29, + }) + }) +}) From 2096387dd9e35237d75354b92c8feafadb3bdcbb Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:31 -0400 Subject: [PATCH 05/43] ENG-297 docs: document Bridge rebase PR-readiness plan --- ...026-05-13-bridge-rebase-pr-ready-design.md | 30 ++++ .../2026-05-13-bridge-rebase-pr-ready.md | 130 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md create mode 100644 docs/plans/2026-05-13-bridge-rebase-pr-ready.md diff --git a/docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md b/docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md new file mode 100644 index 000000000..dee60cd28 --- /dev/null +++ b/docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md @@ -0,0 +1,30 @@ +# Bridge Rebase PR-Ready Design + +## Goal +Prepare the rebased Bridge integration branch for review by making the history Linear-scoped, applying only intentional cleanup, and verifying branch-owned changes without expanding lint scope to unrelated code. + +## Constraints +- Every commit in the PR history must reference at least one Linear issue key. +- If multiple commits belong to one Linear issue, squash them unless separation materially improves reviewability. +- Do not lint code that this branch did not change or create. +- Preserve the current rebased branch as a safety point. +- Do not import the broad ForgeMini scratch formatting/dependency churn; cherry-pick only intentional fixes. + +## Approach +Work from a new safety branch, reset the index against `origin/main`, and recommit the branch diff into Linear-scoped slices. Use existing Bridge Linear issues for core feature, security-audit fixes, reconciliation/replay tooling, account wallet creation, and PR-readiness cleanup. After history rewrite, apply the proven Bridge unit/idempotency fix selectively and verify build/unit/focused tests plus scoped lint only on files changed by the branch. + +## Commit grouping +- `ENG-297`: core Bridge parity/integration surface and docs that support the launch path. +- `ENG-276`: deposit reconciliation and replay/backfill tooling. +- `ENG-348`: ERPNext audit-row writer support, if present in the diff. +- `ENG-376`: webhook/request idempotency hardening and retry-safety fixes. +- `ENG-394`: ETH-USDT Cash Wallet creation for raw account creation. +- Existing audit issues (`ENG-278`, `ENG-280`, `ENG-281`, `ENG-282`, `ENG-283`, `ENG-284`, `ENG-285`, `ENG-349`, `ENG-363`, etc.) stay mapped in commit messages where their changes are separable. + +## Verification +- `yarn build` +- full unit suite if available in this worktree +- focused Bridge unit suites +- typecheck, with baseline failures documented separately if reproduced on `origin/main` +- scoped ESLint only for branch-created or branch-modified files, never unrelated files +- SDL diff check and commit regenerated schema/supergraph output only when branch-owned changes require it diff --git a/docs/plans/2026-05-13-bridge-rebase-pr-ready.md b/docs/plans/2026-05-13-bridge-rebase-pr-ready.md new file mode 100644 index 000000000..43f3a608f --- /dev/null +++ b/docs/plans/2026-05-13-bridge-rebase-pr-ready.md @@ -0,0 +1,130 @@ +# Bridge Rebase PR-Ready Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Make the rebased Bridge integration branch PR-ready with Linear-scoped commits and scoped verification. + +**Architecture:** Preserve the completed rebase as a safety point, then use a cleanup branch to rewrite the diff into issue-scoped commits. Apply only targeted fixes from scratch worktrees and verify branch-owned behavior without linting unrelated code. + +**Tech Stack:** TypeScript, GraphQL, Jest, Yarn, Git, Linear issue keys, Bridge.xyz integration code. + +--- + +### Task 1: Create safety branch and planning docs + +**Files:** +- Create: `docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md` +- Create: `docs/plans/2026-05-13-bridge-rebase-pr-ready.md` + +**Step 1:** Switch to a cleanup branch at current rebased HEAD. + +Run: `git switch -c tmp/bridge-rebase-pr-ready` +Expected: branch points to the rebased candidate HEAD. + +**Step 2:** Add the design and implementation plan docs. + +Run: `git status --short` +Expected: only the two new docs are untracked. + +**Step 3:** Commit with a Linear key. + +Run: `git add docs/plans && git commit -m "ENG-297 docs: document Bridge rebase PR-readiness plan"` +Expected: commit succeeds and includes only plan docs. + +### Task 2: Rewrite existing branch history into Linear-scoped commits + +**Files:** +- Modify: all files in the current `origin/main..HEAD` diff. + +**Step 1:** Capture current HEAD. + +Run: `git branch safety/bridge-rebase-before-history-rewrite HEAD` +Expected: safety ref exists. + +**Step 2:** Soft reset to `origin/main`. + +Run: `git reset --soft origin/main && git restore --staged .` +Expected: full branch diff is unstaged working tree changes. + +**Step 3:** Stage and commit logical groups. + +Use path-based staging and `git add -p` where needed. Each commit message must start with or include a Linear issue key. + +Expected commit groups: +- `ENG-297 feat(bridge): add core Bridge parity integration` +- `ENG-276 feat(bridge): add reconciliation and replay tooling` +- `ENG-348 feat(bridge): add ERPNext audit rows for Bridge movements` +- `ENG-376 fix(bridge): harden Bridge request idempotency` +- `ENG-394 feat(accounts): create ETH-USDT Cash Wallet for new accounts` +- Audit fixes mapped to their issue keys where separable. + +**Step 4:** Verify all commits have Linear keys. + +Run: `git log --format='%h %s' origin/main..HEAD | awk '!/ENG-[0-9]+|OPS-[0-9]+|COM-[0-9]+/ {print}'` +Expected: no output. + +### Task 3: Apply targeted Bridge unit/idempotency fix + +**Files:** +- Modify: `src/services/bridge/index.ts` +- Modify: `test/flash/unit/services/bridge/index.spec.ts` + +**Step 1:** Cherry-pick only intentional hunks from ForgeMini scratch worktree. + +Expected changes: +- Mock `@services/ibex/client` in Bridge service unit test. +- Before creating a new withdrawal row, call `BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer(accountId, externalAccountId, amount)`. +- Reuse an existing pending withdrawal row to derive the same idempotency key on retry. + +**Step 2:** Run focused test. + +Run: `yarn jest test/flash/unit/services/bridge/index.spec.ts --runInBand` +Expected: Bridge service unit suite passes. + +**Step 3:** Commit. + +Run: `git add src/services/bridge/index.ts test/flash/unit/services/bridge/index.spec.ts && git commit -m "ENG-376 fix(bridge): reuse pending withdrawal idempotency rows"` +Expected: commit succeeds. + +### Task 4: Verify PR readiness without unrelated lint + +**Files:** +- Test/build commands operate on the branch. +- Lint command must be limited to files changed by `origin/main..HEAD`. + +**Step 1:** Run build. + +Run: `yarn build` +Expected: pass, or document branch-independent baseline failure. + +**Step 2:** Run focused Bridge tests. + +Run: focused Jest commands for Bridge service/client/webhook suites. +Expected: pass. + +**Step 3:** Run full unit suite if dependencies are present. + +Run: repository unit test command. +Expected: pass, or document failing suites with ownership. + +**Step 4:** Run scoped lint only on branch-owned files. + +Run: construct changed-file list with `git diff --name-only origin/main...HEAD` and pass only supported TS/JS files to ESLint. +Expected: no branch-owned lint failures, or documented remaining branch-owned fixes. + +**Step 5:** Check SDL/schema drift. + +Run: repository SDL generation/check command. +Expected: either clean, or commit required generated schema artifacts with the relevant ENG key. + +### Task 5: Prepare PR branch + +**Step 1:** Show final summary. + +Run: `git status --short --branch`, `git log --oneline origin/main..HEAD`, and `git diff --shortstat origin/main...HEAD`. +Expected: clean worktree and Linear-scoped commits. + +**Step 2:** Push only after explicit approval if force-updating `feature/bridge-integration`. + +Run when approved: `git push --force-with-lease origin HEAD:feature/bridge-integration` +Expected: remote branch updates safely. From 18f1ac7cb83f345fa3458debdc363403481f2846 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:03:43 -0400 Subject: [PATCH 06/43] ENG-376 fix(bridge): reuse pending withdrawal idempotency rows --- src/services/bridge/index.ts | 26 ++++++++++++------- test/flash/unit/services/bridge/index.spec.ts | 8 ++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index ff73924bd..fb308bdbd 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -416,7 +416,7 @@ const initiateWithdrawal = async ( return new BridgeInsufficientFundsError("Invalid withdrawal amount") } - const availableBalance = balance.toIbex(); + const availableBalance = balance.toIbex() if (availableBalance < withdrawalAmount) { baseLogger.warn( { accountId, availableBalance, withdrawalAmount, operation: "initiateWithdrawal" }, @@ -445,15 +445,23 @@ const initiateWithdrawal = async ( return new Error("External account is not verified") } - - // Store withdrawal record - const pendingWithdrawal = await BridgeAccountsRepo.createWithdrawal({ - accountId: accountId as string, - amount: amount, - currency: "usdt", + const existingWithdrawal = await BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer( + accountId as string, externalAccountId, - status: "pending", - }) + amount, + ) + if (existingWithdrawal instanceof Error) return existingWithdrawal + + // Store withdrawal record, or reuse the in-flight row for a retry of the same request. + const pendingWithdrawal = + existingWithdrawal || + (await BridgeAccountsRepo.createWithdrawal({ + accountId: accountId as string, + amount, + currency: "usdt", + externalAccountId, + status: "pending", + })) if (pendingWithdrawal instanceof Error) return pendingWithdrawal const idempotencyKey = deriveWithdrawalIdempotencyKey(pendingWithdrawal.id) diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts index 20aa5f9ad..4324021d2 100644 --- a/test/flash/unit/services/bridge/index.spec.ts +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -29,6 +29,14 @@ jest.mock("@services/bridge/client", () => ({ default: { createTransfer: jest.fn() }, })) +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + getEthereumUsdtOption: jest.fn(), + createCryptoReceiveInfo: jest.fn(), + }, +})) + jest.mock("@services/mongoose/accounts", () => ({ AccountsRepository: jest.fn(), })) From dd759015919909d41dc5a0e05eaccadf8006cf77 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:13:08 -0400 Subject: [PATCH 07/43] ENG-297 chore(bridge): satisfy scoped lint on branch changes --- src/app/offers/Validator.ts | 54 +++++-- src/config/yaml.ts | 18 ++- src/domain/wallets/index.types.d.ts | 11 +- src/graphql/error-map.ts | 10 +- src/graphql/public/queries.ts | 1 + .../root/mutation/bridge-initiate-kyc.ts | 7 +- .../mutation/bridge-initiate-withdrawal.ts | 6 +- .../types/object/bridge-virtual-account.ts | 5 +- .../shared/types/object/usdt-wallet.ts | 18 ++- ...bridge-virtual-account-unique-accountid.ts | 1 + src/servers/graphql-admin-server.ts | 39 +++-- src/servers/graphql-server.ts | 4 +- src/services/bridge/client.ts | 110 +++++++++----- src/services/bridge/errors.ts | 4 +- src/services/bridge/index.ts | 113 ++++++++------ src/services/bridge/webhook-server/index.ts | 15 +- .../middleware/verify-signature.ts | 17 ++- .../bridge/webhook-server/routes/kyc.ts | 8 +- .../bridge/webhook-server/routes/transfer.ts | 2 +- src/services/ibex/webhook-server/index.ts | 1 + src/services/mongoose/accounts.ts | 1 - src/services/mongoose/bridge-accounts.ts | 5 +- src/services/mongoose/bridge-replay-log.ts | 62 ++++---- src/services/mongoose/wallets.ts | 6 +- .../flash/unit/services/bridge/client.spec.ts | 36 ++++- test/flash/unit/services/bridge/index.spec.ts | 51 ++++--- .../bridge/webhook-server/deposit.spec.ts | 9 +- .../bridge/webhook-server/replay.spec.ts | 139 ++++++++++-------- 28 files changed, 478 insertions(+), 275 deletions(-) diff --git a/src/app/offers/Validator.ts b/src/app/offers/Validator.ts index e16b97735..3b113edc9 100644 --- a/src/app/offers/Validator.ts +++ b/src/app/offers/Validator.ts @@ -1,9 +1,22 @@ -import { getBalanceForWallet } from "@app/wallets"; -import { Cashout } from "@config"; -import { AccountValidator, hasErpParty, isActiveAccount, walletBelongsToAccount } from "@domain/accounts"; -import { JMDAmount, USDAmount, USDTAmount, ValidationError, ValidationFn, validator } from "@domain/shared"; -import { ValidationInputs } from "./types"; -import ErpNext from "@services/frappe/ErpNext"; +import { getBalanceForWallet } from "@app/wallets" +import { Cashout } from "@config" +import { + AccountValidator, + hasErpParty, + isActiveAccount, + walletBelongsToAccount, +} from "@domain/accounts" +import { + JMDAmount, + USDAmount, + USDTAmount, + ValidationError, + ValidationFn, + validator, +} from "@domain/shared" +import ErpNext from "@services/frappe/ErpNext" + +import { ValidationInputs } from "./types" const config = Cashout.validations @@ -21,7 +34,9 @@ const cashoutMin = async (o: ValidationInputs): Promise else return true } -const cashoutMax: ValidationFn = async (o: ValidationInputs): Promise => { +const cashoutMax: ValidationFn = async ( + o: ValidationInputs, +): Promise => { const max = USDAmount.cents(config.maximum.amount) if (max instanceof Error) return new ValidationError(max) if (o.payment.amount.isGreaterThan(max)) @@ -35,13 +50,14 @@ const isUsd = async (o: ValidationInputs) => { return true } -const hasSufficientBalance = async (o: ValidationInputs): Promise => { +const hasSufficientBalance = async ( + o: ValidationInputs, +): Promise => { const balance = await getBalanceForWallet({ walletId: o.wallet.id, currency: o.wallet.currency, }) - if (balance instanceof Error) - return new ValidationError(balance) + if (balance instanceof Error) return new ValidationError(balance) if (balance instanceof USDTAmount) return new ValidationError("Cash out only supports withdrawals from USD wallets") else if (o.payment.amount.isGreaterThan(balance)) @@ -53,17 +69,23 @@ const accountLevel = async (o: ValidationInputs) => { return AccountValidator(o.account).isLevel(config.accountLevel) } -// Much of this logic is checked server-side in erpnext, but we want to catch it as early as possible -const verifyBankAccount = async (o: ValidationInputs): Promise => { +// Much of this logic is checked server-side in erpnext, but we want to catch it as early as possible +const verifyBankAccount = async ( + o: ValidationInputs, +): Promise => { const erpParty = o.account.erpParty - if (!erpParty) return new ValidationError("Account does not have an associated erpParty") + if (!erpParty) + return new ValidationError("Account does not have an associated erpParty") const banks = await ErpNext.getBankAccountsByCustomer(erpParty) - if (banks instanceof Error) return new ValidationError("Could not confirm bank account for user") - const bankAccount = banks.find(b => b.name === o.payout.bankAccountId) + if (banks instanceof Error) + return new ValidationError("Could not confirm bank account for user") + const bankAccount = banks.find((b) => b.name === o.payout.bankAccountId) if (!bankAccount) return new ValidationError("Bank account does not belong to user") const payoutCurrency = o.payout.amount instanceof JMDAmount ? "JMD" : "USD" if (bankAccount.currency !== payoutCurrency) - return new ValidationError(`Bank account currency (${bankAccount.currency}) does not match payout currency (${payoutCurrency})`) + return new ValidationError( + `Bank account currency (${bankAccount.currency}) does not match payout currency (${payoutCurrency})`, + ) return true } diff --git a/src/config/yaml.ts b/src/config/yaml.ts index 136300441..4d0058c9c 100644 --- a/src/config/yaml.ts +++ b/src/config/yaml.ts @@ -3,7 +3,7 @@ import fs from "fs" import path from "path" import Ajv from "ajv" -import yaml from "js-yaml" +import { load as loadYaml } from "js-yaml" import { I18n } from "i18n" import { baseLogger } from "@services/logger" @@ -21,19 +21,19 @@ import { AccountLevel } from "@domain/accounts" import mergeWith from "lodash.mergewith" +import yargs from "yargs" + import { configSchema } from "./schema" import { ConfigError } from "./error" -import yargs from "yargs" - -const argv: any = +const argv = // .help() yargs(process.argv.slice(2)).option("configPath", { alias: "c", type: "array", description: "Paths to YAML configuration files", demandOption: true, - }).argv + }).argv as { configPath: string[] } // replaces array with override const merge = (defaultConfig: unknown, customConfig: unknown) => @@ -46,7 +46,7 @@ export const mergeYamls = (filePaths: string[]): Record => { try { const resolvedPath = path.resolve(filePath) const fileContent = fs.readFileSync(resolvedPath, "utf8") - const parsedConfig = yaml.load(fileContent) as Record + const parsedConfig = loadYaml(fileContent) as Record merge(mergedConfig, parsedConfig) @@ -268,8 +268,10 @@ export const getTestAccounts = (config = yamlConfig): TestAccount[] => export const getCronConfig = (config = yamlConfig): CronConfig => config.cronConfig -export const getDefaultFCMTopics = (config = yamlConfig): string[] => config.fcmTopics.filter(t => t.default).map(t => t.name) -export const getFCMTopics = (config = yamlConfig): string[] => config.fcmTopics.map(t => t.name) +export const getDefaultFCMTopics = (config = yamlConfig): string[] => + config.fcmTopics.filter((t) => t.default).map((t) => t.name) +export const getFCMTopics = (config = yamlConfig): string[] => + config.fcmTopics.map((t) => t.name) export const getCaptcha = (config = yamlConfig): CaptchaConfig => config.captcha diff --git a/src/domain/wallets/index.types.d.ts b/src/domain/wallets/index.types.d.ts index ee5e1fb23..d86dcbc21 100644 --- a/src/domain/wallets/index.types.d.ts +++ b/src/domain/wallets/index.types.d.ts @@ -200,7 +200,15 @@ interface IWalletsRepository { walletCurrency: WalletCurrency, ): Promise - upsertExternal({ accountId, currency, lnurlp }: { accountId: AccountId, currency?: WalletCurrency, lnurlp: Lnurl }): Promise + upsertExternal({ + accountId, + currency, + lnurlp, + }: { + accountId: AccountId + currency?: WalletCurrency + lnurlp: Lnurl + }): Promise findExternalByAccountId(accountId: AccountId): Promise } @@ -249,4 +257,3 @@ type PaymentInputValidator = { args: ValidatePaymentInputArgs, ) => Promise | ValidationError | RepositoryError> } - diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 95a461d66..c53a4b37f 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -40,7 +40,7 @@ import { } from "@graphql/error" import { baseLogger } from "@services/logger" -const assertUnreachable = (x: any): never => { +const assertUnreachable = (x: unknown): never => { throw new Error(`This should never compile with ${x}`) } @@ -477,7 +477,8 @@ export const mapError = (error: ApplicationError): CustomApolloError => { }) case "BridgeInvalidAmountError": - message = error.message || "Amount must be strictly positive with at most 6 decimal places" + message = + error.message || "Amount must be strictly positive with at most 6 decimal places" return new ValidationInternalError({ message, logger: baseLogger }) case "BridgeBelowMinimumWithdrawalError": @@ -803,17 +804,16 @@ export const apolloErrorResponse = (e: CustomApolloError): { errors: IError[] } path: e.path, code: e.extensions.code, }, - ] + ], } } - export const mapAndParseErrorForGqlResponse = (err: ApplicationError): IError => { const mappedError = mapError(err) return { message: mappedError.message, path: mappedError.path, - code: mappedError.extensions.code + code: mappedError.extensions.code, } } diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index 2d95858b7..4596d6e48 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -16,6 +16,7 @@ import BusinessMapMarkersQuery from "@graphql/public/root/query/business-map-mar import AccountDefaultWalletQuery from "@graphql/public/root/query/account-default-wallet" import AccountDefaultWalletIdQuery from "@graphql/public/root/query/account-default-wallet-id" import LnInvoicePaymentStatusQuery from "@graphql/public/root/query/ln-invoice-payment-status" + import NpubByUserNameQuery from "./root/query/username-npub-query" import IsFlashNpubQuery from "./root/query/is-flash-npub-query" import TransactionDetailsQuery from "./root/query/transaction-details" diff --git a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts index 89da4f77a..3f1574c1a 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts @@ -38,7 +38,12 @@ const bridgeInitiateKyc = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } - const result = await BridgeService.initiateKyc({ accountId: domainAccount.id, email, type, full_name }) + const result = await BridgeService.initiateKyc({ + accountId: domainAccount.id, + email, + type, + full_name, + }) if (result instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(result)] } } diff --git a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts index afef326e6..945e7624c 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts @@ -45,7 +45,11 @@ const bridgeInitiateWithdrawal = GT.Field({ // validate the amount is greater than the minimum withdrawal amount if (parseFloat(amount) < BridgeConfig.minWithdrawalAmount) { return { - errors: [mapAndParseErrorForGqlResponse(new BridgeBelowMinimumWithdrawalError(BridgeConfig.minWithdrawalAmount))], + errors: [ + mapAndParseErrorForGqlResponse( + new BridgeBelowMinimumWithdrawalError(BridgeConfig.minWithdrawalAmount), + ), + ], } } diff --git a/src/graphql/public/types/object/bridge-virtual-account.ts b/src/graphql/public/types/object/bridge-virtual-account.ts index f2c294b83..02a8c99b2 100644 --- a/src/graphql/public/types/object/bridge-virtual-account.ts +++ b/src/graphql/public/types/object/bridge-virtual-account.ts @@ -3,7 +3,10 @@ import { GT } from "@graphql/index" const BridgeVirtualAccount = GT.Object({ name: "BridgeVirtualAccount", fields: () => ({ - id: { type: GT.ID, resolve: (src) => src.virtualAccountId ?? src.bridgeVirtualAccountId }, + id: { + type: GT.ID, + resolve: (src) => src.virtualAccountId ?? src.bridgeVirtualAccountId, + }, bankName: { type: GT.String }, routingNumber: { type: GT.String }, accountNumber: { type: GT.String }, diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index d91a0e613..59139203e 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -9,19 +9,24 @@ import { mapError } from "@graphql/error-map" import { Wallets } from "@app" -import { WalletCurrency as WalletCurrencyDomain, USDTAmount } from "@domain/shared" +import { + USDAmount, + WalletCurrency as WalletCurrencyDomain, + USDTAmount, +} from "@domain/shared" + +import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import IWallet from "../abstract/wallet" import WalletCurrency from "../scalar/wallet-currency" import SignedAmount from "../scalar/signed-amount" import OnChainAddress from "../scalar/on-chain-address" -import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" -import { TransactionConnection } from "./transaction" -import { baseLogger } from "@services/logger" import Lnurl from "../scalar/lnurl" +import { TransactionConnection } from "./transaction" + const UsdtWallet = GT.Object({ name: "UsdtWallet", description: @@ -58,7 +63,10 @@ const UsdtWallet = GT.Object({ if (balance instanceof USDTAmount) { return Number(balance.asSmallestUnits(8)) } - return Number((balance as any).asCents(8)) + if (balance instanceof USDAmount) { + return Number(balance.asCents(8)) + } + throw mapError(balance) }, }, pendingIncomingBalance: { diff --git a/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts b/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts index 3f6f6f71a..3b6d676b4 100644 --- a/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts +++ b/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-nocheck /* eslint @typescript-eslint/no-var-requires: "off" */ diff --git a/src/servers/graphql-admin-server.ts b/src/servers/graphql-admin-server.ts index 3a31cae69..52d65959e 100644 --- a/src/servers/graphql-admin-server.ts +++ b/src/servers/graphql-admin-server.ts @@ -1,13 +1,14 @@ +import { createServer } from "http" + import { applyMiddleware } from "graphql-middleware" -import { and, or, rule, shield } from "graphql-shield" -import { Rule, RuleAnd, RuleOr } from "graphql-shield/typings/rules" +import { or, rule, shield } from "graphql-shield" +import { Rule, RuleOr } from "graphql-shield/typings/rules" import { baseLogger } from "@services/logger" import { setupMongoConnection } from "@services/mongodb" import { adminMutationFields, adminQueryFields, gqlAdminSchema } from "@graphql/admin" import { ADMIN_CONFIG } from "@config" import { AuthenticationError, AuthorizationError } from "@graphql/error" -import { createServer } from "http" import express from "express" import { ApolloServerPluginDrainHttpServer } from "apollo-server-core" import { ApolloError, ApolloServer, ExpressContext } from "apollo-server-express" @@ -15,14 +16,19 @@ import { GraphQLError, GraphQLSchema } from "graphql" import PinoHttp from "pino-http" import { mapError } from "@graphql/error-map" import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity" -import { createComplexityPlugin } from "./plugins/complexity" + import { parseUnknownDomainErrorFromUnknown } from "@domain/shared" -import healthzHandler from "./middlewares/healthz" -import { idempotencyMiddleware } from "./middlewares/idempotency" + import requestIp from "request-ip" + import jwt from "jsonwebtoken" + import { ErpNextRole, ErpNextRoles } from "@services/frappe/Roles" +import { createComplexityPlugin } from "./plugins/complexity" +import healthzHandler from "./middlewares/healthz" +import { idempotencyMiddleware } from "./middlewares/idempotency" + const graphqlLogger = baseLogger.child({ module: "graphql" }) interface JWTPayload { @@ -46,13 +52,12 @@ function parseAuthHeader(authHeader: string | undefined): JWTPayload { } } -export const hasRole = (role: ErpNextRole) => rule({ cache: "contextual" })(( - parent, - args, - ctx: GraphQLAdminContext, -) => { - return ctx.user.roles.includes(role) ? true : new AuthorizationError({ logger: graphqlLogger }) -}) +export const hasRole = (role: ErpNextRole) => + rule({ cache: "contextual" })((parent, args, ctx: GraphQLAdminContext) => { + return ctx.user.roles.includes(role) + ? true + : new AuthorizationError({ logger: graphqlLogger }) + }) // // const ipString = UNSECURE_IP_FROM_REQUEST_OBJECT // // ? req.ip @@ -218,15 +223,17 @@ const startAdminServer = async ({ } export async function startApolloServerForAdminSchema() { - const defaultRule = or(hasRole(ErpNextRoles.SystemManager), hasRole(ErpNextRoles.AccountsManager)) + const defaultRule = or( + hasRole(ErpNextRoles.SystemManager), + hasRole(ErpNextRoles.AccountsManager), + ) const authedQueryFields: { [key: string]: RuleOr } = {} for (const key of Object.keys(adminQueryFields.authed)) { authedQueryFields[key] = defaultRule } - - const mutationRoleOverrides: { [key: string]: Rule } = { + const mutationRoleOverrides: { [key: string]: Rule } = { // sendNotification: hasRole(ErpNextRoles.SystemManager) } diff --git a/src/servers/graphql-server.ts b/src/servers/graphql-server.ts index 6ddc84da6..b630d0cee 100644 --- a/src/servers/graphql-server.ts +++ b/src/servers/graphql-server.ts @@ -16,12 +16,12 @@ import { mapError } from "@graphql/error-map" import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity" -import { createComplexityPlugin } from "./plugins/complexity" - import jwksRsa from "jwks-rsa" import { parseUnknownDomainErrorFromUnknown } from "@domain/shared" +import { createComplexityPlugin } from "./plugins/complexity" + import authRouter from "./authorization" import kratosCallback from "./event-handlers/kratos" import healthzHandler from "./middlewares/healthz" diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index 51f4ec882..e5503fccf 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -4,18 +4,10 @@ */ import crypto from "crypto" + import { BridgeConfig } from "@config" -import { - BridgeCustomerId, - BridgeVirtualAccountId, - BridgeExternalAccountId, - BridgeTransferId, - toBridgeCustomerId, - toBridgeVirtualAccountId, - toBridgeExternalAccountId, - toBridgeTransferId, -} from "@domain/primitives/bridge" +import { BridgeCustomerId, BridgeTransferId } from "@domain/primitives/bridge" // ============ Error Handling ============ @@ -73,7 +65,16 @@ export type CreateCustomerRequest = export interface Customer { id: string type: "individual" | "business" - status?: "active" | "awaiting_questionnaire" | "rejected" | "paused" | "under_review" | "offboarded" | "awaiting_ubo" | "incomplete" | "not_started" + status?: + | "active" + | "awaiting_questionnaire" + | "rejected" + | "paused" + | "under_review" + | "offboarded" + | "awaiting_ubo" + | "incomplete" + | "not_started" has_accepted_terms_of_service?: string created_at: string updated_at: string @@ -90,18 +91,44 @@ export interface KycLink { } // Extended payment rails to include Tron -export type PaymentRail = "ach" | "wire" | "ach_push" | "ach_same_day" | "arbitrum" | "avalanche_c_chain" | "base" | "bre_b" | "co_bank_transfer" | "celo" | "ethereum" | "faster_payments" | "optimism" | "pix" | "polygon" | "sepa" | "solana" | "spei" | "stellar" | "swift" | "tempo" | "tron"; - -export type VirtualAccountDestinationPaymentRail = "arbitrum" | "avalanche_c_chain" | "base" | "celo" | "ethereum" | "optimism" | "polygon" | "solana" | "stellar" | "tempo" | "tron" - - -export type SourceCurrency = - | "usd" - | "eur" - | "mxn" - | "brl" - | "gbp" - | "cop" +export type PaymentRail = + | "ach" + | "wire" + | "ach_push" + | "ach_same_day" + | "arbitrum" + | "avalanche_c_chain" + | "base" + | "bre_b" + | "co_bank_transfer" + | "celo" + | "ethereum" + | "faster_payments" + | "optimism" + | "pix" + | "polygon" + | "sepa" + | "solana" + | "spei" + | "stellar" + | "swift" + | "tempo" + | "tron" + +export type VirtualAccountDestinationPaymentRail = + | "arbitrum" + | "avalanche_c_chain" + | "base" + | "celo" + | "ethereum" + | "optimism" + | "polygon" + | "solana" + | "stellar" + | "tempo" + | "tron" + +export type SourceCurrency = "usd" | "eur" | "mxn" | "brl" | "gbp" | "cop" // Extended currencies to include USDT export type Currency = "usdb" | "usdt" | "dai" | "pyusd" | "usdc" | "eurc" @@ -128,7 +155,7 @@ export interface CreateExternalAccountRequest { country: string } account_type: string | "us" | "iban" | "unknown" | "clabe" | "pix" | "gb" - currency: 'usd' | 'gbp' | 'brl' | 'eur' | string + currency: "usd" | "gbp" | "brl" | "eur" | string account: { account_number: string routing_number: string @@ -185,7 +212,19 @@ export interface ListResponse { cursor?: string } -export type TrasfertSourceCurrency = "brl" | "cop" | "dai" | "eur" | "eurc" | "gbp" | "mxn" | "pyusd" | "usd" | "usdb" | "usdc" | "usdt" +export type TrasfertSourceCurrency = + | "brl" + | "cop" + | "dai" + | "eur" + | "eurc" + | "gbp" + | "mxn" + | "pyusd" + | "usd" + | "usdb" + | "usdc" + | "usdt" export interface CreateTransferRequest { amount?: string currency?: string @@ -252,14 +291,17 @@ export interface Transfer { updated_at: string } - export interface BridgeIntiateKyc { email: string type: "individual" | "business" full_name?: string } -export type BridgeWebhookEventType = "kyc" | "transfer" | "virtual_account" | "external_account" +export type BridgeWebhookEventType = + | "kyc" + | "transfer" + | "virtual_account" + | "external_account" export interface BridgeWebhookEvent { id: string @@ -291,7 +333,6 @@ type WebhookEventsApiResponse = { cursor?: string } - // ============ Bridge Client ============ export class BridgeClient { @@ -324,7 +365,6 @@ export class BridgeClient { } } - const response = await fetch(url, { method, headers, @@ -359,7 +399,10 @@ export class BridgeClient { // ============ KYC ============ - async createKycLink(request: BridgeIntiateKyc, idempotencyKey?: string): Promise { + async createKycLink( + request: BridgeIntiateKyc, + idempotencyKey?: string, + ): Promise { return this.request("POST", "/kyc_links", request, idempotencyKey) } @@ -393,7 +436,8 @@ export class BridgeClient { "POST", `/customers/${customerId}/external_accounts`, data, - idempotencyKey); + idempotencyKey, + ) } async getExternalAccountLinkUrl( @@ -437,7 +481,6 @@ export class BridgeClient { return this.request("GET", `/transfers/${transferId}`) } - // ============ List Events ============ async listEvents(params?: ListEventsParams): Promise> { @@ -478,7 +521,6 @@ export class BridgeClient { cursor, } } - } export default new BridgeClient() @@ -498,4 +540,4 @@ export async function* listAllEvents( cursor = page.has_more ? page.cursor : undefined } while (cursor) -} \ No newline at end of file +} diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index 69340d4b0..6f6572b0c 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -64,7 +64,9 @@ export class BridgeBelowMinimumWithdrawalError extends BridgeError { } export class BridgeInvalidAmountError extends BridgeError { - constructor(message: string = "Amount must be strictly positive with at most 6 decimal places") { + constructor( + message: string = "Amount must be strictly positive with at most 6 decimal places", + ) { super(message) } } diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index fb308bdbd..ecd4891c2 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -7,17 +7,23 @@ import crypto from "crypto" import { BridgeConfig } from "@config" -import BridgeClient, { - KycLink, - VirtualAccount, - ExternalAccountLinkUrl, - Transfer, -} from "./client" + import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" import { AccountsRepository } from "@services/mongoose/accounts" import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" import { baseLogger } from "@services/logger" + +import { RepositoryError } from "@domain/errors" +import { toBridgeCustomerId } from "@domain/primitives/bridge" +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import { USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletsRepository } from "@services/mongoose/wallets" + +import { IdentityRepository } from "@services/kratos" +import IbexClient from "@services/ibex/client" + import { + BridgeInsufficientFundsError, BridgeError, BridgeDisabledError, BridgeAccountLevelError, @@ -25,14 +31,7 @@ import { BridgeKycRejectedError, BridgeCustomerNotFoundError, } from "./errors" -import { RepositoryError } from "@domain/errors" -import { toBridgeCustomerId, toBridgeExternalAccountId } from "@domain/primitives/bridge" -import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" -import { USDTAmount, WalletCurrency } from "@domain/shared" -import { WalletsRepository } from "@services/mongoose/wallets" -import { BridgeInsufficientFundsError } from "./errors" -import { IdentityRepository } from "@services/kratos" -import IbexClient from "@services/ibex/client" +import BridgeApiClient from "./client" // ============ Types ============ @@ -94,7 +93,6 @@ export const deriveWithdrawalIdempotencyKey = (rowId: string): string => // ============ Guards ============ - const checkBridgeEnabled = (): true | BridgeDisabledError => { if (!BridgeConfig.enabled) { return new BridgeDisabledError() @@ -120,7 +118,17 @@ const checkAccountLevel = async ( * - Creates Bridge customer if not exists * - Returns KYC and TOS links */ -const initiateKyc = async ({ accountId, email, type, full_name }: { accountId: AccountId, email: string, type?: "individual" | "business", full_name: string }): Promise => { +const initiateKyc = async ({ + accountId, + email, + type, + full_name, +}: { + accountId: AccountId + email: string + type?: "individual" | "business" + full_name: string +}): Promise => { baseLogger.info({ accountId, operation: "initiateKyc" }, "Bridge operation started") const enabledCheck = checkBridgeEnabled() @@ -133,19 +141,18 @@ const initiateKyc = async ({ accountId, email, type, full_name }: { accountId: A return new BridgeError("KYC already approved for this account") } - const identity = await IdentityRepository().getIdentity(account.kratosUserId); + const identity = await IdentityRepository().getIdentity(account.kratosUserId) if (identity instanceof Error) return identity - const useremail = identity.email; + const useremail = identity.email try { - // Create KYC link - const kycLink = await BridgeClient.createKycLink({ + const kycLink = await BridgeApiClient.createKycLink({ email: useremail || email, type: type || "individual", - full_name: full_name || account.username + full_name: full_name || account.username, }) const result: InitiateKycResult = { @@ -154,12 +161,12 @@ const initiateKyc = async ({ accountId, email, type, full_name }: { accountId: A tosLink: kycLink.tos_link, } - // link the customer Id to the bridge account - const customerId = toBridgeCustomerId(kycLink.customer_id); + // link the customer Id to the bridge account + const customerId = toBridgeCustomerId(kycLink.customer_id) const updateResult = await AccountsRepository().updateBridgeFields(accountId, { bridgeCustomerId: customerId, - bridgeKycStatus: "pending" + bridgeKycStatus: "pending", }) if (updateResult instanceof Error) { @@ -235,7 +242,8 @@ const createVirtualAccount = async ( } // Get or create Ethereum address - let ethereumAddress = account.bridgeEthereumAddress || "0xaF095D35bfDd462165eA7eCF8AC75351a93d72bD" + let ethereumAddress = + account.bridgeEthereumAddress || "0xaF095D35bfDd462165eA7eCF8AC75351a93d72bD" if (!ethereumAddress) { const option = await IbexClient.getEthereumUsdtOption() @@ -263,7 +271,7 @@ const createVirtualAccount = async ( .digest("hex") // Create Bridge virtual account - const virtualAccount = await BridgeClient.createVirtualAccount( + const virtualAccount = await BridgeApiClient.createVirtualAccount( customerId, { source: { currency: "usd" }, @@ -276,7 +284,8 @@ const createVirtualAccount = async ( vaIdempotencyKey, ) - const fullAccountNumber = virtualAccount.source_deposit_instructions.bank_account_number || "" + const fullAccountNumber = + virtualAccount.source_deposit_instructions.bank_account_number || "" // Store virtual account in repository const repoResult = await BridgeAccountsRepo.createVirtualAccount({ @@ -341,7 +350,7 @@ const addExternalAccount = async ( ) } - const linkUrl = await BridgeClient.getExternalAccountLinkUrl(customerId) + const linkUrl = await BridgeApiClient.getExternalAccountLinkUrl(customerId) const result: AddExternalAccountResult = { linkUrl: linkUrl.link_url, @@ -419,7 +428,12 @@ const initiateWithdrawal = async ( const availableBalance = balance.toIbex() if (availableBalance < withdrawalAmount) { baseLogger.warn( - { accountId, availableBalance, withdrawalAmount, operation: "initiateWithdrawal" }, + { + accountId, + availableBalance, + withdrawalAmount, + operation: "initiateWithdrawal", + }, "Insufficient USDT balance for withdrawal", ) return new BridgeInsufficientFundsError( @@ -445,11 +459,12 @@ const initiateWithdrawal = async ( return new Error("External account is not verified") } - const existingWithdrawal = await BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer( - accountId as string, - externalAccountId, - amount, - ) + const existingWithdrawal = + await BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer( + accountId as string, + externalAccountId, + amount, + ) if (existingWithdrawal instanceof Error) return existingWithdrawal // Store withdrawal record, or reuse the in-flight row for a retry of the same request. @@ -467,20 +482,24 @@ const initiateWithdrawal = async ( const idempotencyKey = deriveWithdrawalIdempotencyKey(pendingWithdrawal.id) // Create transfer via Bridge - const transfer = await BridgeClient.createTransfer(customerId, { - amount, - on_behalf_of: customerId, - source: { - payment_rail: "ethereum", - currency: "usdt", - from_address: ethereumAddress, - }, - destination: { - payment_rail: "ach", - currency: "usd", - external_account_id: externalAccountId, + const transfer = await BridgeApiClient.createTransfer( + customerId, + { + amount, + on_behalf_of: customerId, + source: { + payment_rail: "ethereum", + currency: "usdt", + from_address: ethereumAddress, + }, + destination: { + payment_rail: "ach", + currency: "usd", + external_account_id: externalAccountId, + }, }, - }, idempotencyKey) + idempotencyKey, + ) const result: InitiateWithdrawalResult = { transferId: transfer.id, diff --git a/src/services/bridge/webhook-server/index.ts b/src/services/bridge/webhook-server/index.ts index 22e75154c..2cf97522a 100644 --- a/src/services/bridge/webhook-server/index.ts +++ b/src/services/bridge/webhook-server/index.ts @@ -9,20 +9,27 @@ import express from "express" import { BridgeConfig } from "@config" import { baseLogger } from "@services/logger" + import { verifyBridgeSignature } from "./middleware/verify-signature" import { kycHandler } from "./routes/kyc" import { depositHandler } from "./routes/deposit" import { transferHandler } from "./routes/transfer" import { replayAuthMiddleware, replayHandler } from "./routes/replay" +type RawBodyRequest = express.Request & { rawBody?: string } + export const startBridgeWebhookServer = () => { const app = express() // Middleware - MUST capture raw body for signature verification app.use( express.json({ - verify: (req: any, res, buf) => { - req.rawBody = buf.toString("utf8") + verify: (req, res, buf) => { + if (res.writableEnded) { + return + } + const rawReq = req as RawBodyRequest + rawReq.rawBody = buf.toString("utf8") }, }), ) @@ -39,7 +46,9 @@ export const startBridgeWebhookServer = () => { app.post("/internal/replay", replayAuthMiddleware, replayHandler) if (!BridgeConfig.webhook.replaySecret && !process.env.BRIDGE_WEBHOOK_REPLAY_SECRET) { - baseLogger.warn("replaySecret not configured (neither BridgeConfig.webhook.replaySecret nor BRIDGE_WEBHOOK_REPLAY_SECRET) — /internal/replay will reject all requests with 503") + baseLogger.warn( + "replaySecret not configured (neither BridgeConfig.webhook.replaySecret nor BRIDGE_WEBHOOK_REPLAY_SECRET) — /internal/replay will reject all requests with 503", + ) } // Start server diff --git a/src/services/bridge/webhook-server/middleware/verify-signature.ts b/src/services/bridge/webhook-server/middleware/verify-signature.ts index c4a100ecb..03b75494f 100644 --- a/src/services/bridge/webhook-server/middleware/verify-signature.ts +++ b/src/services/bridge/webhook-server/middleware/verify-signature.ts @@ -6,11 +6,14 @@ * Signature is computed over: . */ -import { Request, Response, NextFunction } from "express" import crypto from "crypto" + +import { Request, Response, NextFunction } from "express" import { BridgeConfig } from "@config" import { baseLogger } from "@services/logger" +type RawBodyRequest = Request & { rawBody?: string } + export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transfer") => { return (req: Request, res: Response, next: NextFunction) => { const signature = req.headers["x-webhook-signature"] as string @@ -37,7 +40,10 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf const now = Date.now() const timestampMs = parseInt(timestamp, 10) if (isNaN(timestampMs) || !isFinite(timestampMs)) { - baseLogger.warn({ timestamp }, "Invalid timestamp format in Bridge webhook signature") + baseLogger.warn( + { timestamp }, + "Invalid timestamp format in Bridge webhook signature", + ) return res.status(401).json({ error: "Invalid signature format" }) } @@ -50,7 +56,7 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf // Verify signature using Bridge public key const publicKey = BridgeConfig.webhook.publicKeys[publicKeyType] - const rawBody = (req as any).rawBody + const rawBody = (req as RawBodyRequest).rawBody if (!rawBody) { baseLogger.warn(`Missing raw body for webhook`) @@ -60,7 +66,10 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf const signedPayload = `${timestamp}.${rawBody}` const digest = crypto.createHash("sha256").update(signedPayload).digest() - baseLogger.debug({ signedPayload, digest: digest.toString("hex") }, "Verifying Bridge webhook signature") + baseLogger.debug( + { signedPayload, digest: digest.toString("hex") }, + "Verifying Bridge webhook signature", + ) try { const verifier = crypto.createVerify("RSA-SHA256") diff --git a/src/services/bridge/webhook-server/routes/kyc.ts b/src/services/bridge/webhook-server/routes/kyc.ts index 7a8783742..2706b7499 100644 --- a/src/services/bridge/webhook-server/routes/kyc.ts +++ b/src/services/bridge/webhook-server/routes/kyc.ts @@ -11,7 +11,6 @@ import { toBridgeCustomerId } from "@domain/primitives/bridge" import BridgeService from "@services/bridge" export const kycHandler = async (req: Request, res: Response) => { - const { event_id, event_object } = req.body const { customer_id, kyc_status, rejection_reasons } = event_object @@ -21,7 +20,7 @@ export const kycHandler = async (req: Request, res: Response) => { // Idempotency check using customer_id + event as lock key const lockKey = `bridge-kyc:${customer_id}:${event_object.kyc_status}:${event_object.id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) if (lockResult instanceof Error) { baseLogger.info({ customer_id, event_id }, "Duplicate Bridge KYC webhook") return res.status(200).json({ status: "already_processed" }) @@ -31,7 +30,10 @@ export const kycHandler = async (req: Request, res: Response) => { const bridgeCustomerId = toBridgeCustomerId(customer_id) const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) if (account instanceof Error) { - baseLogger.warn({ customer_id }, "Account not found for Bridge customer — may be a timing issue, Bridge will retry") + baseLogger.warn( + { customer_id }, + "Account not found for Bridge customer — may be a timing issue, Bridge will retry", + ) return res.status(503).json({ error: "Account not ready" }) } diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts index e08b8fa10..0863a074b 100644 --- a/src/services/bridge/webhook-server/routes/transfer.ts +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -19,7 +19,7 @@ export const transferHandler = async (req: Request, res: Response) => { // Idempotency check using transfer_id as lock key const lockKey = `bridge-transfer:${transfer_id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) if (lockResult instanceof Error) { baseLogger.info({ transfer_id }, "Duplicate Bridge transfer webhook") return res.status(200).json({ status: "already_processed" }) diff --git a/src/services/ibex/webhook-server/index.ts b/src/services/ibex/webhook-server/index.ts index 0bd7aa7f8..b190516c4 100644 --- a/src/services/ibex/webhook-server/index.ts +++ b/src/services/ibex/webhook-server/index.ts @@ -1,6 +1,7 @@ import express, { Request, Response } from "express" import { IbexConfig } from "@config" import { baseLogger as logger } from "@services/logger" + import { onPay, onReceive, cryptoReceive } from "./routes" const start = () => { diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index 9a8842bcf..853a7a93b 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -82,7 +82,6 @@ export const AccountsRepository = (): IAccountsRepository => { if (!result) { return new CouldNotFindAccountFromUsernameError(npub) } - let account = translateToAccount(result) return translateToAccount(result) } catch (err) { return parseRepositoryError(err) diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index 6877d092e..1828e3650 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -1,4 +1,3 @@ -import { BridgeVirtualAccount, BridgeExternalAccount, BridgeWithdrawal } from "./schema" import { BridgeVirtualAccountId, BridgeExternalAccountId, @@ -6,6 +5,8 @@ import { } from "@domain/primitives/bridge" import { RepositoryError } from "@domain/errors" +import { BridgeVirtualAccount, BridgeExternalAccount, BridgeWithdrawal } from "./schema" + // ============ Virtual Accounts ============ export const createVirtualAccount = async (data: { @@ -123,7 +124,7 @@ export const findPendingWithdrawalWithoutTransfer = async ( bridgeTransferId: { $exists: false }, status: "pending", }) - return record // null when no in-flight row exists + return record // null when no in-flight row exists } catch (error) { return new RepositoryError(String(error)) } diff --git a/src/services/mongoose/bridge-replay-log.ts b/src/services/mongoose/bridge-replay-log.ts index 5782471cf..d1842be8c 100644 --- a/src/services/mongoose/bridge-replay-log.ts +++ b/src/services/mongoose/bridge-replay-log.ts @@ -1,42 +1,42 @@ import { BridgeReplayLog } from "./schema" export const createBridgeReplayLog = async (data: { - eventId: string - eventType: string, - eventPayload: Record, - bridgeEventCreatedAt: Date, - replayedAt: Date, - operator: string, - timeWindowStart: Date, - timeWindowEnd: Date, - httpStatus: number, - httpResponse: Record, - dryRun?: boolean + eventId: string + eventType: string + eventPayload: Record + bridgeEventCreatedAt: Date + replayedAt: Date + operator: string + timeWindowStart: Date + timeWindowEnd: Date + httpStatus: number + httpResponse: Record + dryRun?: boolean }): Promise<{ id: string } | Error> => { + try { + const log = await BridgeReplayLog.create(data) - try { - const log = await BridgeReplayLog.create(data) - - return { id: log._id.toString() } - - } catch (error) { - return error instanceof Error ? error : new Error(String(error)) - } + return { id: log._id.toString() } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } } - export const findBridgeReplayLogs = async (filter?: { - eventType?: string - dryRun?: boolean - limit?: number + eventType?: string + dryRun?: boolean + limit?: number }): Promise => { - try { - const queryFilter: Record = {} - if (filter?.eventType !== undefined) queryFilter.eventType = filter.eventType - if (filter?.dryRun !== undefined) queryFilter.dryRun = filter.dryRun + try { + const queryFilter: Record = {} + if (filter?.eventType !== undefined) queryFilter.eventType = filter.eventType + if (filter?.dryRun !== undefined) queryFilter.dryRun = filter.dryRun - return await BridgeReplayLog.find(queryFilter).sort({ replayedAt: -1 }).limit(filter?.limit ?? 100).lean() - } catch (error) { - return error instanceof Error ? error : new Error(String(error)) - } + return await BridgeReplayLog.find(queryFilter) + .sort({ replayedAt: -1 }) + .limit(filter?.limit ?? 100) + .lean() + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } } diff --git a/src/services/mongoose/wallets.ts b/src/services/mongoose/wallets.ts index 300be6fbd..0a2da5d61 100644 --- a/src/services/mongoose/wallets.ts +++ b/src/services/mongoose/wallets.ts @@ -1,3 +1,5 @@ +import { randomUUID } from "crypto" + import { CouldNotFindWalletFromIdError, CouldNotFindWalletFromOnChainAddressError, @@ -9,7 +11,6 @@ import { UnsupportedCurrencyError, } from "@domain/errors" import { Types } from "mongoose" -import { randomUUID } from "crypto" // FLASH FORK: import IBEX routes and helper import Ibex from "@services/ibex/client" @@ -20,10 +21,11 @@ import { recordExceptionInCurrentSpan } from "@services/tracing" import { ErrorLevel, USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + import { toObjectId, fromObjectId, parseRepositoryError } from "./utils" import { Wallet } from "./schema" import { AccountsRepository } from "./accounts" -import { WalletType } from "@domain/wallets" export interface WalletRecord { id: string diff --git a/test/flash/unit/services/bridge/client.spec.ts b/test/flash/unit/services/bridge/client.spec.ts index a4fc194f5..ef9c4c203 100644 --- a/test/flash/unit/services/bridge/client.spec.ts +++ b/test/flash/unit/services/bridge/client.spec.ts @@ -78,11 +78,23 @@ describe("listAllEvents", () => { it("passes the cursor from page N as 'after' on page N+1", async () => { listEventsSpy - .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "cur-abc" }) - .mockResolvedValueOnce({ data: [makeEvent("e2")], has_more: false, cursor: undefined }) + .mockResolvedValueOnce({ + data: [makeEvent("e1")], + has_more: true, + cursor: "cur-abc", + }) + .mockResolvedValueOnce({ + data: [makeEvent("e2")], + has_more: false, + cursor: undefined, + }) - for await (const _ of listAllEvents()) { /* drain */ } + const drained: BridgeWebhookEvent[] = [] + for await (const event of listAllEvents()) { + drained.push(event) + } + expect(drained).toHaveLength(2) expect(listEventsSpy).toHaveBeenNthCalledWith( 1, expect.objectContaining({ after: undefined }), @@ -96,8 +108,12 @@ describe("listAllEvents", () => { it("always requests page_size 100", async () => { listEventsSpy.mockResolvedValue({ data: [], has_more: false, cursor: undefined }) - for await (const _ of listAllEvents()) { /* drain */ } + const drained: BridgeWebhookEvent[] = [] + for await (const event of listAllEvents()) { + drained.push(event) + } + expect(drained).toHaveLength(0) expect(listEventsSpy).toHaveBeenCalledWith( expect.objectContaining({ page_size: 100 }), ) @@ -106,7 +122,11 @@ describe("listAllEvents", () => { it("forwards start_date, end_date and event_type filters to every page", async () => { listEventsSpy .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "c1" }) - .mockResolvedValueOnce({ data: [makeEvent("e2")], has_more: false, cursor: undefined }) + .mockResolvedValueOnce({ + data: [makeEvent("e2")], + has_more: false, + cursor: undefined, + }) const params = { start_date: "2026-05-01T00:00:00Z", @@ -114,8 +134,12 @@ describe("listAllEvents", () => { event_type: "transfer.completed", } - for await (const _ of listAllEvents(params)) { /* drain */ } + const drained: BridgeWebhookEvent[] = [] + for await (const event of listAllEvents(params)) { + drained.push(event) + } + expect(drained).toHaveLength(2) expect(listEventsSpy).toHaveBeenCalledTimes(2) for (const call of listEventsSpy.mock.calls) { expect(call[0]).toMatchObject(params) diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts index 4324021d2..635200766 100644 --- a/test/flash/unit/services/bridge/index.spec.ts +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -67,7 +67,9 @@ jest.mock("@domain/primitives/bridge", () => ({ // `instanceof USDTAmount` guard is satisfied without breaking other domain exports. jest.mock("@domain/shared", () => { class USDTAmount { - constructor(private readonly ibexValue: number) {} + constructor(private readonly ibexValue: number) { + // Parameter property initializes ibexValue. + } toIbex() { return this.ibexValue } @@ -120,15 +122,18 @@ const mockTransfer = { // ── Helpers ─────────────────────────────────────────────────────────────────── const setupGuards = () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const { USDTAmount } = require("@domain/shared") + const { USDTAmount } = jest.requireMock("@domain/shared") as { + USDTAmount: new (ibexValue: number) => { toIbex: () => number } + } const balance = new USDTAmount(1000) // 1000 USDT — well above minWithdrawalAmount ;(AccountsRepository as jest.Mock).mockReturnValue({ findById: jest.fn().mockResolvedValue(mockAccount), }) ;(WalletsRepository as jest.Mock).mockReturnValue({ - listByAccountId: jest.fn().mockResolvedValue([{ id: "wallet-001", currency: "USDT" }]), + listByAccountId: jest + .fn() + .mockResolvedValue([{ id: "wallet-001", currency: "USDT" }]), }) ;(getBalanceForWallet as jest.Mock).mockResolvedValue(balance) ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ @@ -144,7 +149,7 @@ const setupGuards = () => { // ── Tests ───────────────────────────────────────────────────────────────────── describe("deriveWithdrawalIdempotencyKey", () => { - it("returns sha256(\"withdrawal:\") as a hex string", () => { + it('returns sha256("withdrawal:") as a hex string', () => { const rowId = "507f1f77bcf86cd799439011" const expected = crypto .createHash("sha256") @@ -162,7 +167,9 @@ describe("deriveWithdrawalIdempotencyKey", () => { it("is deterministic — same input always returns same output", () => { const rowId = "507f1f77bcf86cd799439011" - expect(deriveWithdrawalIdempotencyKey(rowId)).toBe(deriveWithdrawalIdempotencyKey(rowId)) + expect(deriveWithdrawalIdempotencyKey(rowId)).toBe( + deriveWithdrawalIdempotencyKey(rowId), + ) }) it("output is a 64-character lowercase hex string (sha256)", () => { @@ -180,25 +187,28 @@ describe("initiateWithdrawal — idempotency key wiring", () => { describe("fresh request (no in-flight row)", () => { it("creates a pending withdrawal row before calling Bridge", async () => { const row = makeRow("fresh-row-001") - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( - null, - ) + ;( + BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock + ).mockResolvedValue(null) ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) const createOrder = (BridgeAccountsRepo.createWithdrawal as jest.Mock).mock .invocationCallOrder[0] - const transferOrder = (BridgeClient.createTransfer as jest.Mock).mock.invocationCallOrder[0] + const transferOrder = (BridgeClient.createTransfer as jest.Mock).mock + .invocationCallOrder[0] expect(createOrder).toBeLessThan(transferOrder) }) it("derives the idempotency key from the newly created row's id", async () => { const rowId = "fresh-row-id-abc" - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( - null, + ;( + BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock + ).mockResolvedValue(null) + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue( + makeRow(rowId), ) - ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(makeRow(rowId)) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) @@ -214,9 +224,9 @@ describe("initiateWithdrawal — idempotency key wiring", () => { describe("retry — in-flight row already exists", () => { it("does not create a second withdrawal row", async () => { const existingRow = makeRow("existing-row-001") - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( - existingRow, - ) + ;( + BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock + ).mockResolvedValue(existingRow) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) @@ -225,9 +235,9 @@ describe("initiateWithdrawal — idempotency key wiring", () => { it("derives the key from the existing row's id — identical to the first attempt's key", async () => { const rowId = "existing-row-001" - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( - makeRow(rowId), - ) + ;( + BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock + ).mockResolvedValue(makeRow(rowId)) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) @@ -249,8 +259,7 @@ describe("initiateWithdrawal — idempotency key wiring", () => { ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock) .mockResolvedValueOnce(null) // call 1: nothing in-flight yet - .mockResolvedValueOnce(row) // call 2: row A now visible - + .mockResolvedValueOnce(row) // call 2: row A now visible ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) diff --git a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts index fe2756e3d..961b1f24c 100644 --- a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts @@ -26,8 +26,7 @@ const makeRes = () => { return res } -const makeReq = (body: Record) => - ({ body } as unknown as Request) +const makeReq = (body: Record) => ({ body }) as unknown as Request // Real Bridge deposit event shape const VALID_EVENT_OBJECT = { @@ -85,7 +84,8 @@ beforeEach(() => { describe("depositHandler — invalid payload", () => { it("returns 400 when event_id is missing", async () => { const res = makeRes() - const { event_id: _, ...body } = VALID_BODY + const body = { ...VALID_BODY } + delete body.event_id await depositHandler(makeReq(body), res) expect(res.status as jest.Mock).toHaveBeenCalledWith(400) expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() @@ -93,7 +93,8 @@ describe("depositHandler — invalid payload", () => { it("returns 400 when event_object.id is missing", async () => { const res = makeRes() - const { id: _, ...objWithoutId } = VALID_EVENT_OBJECT + const objWithoutId = { ...VALID_EVENT_OBJECT } + delete objWithoutId.id await depositHandler(makeReq({ ...VALID_BODY, event_object: objWithoutId }), res) expect(res.status as jest.Mock).toHaveBeenCalledWith(400) expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() diff --git a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts index 32d6e7302..ea6c9610b 100644 --- a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts @@ -1,5 +1,3 @@ -import crypto from "crypto" - // AC1: Orphan event surfaces in ops tooling with triage context // AC2: Replay CLI re-runs a stuck handler against a chosen transfer-id @@ -28,7 +26,10 @@ jest.mock("@services/bridge/webhook-server/routes/transfer", () => ({ })) import { Request, Response } from "express" -import { replayAuthMiddleware, replayHandler } from "@services/bridge/webhook-server/routes/replay" +import { + replayAuthMiddleware, + replayHandler, +} from "@services/bridge/webhook-server/routes/replay" import * as ReplayLog from "@services/mongoose/bridge-replay-log" import { depositHandler } from "@services/bridge/webhook-server/routes/deposit" import { kycHandler } from "@services/bridge/webhook-server/routes/kyc" @@ -43,8 +44,10 @@ const makeRes = () => { return res } -const makeReq = (body: Record = {}, headers: Record = {}) => - ({ body, headers } as unknown as Request) +const makeReq = ( + body: Record = {}, + headers: Record = {}, +) => ({ body, headers }) as unknown as Request const BASE_BODY = { event_type: "funds_received", @@ -69,7 +72,7 @@ describe("replayAuthMiddleware", () => { const next = jest.fn() replayAuthMiddleware(makeReq({}, {}), res, next) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(503) + expect(res.status as jest.Mock).toHaveBeenCalledWith(503) expect(next).not.toHaveBeenCalled() BridgeConfig.webhook.replaySecret = saved @@ -78,13 +81,9 @@ describe("replayAuthMiddleware", () => { it("returns 401 for a wrong token", () => { const res = makeRes() const next = jest.fn() - replayAuthMiddleware( - makeReq({}, { authorization: "Bearer wrong-token" }), - res, - next, - ) + replayAuthMiddleware(makeReq({}, { authorization: "Bearer wrong-token" }), res, next) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(res.status as jest.Mock).toHaveBeenCalledWith(401) expect(next).not.toHaveBeenCalled() }) @@ -93,7 +92,7 @@ describe("replayAuthMiddleware", () => { const next = jest.fn() replayAuthMiddleware(makeReq({}, {}), res, next) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(res.status as jest.Mock).toHaveBeenCalledWith(401) expect(next).not.toHaveBeenCalled() }) @@ -107,7 +106,7 @@ describe("replayAuthMiddleware", () => { ) expect(next).toHaveBeenCalledTimes(1) - expect((res.status as jest.Mock)).not.toHaveBeenCalled() + expect(res.status as jest.Mock).not.toHaveBeenCalled() }) it("uses timing-safe comparison (different-length token is rejected)", () => { @@ -120,7 +119,7 @@ describe("replayAuthMiddleware", () => { next, ) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(res.status as jest.Mock).toHaveBeenCalledWith(401) expect(next).not.toHaveBeenCalled() }) }) @@ -133,64 +132,72 @@ describe("replayHandler", () => { describe("input validation", () => { it("returns 400 when event_type is missing", async () => { const res = makeRes() - const { event_type: _et, ...body } = BASE_BODY + const body = { ...BASE_BODY } + delete body.event_type await replayHandler(makeReq(body), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) }) it("returns 400 when event_object is missing", async () => { const res = makeRes() - const { event_object: _eo, ...body } = BASE_BODY + const body = { ...BASE_BODY } + delete body.event_object await replayHandler(makeReq(body), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) }) it("returns 400 when event_created_at is missing", async () => { const res = makeRes() - const { event_created_at: _ec, ...body } = BASE_BODY + const body = { ...BASE_BODY } + delete body.event_created_at await replayHandler(makeReq(body), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) }) it("returns 400 for an unrecognised event_type", async () => { const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, event_type: "unknown_event" }), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) }) }) describe("event_type → handler routing", () => { const cases: Array<[string, jest.Mock]> = [ - ["funds_received", depositHandler as jest.Mock], + ["funds_received", depositHandler as jest.Mock], ["funds_scheduled", depositHandler as jest.Mock], ["payment_processed", depositHandler as jest.Mock], - ["kyc.approved", kycHandler as jest.Mock], - ["kyc.rejected", kycHandler as jest.Mock], + ["kyc.approved", kycHandler as jest.Mock], + ["kyc.rejected", kycHandler as jest.Mock], ["transfer.completed", transferHandler as jest.Mock], - ["transfer.failed", transferHandler as jest.Mock], + ["transfer.failed", transferHandler as jest.Mock], ] beforeEach(() => { ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-001" }) }) - test.each(cases)("%s is routed to the correct handler", async (eventType, handler) => { - handler.mockImplementation((_req: Request, res: Response) => { - ;(res.status as jest.Mock)(200) - ;(res.json as jest.Mock)({ status: "success" }) - return Promise.resolve(res) - }) + test.each(cases)( + "%s is routed to the correct handler", + async (eventType, handler) => { + handler.mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }) - const res = makeRes() - await replayHandler(makeReq({ ...BASE_BODY, event_type: eventType }), res) + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, event_type: eventType }), res) - expect(handler).toHaveBeenCalledTimes(1) - }) + expect(handler).toHaveBeenCalledTimes(1) + }, + ) }) describe("dry_run mode", () => { it("returns 200 without calling any handler", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-dry-001" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-dry-001", + }) const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) @@ -198,11 +205,13 @@ describe("replayHandler", () => { expect(depositHandler).not.toHaveBeenCalled() expect(kycHandler).not.toHaveBeenCalled() expect(transferHandler).not.toHaveBeenCalled() - expect((res.status as jest.Mock)).toHaveBeenCalledWith(200) + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) }) it("persists a dry-run log entry with httpStatus 0", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-dry-002" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-dry-002", + }) const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) @@ -213,37 +222,45 @@ describe("replayHandler", () => { }) it("returns 500 when dry-run log creation fails", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue(new Error("db error")) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue( + new Error("db error"), + ) const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(500) + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) }) }) describe("live replay", () => { beforeEach(() => { - ;(depositHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { - ;(res.status as jest.Mock)(200) - ;(res.json as jest.Mock)({ status: "success" }) - return Promise.resolve(res) - }) + ;(depositHandler as jest.Mock).mockImplementation( + (_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }, + ) }) it("returns the handler's status code and response body", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-live-001" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-live-001", + }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(200) + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) const jsonArg = (res.json as jest.Mock).mock.calls[0][0] expect(jsonArg).toMatchObject({ status: "replayed", handler_status: 200 }) }) it("persists a replay log with triage context (operator + time window)", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-live-002" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-live-002", + }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) @@ -261,7 +278,9 @@ describe("replayHandler", () => { }) it("includes log_id in the response so ops can trace the replay", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-trace-007" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-trace-007", + }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) @@ -271,26 +290,30 @@ describe("replayHandler", () => { }) it("returns 500 when log creation fails after a successful handler run", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue(new Error("mongo down")) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue( + new Error("mongo down"), + ) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(500) + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) }) it("propagates a handler 4xx back to the caller", async () => { - ;(depositHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { - ;(res.status as jest.Mock)(422) - ;(res.json as jest.Mock)({ error: "Unprocessable" }) - return Promise.resolve(res) - }) + ;(depositHandler as jest.Mock).mockImplementation( + (_req: Request, res: Response) => { + ;(res.status as jest.Mock)(422) + ;(res.json as jest.Mock)({ error: "Unprocessable" }) + return Promise.resolve(res) + }, + ) ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-4xx" }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(422) + expect(res.status as jest.Mock).toHaveBeenCalledWith(422) }) }) }) From c8569deb3951e4be2c4d0b993fb21bff79129cea Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 18:31:11 -0400 Subject: [PATCH 08/43] ENG-297 fix(graphql): add isExternal to UsdtWallet --- dev/apollo-federation/supergraph.graphql | 1 + src/graphql/public/schema.graphql | 1 + src/graphql/shared/types/object/usdt-wallet.ts | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 7f2bcb3a7..7af42dc4d 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -2041,6 +2041,7 @@ type UsdtWallet implements Wallet accountId: ID! balance: FractionalCentAmount! id: ID! + isExternal: Boolean! lnurlp: Lnurl """An unconfirmed incoming onchain balance.""" diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index a6e0a50a1..3794cf539 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -1655,6 +1655,7 @@ type UsdtWallet implements Wallet { accountId: ID! balance: FractionalCentAmount! id: ID! + isExternal: Boolean! lnurlp: Lnurl """An unconfirmed incoming onchain balance.""" diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index 59139203e..046e21bb7 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -14,6 +14,7 @@ import { WalletCurrency as WalletCurrencyDomain, USDTAmount, } from "@domain/shared" +import { WalletType } from "@domain/wallets" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" @@ -49,6 +50,10 @@ const UsdtWallet = GT.Object({ type: Lnurl, resolve: (source) => source.lnurlp, }, + isExternal: { + type: GT.NonNull(GT.Boolean), + resolve: (source) => source.type === WalletType.External, + }, balance: { type: GT.NonNull(FractionalCentAmount), From f01c0adb2cb585df4e2ce0fed84a2730302b9a96 Mon Sep 17 00:00:00 2001 From: Vandana Date: Fri, 15 May 2026 08:46:48 -0400 Subject: [PATCH 09/43] fix(graphql): preserve latest account upgrade request query --- dev/apollo-federation/supergraph.graphql | 2 +- src/graphql/public/queries.ts | 4 ++-- src/graphql/public/schema.graphql | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 7af42dc4d..8c6041023 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -1644,7 +1644,7 @@ type Query @join__type(graph: PUBLIC) { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! - accountUpgradeRequest: AccountUpgradeRequestPayload! + latestAccountUpgradeRequest: AccountUpgradeRequestPayload! bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index 4596d6e48..8f8e7f46f 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -20,7 +20,7 @@ import LnInvoicePaymentStatusQuery from "@graphql/public/root/query/ln-invoice-p import NpubByUserNameQuery from "./root/query/username-npub-query" import IsFlashNpubQuery from "./root/query/is-flash-npub-query" import TransactionDetailsQuery from "./root/query/transaction-details" -import AccountUpgradeRequestQuery from "./root/query/account-upgrade-request" +import LatestAccountUpgradeRequestQuery from "./root/query/account-upgrade-request" import SupportedBanksQuery from "./root/query/supported-banks" import BridgeKycStatusQuery from "./root/query/bridge-kyc-status" import BridgeVirtualAccountQuery from "./root/query/bridge-virtual-account" @@ -49,7 +49,7 @@ export const queryFields = { atAccountLevel: { me: MeQuery, transactionDetails: TransactionDetailsQuery, - accountUpgradeRequest: AccountUpgradeRequestQuery, + latestAccountUpgradeRequest: LatestAccountUpgradeRequestQuery, bridgeKycStatus: BridgeKycStatusQuery, bridgeVirtualAccount: BridgeVirtualAccountQuery, bridgeExternalAccounts: BridgeExternalAccountsQuery, diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index 3794cf539..b5d7f3c79 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -1288,7 +1288,7 @@ type PublicWallet { type Query { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! - accountUpgradeRequest: AccountUpgradeRequestPayload! + latestAccountUpgradeRequest: AccountUpgradeRequestPayload! bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount From 0fe52639a5baee2200f62fa90137f0e82b3462d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olaniran=20=E2=9A=A1?= <93789719+heyolaniran@users.noreply.github.com> Date: Sat, 16 May 2026 18:30:48 +0100 Subject: [PATCH 10/43] feat: bridge reconciliation + USDT provisioning additions (#354) Ready to merge. Verified locally: - `yarn check:sdl` passes - targeted bridge/IBEX unit tests pass - `git diff --check` passes **squash & merge** used to keep `tmp/bridge-rebase-pr-ready` clean. --- dev/apollo-federation/supergraph.graphql | 2 +- .../Flash GraphQL API/environments/local.bru | 4 +- src/app/authentication/login.ts | 5 +- src/domain/pubsub/index.ts | 1 + src/graphql/admin/queries.ts | 2 + .../query/bridge-reconciliation-orphans.ts | 40 ++ src/graphql/admin/schema.graphql | 60 +++ src/graphql/admin/types/index.ts | 3 +- .../object/bridge-reconciliation-orphan.ts | 21 + .../root/mutation/onchain-payment-send-all.ts | 7 +- src/graphql/public/schema.graphql | 2 +- src/graphql/shared/types/object/btc-wallet.ts | 5 +- src/scripts/reconcile-bridge-ibex-deposits.ts | 6 +- src/servers/cron.ts | 6 +- src/services/bridge/index.ts | 71 +++- src/services/bridge/reconciliation.ts | 147 ++++++- .../bridge/webhook-server/routes/deposit.ts | 7 + src/services/ibex/client.ts | 55 ++- src/services/ibex/types.ts | 16 +- .../webhook-server/routes/crypto-receive.ts | 27 +- src/services/mongoose/bridge-accounts.ts | 13 +- .../mongoose/bridge-reconciliation-orphan.ts | 76 +++- src/services/mongoose/schema.ts | 21 + src/services/mongoose/wallets.ts | 5 +- test/flash/unit/services/bridge/index.spec.ts | 214 +++++++++- .../services/bridge/reconciliation.spec.ts | 378 ++++++++++++++++++ .../routes/crypto-receive.spec.ts | 117 ++++++ 27 files changed, 1251 insertions(+), 60 deletions(-) create mode 100644 src/graphql/admin/root/query/bridge-reconciliation-orphans.ts create mode 100644 src/graphql/admin/types/object/bridge-reconciliation-orphan.ts create mode 100644 test/flash/unit/services/bridge/reconciliation.spec.ts create mode 100644 test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 8c6041023..bb5202f04 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -1644,7 +1644,6 @@ type Query @join__type(graph: PUBLIC) { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! - latestAccountUpgradeRequest: AccountUpgradeRequestPayload! bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount @@ -1655,6 +1654,7 @@ type Query currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload + latestAccountUpgradeRequest: AccountUpgradeRequestPayload! lnInvoicePaymentStatus(input: LnInvoicePaymentStatusInput!): LnInvoicePaymentStatusPayload! me: User mobileVersions: [MobileVersions] diff --git a/dev/bruno/Flash GraphQL API/environments/local.bru b/dev/bruno/Flash GraphQL API/environments/local.bru index 2c8d6c6bd..1cda70a56 100644 --- a/dev/bruno/Flash GraphQL API/environments/local.bru +++ b/dev/bruno/Flash GraphQL API/environments/local.bru @@ -8,6 +8,6 @@ vars { token: walletId: walletIdUsd: c593736e-5a58-42e4-93fa-dc895856c1f1 - userEmail: maurienteso@gmail.com - userFullName: maurienteso + userEmail: mauriente@gmail.com + userFullName: maurientes } diff --git a/src/app/authentication/login.ts b/src/app/authentication/login.ts index 736eb90ed..c87b3705c 100644 --- a/src/app/authentication/login.ts +++ b/src/app/authentication/login.ts @@ -309,7 +309,10 @@ export const loginDeviceUpgradeWithPhone = async ({ if (deviceWallets instanceof Error) return deviceWallets let deviceAccountHasBalance = false for (const wallet of deviceWallets) { - const balance = await getBalanceForWallet({ walletId: wallet.id }) + const balance = await getBalanceForWallet({ + walletId: wallet.id, + currency: wallet.currency, + }) if (balance instanceof Error) return balance if (!balance.isZero()) { deviceAccountHasBalance = true diff --git a/src/domain/pubsub/index.ts b/src/domain/pubsub/index.ts index a64a52b7b..d27047df1 100644 --- a/src/domain/pubsub/index.ts +++ b/src/domain/pubsub/index.ts @@ -5,6 +5,7 @@ export const PubSubDefaultTriggers = { UserPriceUpdate: "USER_PRICE_UPDATE", AccountUpdate: "ACCOUNT_UPDATE", LnPaymentStatus: "LN_PAYMENT_STATUS", + BridgeReconciliationUpdate: "BRIDGE_RECONCILIATION_UPDATE", } as const export const customPubSubTrigger = ({ diff --git a/src/graphql/admin/queries.ts b/src/graphql/admin/queries.ts index f18b8836a..a069657df 100644 --- a/src/graphql/admin/queries.ts +++ b/src/graphql/admin/queries.ts @@ -15,6 +15,7 @@ import AccountDetailsByAccountId from "./root/query/account-details-by-account-i import MerchantsPendingApprovalQuery from "./root/query/merchants-pending-approval-listing" import IdDocumentReadUrlQuery from "./root/query/id-document-read-url" import NotificationTopicsQuery from "./root/query/notification-topics" +import BridgeReconciliationOrphansQuery from "./root/query/bridge-reconciliation-orphans" export const queryFields = { unauthed: {}, @@ -34,6 +35,7 @@ export const queryFields = { merchantsPendingApproval: MerchantsPendingApprovalQuery, idDocumentReadUrl: IdDocumentReadUrlQuery, notificationTopics: NotificationTopicsQuery, + bridgeReconciliationOrphans: BridgeReconciliationOrphansQuery, }, } diff --git a/src/graphql/admin/root/query/bridge-reconciliation-orphans.ts b/src/graphql/admin/root/query/bridge-reconciliation-orphans.ts new file mode 100644 index 000000000..a50bded31 --- /dev/null +++ b/src/graphql/admin/root/query/bridge-reconciliation-orphans.ts @@ -0,0 +1,40 @@ +import { GT } from "@graphql/index" +import BridgeReconciliationOrphanObject from "@graphql/admin/types/object/bridge-reconciliation-orphan" +import { findOrphans } from "@services/mongoose/bridge-reconciliation-orphan" + +const BridgeReconciliationOrphansQuery = GT.Field({ + type: GT.NonNullList(BridgeReconciliationOrphanObject), + args: { + status: { type: GT.String, defaultValue: null }, + orphanType: { type: GT.String, defaultValue: null }, + limit: { type: GT.Int, defaultValue: 50 }, + }, + resolve: async ( + _: unknown, + { + status, + orphanType, + limit, + }: { status?: string; orphanType?: string; limit?: number }, + ) => { + const result = await findOrphans({ + status: status as "unmatched" | "resolved" | undefined, + orphanType: orphanType as + | "bridge_without_ibex" + | "ibex_without_bridge" + | undefined, + limit: limit ?? 50, + }) + + if (result instanceof Error) throw result + + return result.map((o) => ({ + ...o, + detectedAt: o.detectedAt.toISOString(), + resolvedAt: o.resolvedAt?.toISOString() ?? null, + triageContext: JSON.stringify(o.triageContext), + })) + }, +}) + +export default BridgeReconciliationOrphansQuery diff --git a/src/graphql/admin/schema.graphql b/src/graphql/admin/schema.graphql index d858eaf79..1eb75c263 100644 --- a/src/graphql/admin/schema.graphql +++ b/src/graphql/admin/schema.graphql @@ -110,6 +110,21 @@ type BTCWallet implements Wallet { walletCurrency: WalletCurrency! } +type BridgeReconciliationOrphan { + amount: String + currency: String + customerId: String + detectedAt: String! + id: ID! + orphanKey: String! + orphanType: String! + resolvedAt: String + status: String! + transferId: String + triageContext: String! + txHash: String +} + input BusinessDeleteMapInfoInput { username: Username! } @@ -319,6 +334,7 @@ type Query { accountDetailsByUserPhone(phone: Phone!): AuditedAccount! accountDetailsByUsername(username: Username!): AuditedAccount! allLevels: [AccountLevel!]! + bridgeReconciliationOrphans(limit: Int = 50, orphanType: String = null, status: String = null): [BridgeReconciliationOrphan!]! idDocumentReadUrl( """Storage key of the ID document file""" fileKey: String! @@ -551,6 +567,50 @@ type UsdWallet implements Wallet { walletCurrency: WalletCurrency! } +""" +A wallet belonging to an account which contains a USDT balance and a list of transactions. +""" +type UsdtWallet implements Wallet { + accountId: ID! + balance: FractionalCentAmount! + id: ID! + isExternal: Boolean! + lnurlp: Lnurl + + """An unconfirmed incoming onchain balance.""" + pendingIncomingBalance: SignedAmount! + transactions( + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + transactionsByAddress( + """Returns the items that include this address.""" + address: OnChainAddress! + + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + walletCurrency: WalletCurrency! +} + input UserUpdatePhoneInput { accountUuid: ID! phone: Phone! diff --git a/src/graphql/admin/types/index.ts b/src/graphql/admin/types/index.ts index 566bf6c92..95ebebcf9 100644 --- a/src/graphql/admin/types/index.ts +++ b/src/graphql/admin/types/index.ts @@ -1,5 +1,6 @@ import BtcWallet from "@graphql/shared/types/object/btc-wallet" import GraphQLApplicationError from "@graphql/shared/types/object/graphql-application-error" import UsdWallet from "@graphql/shared/types/object/usd-wallet" +import UsdtWallet from "@graphql/shared/types/object/usdt-wallet" -export const ALL_INTERFACE_TYPES = [GraphQLApplicationError, BtcWallet, UsdWallet] +export const ALL_INTERFACE_TYPES = [GraphQLApplicationError, BtcWallet, UsdWallet, UsdtWallet] diff --git a/src/graphql/admin/types/object/bridge-reconciliation-orphan.ts b/src/graphql/admin/types/object/bridge-reconciliation-orphan.ts new file mode 100644 index 000000000..7ce3bc480 --- /dev/null +++ b/src/graphql/admin/types/object/bridge-reconciliation-orphan.ts @@ -0,0 +1,21 @@ +import { GT } from "@graphql/index" + +const BridgeReconciliationOrphanObject = GT.Object({ + name: "BridgeReconciliationOrphan", + fields: () => ({ + id: { type: GT.NonNullID }, + orphanKey: { type: GT.NonNull(GT.String) }, + orphanType: { type: GT.NonNull(GT.String) }, + status: { type: GT.NonNull(GT.String) }, + txHash: { type: GT.String }, + transferId: { type: GT.String }, + customerId: { type: GT.String }, + amount: { type: GT.String }, + currency: { type: GT.String }, + detectedAt: { type: GT.NonNull(GT.String) }, + resolvedAt: { type: GT.String }, + triageContext: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeReconciliationOrphanObject diff --git a/src/graphql/public/root/mutation/onchain-payment-send-all.ts b/src/graphql/public/root/mutation/onchain-payment-send-all.ts index 2f90ff8c9..c40b2c50a 100644 --- a/src/graphql/public/root/mutation/onchain-payment-send-all.ts +++ b/src/graphql/public/root/mutation/onchain-payment-send-all.ts @@ -10,7 +10,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import { Wallets } from "@app" import { getBalanceForWallet } from "@app/wallets" -import { USDAmount } from "@domain/shared" +import { USDAmount, WalletCurrency } from "@domain/shared" const OnChainPaymentSendAllInput = GT.Input({ name: "OnChainPaymentSendAllInput", @@ -63,7 +63,10 @@ const OnChainPaymentSendAllMutation = GT.Field< return { errors: [{ message: speed.message }] } } - const amount = await getBalanceForWallet({ walletId }) + const amount = await getBalanceForWallet({ + walletId, + currency: WalletCurrency.Usd, + }) if (amount instanceof Error) return amount if (!(amount instanceof USDAmount)) { return { errors: [{ message: "Onchain payments require a USD wallet" }] } diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index b5d7f3c79..0ed531044 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -1288,7 +1288,6 @@ type PublicWallet { type Query { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! - latestAccountUpgradeRequest: AccountUpgradeRequestPayload! bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount @@ -1299,6 +1298,7 @@ type Query { currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload + latestAccountUpgradeRequest: AccountUpgradeRequestPayload! lnInvoicePaymentStatus(input: LnInvoicePaymentStatusInput!): LnInvoicePaymentStatusPayload! me: User mobileVersions: [MobileVersions] diff --git a/src/graphql/shared/types/object/btc-wallet.ts b/src/graphql/shared/types/object/btc-wallet.ts index 3a29738e2..9991092a0 100644 --- a/src/graphql/shared/types/object/btc-wallet.ts +++ b/src/graphql/shared/types/object/btc-wallet.ts @@ -52,7 +52,10 @@ const BtcWallet = GT.Object({ description: "A balance stored in BTC.", resolve: async (source) => { if (source.type === WalletType.External) return null - const balanceSats = await Wallets.getBalanceForWallet({ walletId: source.id }) + const balanceSats = await Wallets.getBalanceForWallet({ + walletId: source.id, + currency: source.currency, + }) if (balanceSats instanceof Error) { throw mapError(balanceSats) } diff --git a/src/scripts/reconcile-bridge-ibex-deposits.ts b/src/scripts/reconcile-bridge-ibex-deposits.ts index 7e643bc0f..747ac99ca 100644 --- a/src/scripts/reconcile-bridge-ibex-deposits.ts +++ b/src/scripts/reconcile-bridge-ibex-deposits.ts @@ -9,14 +9,14 @@ import { reconcileBridgeAndIbexDeposits } from "@services/bridge/reconciliation" const args = yargs(hideBin(process.argv)) .option("window-hours", { type: "number", - default: 24, - describe: "Reconciliation window in hours", + default: 0.25, + describe: "Reconciliation window in hours (default: 15 minutes)", }) .option("configPath", { type: "string", demandOption: true }) .parseSync() const main = async () => { - const windowMs = Math.max(1, Math.floor(args["window-hours"])) * 60 * 60 * 1000 + const windowMs = args["window-hours"] * 60 * 60 * 1000 const result = await reconcileBridgeAndIbexDeposits({ windowMs }) if (result instanceof Error) throw result baseLogger.info(result, "Bridge↔IBEX reconciliation finished") diff --git a/src/servers/cron.ts b/src/servers/cron.ts index 19511890e..469b59062 100644 --- a/src/servers/cron.ts +++ b/src/servers/cron.ts @@ -65,10 +65,14 @@ const swapOutJob = async () => { if (swapResult instanceof Error) throw swapResult } +// Window covers 15 min of events — real-time webhook reconciliation handles everything +// else immediately. This batch pass is only a safety net for missed/delayed webhooks. +const RECONCILE_WINDOW_MS = 15 * 60 * 1000 + const reconcileBridgeDepositsJob = async () => { if (!BridgeConfig.enabled) return - const result = await reconcileBridgeAndIbexDeposits() + const result = await reconcileBridgeAndIbexDeposits({ windowMs: RECONCILE_WINDOW_MS }) if (result instanceof Error) throw result } diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index ecd4891c2..f5f51e2da 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -17,6 +17,7 @@ import { RepositoryError } from "@domain/errors" import { toBridgeCustomerId } from "@domain/primitives/bridge" import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" import { USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" import { WalletsRepository } from "@services/mongoose/wallets" import { IdentityRepository } from "@services/kratos" @@ -91,6 +92,38 @@ type ExternalAccountResult = { export const deriveWithdrawalIdempotencyKey = (rowId: string): string => crypto.createHash("sha256").update(`withdrawal:${rowId}`).digest("hex") +const ensureEthUsdtCashWallet = async ( + account: Account, +): Promise => { + const wallets = await WalletsRepository().listByAccountId(account.id) + if (wallets instanceof Error) return wallets + + let usdtWallet = wallets.find( + (wallet) => + wallet.currency === WalletCurrency.Usdt && wallet.type === WalletType.Checking, + ) + + if (!usdtWallet) { + const createdWallet = await WalletsRepository().persistNew({ + accountId: account.id, + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }) + if (createdWallet instanceof Error) return createdWallet + usdtWallet = createdWallet + } + + if (account.defaultWalletId !== usdtWallet.id) { + const updatedAccount = await AccountsRepository().update({ + ...account, + defaultWalletId: usdtWallet.id, + }) + if (updatedAccount instanceof Error) return updatedAccount + } + + return usdtWallet +} + // ============ Guards ============ const checkBridgeEnabled = (): true | BridgeDisabledError => { @@ -180,6 +213,16 @@ const initiateKyc = async ({ return result } catch (error) { + const bridgeError = error as { statusCode?: number; response?: { existing_kyc_link?: { kyc_link: string; customer_id: string; tos_link: string } } } + + if (bridgeError?.statusCode === 400 && bridgeError.response?.existing_kyc_link) { + return { + kycLink: bridgeError.response.existing_kyc_link.kyc_link, + customerId: bridgeError.response.existing_kyc_link.customer_id, + tosLink: bridgeError.response.existing_kyc_link.tos_link, + } + } + baseLogger.error( { accountId, operation: "initiateKyc", error }, "Bridge operation failed", @@ -191,8 +234,9 @@ const initiateKyc = async ({ /** * Creates a virtual account for receiving USD deposits * - Requires approved KYC - * - Creates IBEX Tron USDT receive address - * - Creates Bridge virtual account pointing to Tron address + * - Ensures an IBEX ETH-USDT Cash Wallet exists and is the account default + * - Creates IBEX Ethereum USDT receive address + * - Creates Bridge virtual account pointing to Ethereum address */ const createVirtualAccount = async ( accountId: AccountId, @@ -227,7 +271,7 @@ const createVirtualAccount = async ( return new BridgeKycPendingError("KYC not yet completed") } - // Idempotency guard: return the existing VA immediately without touching Bridge + // Idempotency guard first: do not mutate wallets/default when a VA already exists const existingVa = await BridgeAccountsRepo.findVirtualAccountByAccountId( accountId as string, ) @@ -241,26 +285,29 @@ const createVirtualAccount = async ( } } - // Get or create Ethereum address - let ethereumAddress = - account.bridgeEthereumAddress || "0xaF095D35bfDd462165eA7eCF8AC75351a93d72bD" + const usdtCashWallet = await ensureEthUsdtCashWallet(account) + if (usdtCashWallet instanceof Error) return usdtCashWallet + + // Get or create Ethereum USDT receive address for the ETH-USDT Cash Wallet + let ethereumAddress = account.bridgeEthereumAddress if (!ethereumAddress) { - const option = await IbexClient.getEthereumUsdtOption() + let option = await IbexClient.getEthereumUsdtOption() if (option instanceof Error) return new BridgeError(option.message) + option.name = `USDT-ETH ${account.username}-${crypto.randomBytes(4).toString("hex")}` const receiveInfo = await IbexClient.createCryptoReceiveInfo( - account.defaultWalletId as IbexAccountId, + usdtCashWallet.id as IbexAccountId, option, ) if (receiveInfo instanceof Error) return new BridgeError(receiveInfo.message) const updateResult = await AccountsRepository().updateBridgeFields(accountId, { - bridgeEthereumAddress: receiveInfo.address, + bridgeEthereumAddress: receiveInfo.data.address, }) if (updateResult instanceof Error) return updateResult - ethereumAddress = receiveInfo.address + ethereumAddress = receiveInfo.data.address } // Deterministic key so Bridge deduplicates on their side if two calls race past @@ -407,7 +454,9 @@ const initiateWithdrawal = async ( const wallets = await WalletsRepository().listByAccountId(accountId) if (wallets instanceof Error) return wallets - const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) + const usdtWallet = wallets.find( + (w) => w.currency === WalletCurrency.Usdt && w.type === WalletType.Checking, + ) if (!usdtWallet) { return new BridgeInsufficientFundsError("No USDT wallet found on account") } diff --git a/src/services/bridge/reconciliation.ts b/src/services/bridge/reconciliation.ts index ec964ba00..891229e10 100644 --- a/src/services/bridge/reconciliation.ts +++ b/src/services/bridge/reconciliation.ts @@ -1,9 +1,14 @@ import { baseLogger } from "@services/logger" import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" -import { upsertBridgeReconciliationOrphan } from "@services/mongoose/bridge-reconciliation-orphan" -import { BridgeDepositLog } from "@services/mongoose/schema" +import { + upsertBridgeReconciliationOrphan, + resolveOrphansByTxHash, +} from "@services/mongoose/bridge-reconciliation-orphan" +import { BridgeDepositLog, IbexCryptoReceiveLog } from "@services/mongoose/schema" +import { PubSubService } from "@services/pubsub" +import { PubSubDefaultTriggers } from "@domain/pubsub" -const ONE_DAY_MS = 24 * 60 * 60 * 1000 +const FIFTEEN_MIN_MS = 15 * 60 * 1000 type BridgeDepositLike = { eventId: string @@ -29,7 +34,7 @@ type IbexReceiveLike = { const toOrphanKey = (prefix: string, value: string) => `${prefix}:${value.toLowerCase()}` export const reconcileBridgeAndIbexDeposits = async ({ - windowMs = ONE_DAY_MS, + windowMs = FIFTEEN_MIN_MS, }: { windowMs?: number } = {}): Promise< @@ -47,7 +52,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ const bridgeDeposits = (await BridgeDepositLog.find({ createdAt: { $gte: since, $lte: now }, - state: "funds_received", + state: "payment_processed", }) .lean() .exec()) as BridgeDepositLike[] @@ -82,7 +87,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ amount: deposit.amount, currency: deposit.currency, triageContext: { - reason: "Bridge funds_received has no destinationTxHash", + reason: "Bridge payment_processed has no destinationTxHash", windowStart: since.toISOString(), windowEnd: now.toISOString(), depositState: deposit.state, @@ -107,7 +112,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ currency: deposit.currency, triageContext: { reason: - "No IBEX crypto.receive found for Bridge destinationTxHash within 24h window", + "No IBEX crypto.receive found for Bridge destinationTxHash within window", windowStart: since.toISOString(), windowEnd: now.toISOString(), depositState: deposit.state, @@ -129,7 +134,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ currency: receive.currency, triageContext: { reason: - "No Bridge deposit funds_received found for IBEX tx hash within 24h window", + "No Bridge deposit payment_processed found for IBEX tx hash within window", windowStart: since.toISOString(), windowEnd: now.toISOString(), address: receive.address, @@ -153,3 +158,129 @@ export const reconcileBridgeAndIbexDeposits = async ({ return error instanceof Error ? error : new Error(String(error)) } } + +type ReconcileByTxHashResult = { + txHash: string + status: "matched" | "unmatched" + orphanType?: "bridge_without_ibex" | "ibex_without_bridge" + transferId?: string + customerId?: string + amount?: string + currency?: string + detectedAt: Date +} + +export const reconcileByTxHash = async ({ + txHash, +}: { + txHash: string +}): Promise => { + const normalizedHash = txHash.toLowerCase() + const now = new Date() + + try { + const [bridgeDeposit, ibexReceive] = await Promise.all([ + BridgeDepositLog.findOne({ + destinationTxHash: { $regex: new RegExp(`^${normalizedHash}$`, "i") }, + state: "payment_processed", + }) + .lean() + .exec(), + IbexCryptoReceiveLog.findOne({ + txHash: { $regex: new RegExp(`^${normalizedHash}$`, "i") }, + }) + .lean() + .exec(), + ]) + + const pubsub = PubSubService() + + if (bridgeDeposit && ibexReceive) { + await resolveOrphansByTxHash(normalizedHash) + + const event: ReconcileByTxHashResult = { + txHash: normalizedHash, + status: "matched", + transferId: (bridgeDeposit as BridgeDepositLike).transferId, + customerId: (bridgeDeposit as BridgeDepositLike).customerId, + amount: (bridgeDeposit as BridgeDepositLike).amount, + currency: (bridgeDeposit as BridgeDepositLike).currency, + detectedAt: now, + } + + baseLogger.info(event, "Bridge↔IBEX real-time reconciliation: matched") + pubsub.publish({ + trigger: PubSubDefaultTriggers.BridgeReconciliationUpdate, + payload: event, + }) + return event + } + + let orphanType: "bridge_without_ibex" | "ibex_without_bridge" + let orphanKey: string + let triageContext: Record + let transferId: string | undefined + let customerId: string | undefined + let amount: string | undefined + let currency: string | undefined + + if (bridgeDeposit && !ibexReceive) { + orphanType = "bridge_without_ibex" + orphanKey = toOrphanKey("bridge", normalizedHash) + transferId = (bridgeDeposit as BridgeDepositLike).transferId + customerId = (bridgeDeposit as BridgeDepositLike).customerId + amount = (bridgeDeposit as BridgeDepositLike).amount + currency = (bridgeDeposit as BridgeDepositLike).currency + triageContext = { + reason: "Bridge payment_processed has no matching IBEX crypto.receive yet", + txHash: normalizedHash, + depositState: (bridgeDeposit as BridgeDepositLike).state, + createdAt: (bridgeDeposit as BridgeDepositLike).createdAt.toISOString(), + detectedAt: now.toISOString(), + } + } else { + orphanType = "ibex_without_bridge" + orphanKey = toOrphanKey("ibex", normalizedHash) + amount = ibexReceive ? (ibexReceive as IbexReceiveLike).amount : undefined + currency = ibexReceive ? (ibexReceive as IbexReceiveLike).currency : undefined + triageContext = { + reason: "IBEX crypto.receive has no matching Bridge funds_received yet", + txHash: normalizedHash, + address: ibexReceive ? (ibexReceive as IbexReceiveLike).address : undefined, + network: ibexReceive ? (ibexReceive as IbexReceiveLike).network : undefined, + detectedAt: now.toISOString(), + } + } + + await upsertBridgeReconciliationOrphan({ + orphanKey, + orphanType, + txHash: normalizedHash, + transferId, + customerId, + amount, + currency, + triageContext, + }) + + const event: ReconcileByTxHashResult = { + txHash: normalizedHash, + status: "unmatched", + orphanType, + transferId, + customerId, + amount, + currency, + detectedAt: now, + } + + baseLogger.info(event, "Bridge↔IBEX real-time reconciliation: unmatched") + pubsub.publish({ + trigger: PubSubDefaultTriggers.BridgeReconciliationUpdate, + payload: event, + }) + return event + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index e2217c7b7..a87352b1a 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -10,6 +10,7 @@ import { Request, Response } from "express" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" import { createBridgeDepositLog } from "@services/mongoose/bridge-deposit-log" +import { reconcileByTxHash } from "@services/bridge/reconciliation" export const depositHandler = async (req: Request, res: Response) => { const { event_id, event_object } = req.body @@ -77,6 +78,12 @@ export const depositHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to persist deposit log" }) } + if (state === "payment_processed" && receipt?.destination_tx_hash) { + reconcileByTxHash({ txHash: receipt.destination_tx_hash }).catch((err) => + baseLogger.error({ err, event_id, id }, "Real-time reconciliation failed"), + ) + } + return res.status(200).json({ status: "success" }) } catch (error) { baseLogger.error({ error, id, event_id }, "Error processing Bridge deposit webhook") diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index aae1a775a..b5badcb03 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -39,6 +39,7 @@ import { CryptoReceiveOption, CryptoReceiveInfo, CreateCryptoReceiveInfoRequest, + IbexCurrency, } from "./types" import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" @@ -234,10 +235,10 @@ const payToLnurl = async ( const getIbexToken = async (): Promise => { const cached = await Ibex.authentication.storage.getAccessToken() - if (typeof cached === "string") return `Bearer ${cached}` + if (typeof cached === "string") return `${cached}` // The SDK uses a single base URL for all calls, but the sandbox auth domain is separate - const resp = await fetch(`${IbexConfig.authUrl}/auth/signin`, { + const resp = await fetch(`${IbexConfig.url}/auth/signin`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: IbexConfig.email, password: IbexConfig.password }), @@ -271,7 +272,7 @@ const getIbexToken = async (): Promise => { ) } - return `Bearer ${data.accessToken}` + return data.accessToken as string } const ibexFetch = async ( @@ -302,6 +303,24 @@ const ibexGet = (token: string, path: string) => const ibexPost = (token: string, path: string, body: unknown) => ibexFetch(token, path, { method: "POST", body: JSON.stringify(body) }) + +const createIbexAccount = async ( + name: string, + currencyId: IbexCurrencyId, +): Promise => { + try { + const token = await getIbexToken() + if (token instanceof IbexError) return token + const data = await ibexPost( + token, + "/account/create" + , { name, currencyId }) + if (data instanceof IbexError) return data + return data + } catch (err) { + return new IbexError(err instanceof Error ? err : new Error(String(err))) + } +} const getCryptoReceiveBalance = async ( receiveInfoId: string, ): Promise => { @@ -325,12 +344,13 @@ const getCryptoReceiveOptions = async (): Promise( + const data = await ibexGet( token, "/crypto/receive-infos/options", ) + if (data instanceof IbexError) return data - return data.options || [] + return data } catch (err) { return new IbexError(err instanceof Error ? err : new Error(String(err))) } @@ -349,36 +369,39 @@ const createCryptoReceiveInfo = async ( { name: option.name, network: option.network } as CreateCryptoReceiveInfoRequest, ) if (data instanceof IbexError) return data - if (!data.address) return new UnexpectedIbexResponse("Address not found") + if (!data.data.address) return new UnexpectedIbexResponse("Address not found") return data } catch (err) { return new IbexError(err instanceof Error ? err : new Error(String(err))) } } -const getTronUsdtOption = async (): Promise => { +const getTronUsdtOption = async (): Promise => { const options = await getCryptoReceiveOptions() if (options instanceof IbexError) return options const tronUsdt = options.find( (opt) => - opt.currency.toLowerCase() === "usdt" && opt.network.toLowerCase() === "tron", + opt.currencyId === USDTAmount.currencyId && opt.network.toLowerCase() === "tron", ) if (!tronUsdt) { return new IbexError(new Error("Tron USDT option not found")) } - return tronUsdt.id + return tronUsdt } const getEthereumUsdtOption = async (): Promise => { const options = await getCryptoReceiveOptions() if (options instanceof IbexError) return options + const UsdtCurrencyId = await getIbexCurrencyId(WalletCurrency.Usdt) + if (UsdtCurrencyId instanceof IbexError) return UsdtCurrencyId as IbexError + const ethereumUsdt = options.find( (opt) => - opt.currency.toLowerCase() === "usdt" && opt.network.toLowerCase() === "ethereum", + opt.currencyId === UsdtCurrencyId && opt.network.toLowerCase() === "ethereum", ) if (!ethereumUsdt) { @@ -388,6 +411,16 @@ const getEthereumUsdtOption = async (): Promise return ethereumUsdt } +const getIbexCurrencyId = async ( + currency: WalletCurrency, +): Promise => { + const data = await ibexGet<{ currencies: IbexCurrency[] }>("", "/currency/all") + if (data instanceof IbexError) return data + const currencyId = data.currencies.find((c) => c.name === currency)?.id + if (!currencyId) return new IbexError(new Error(`Currency ${currency} not found`)) + return currencyId +} + // const sendBetweenAccounts = async ( // sender: IbexAccount, // receiver: IbexAccount, @@ -425,10 +458,12 @@ export default wrapAsyncFunctionsToRunInSpan({ createLnurlPay, decodeLnurl, payToLnurl, + createIbexAccount, getCryptoReceiveBalance, getCryptoReceiveOptions, createCryptoReceiveInfo, getTronUsdtOption, getEthereumUsdtOption, + getIbexCurrencyId, }, }) diff --git a/src/services/ibex/types.ts b/src/services/ibex/types.ts index ec3d55015..16764d137 100644 --- a/src/services/ibex/types.ts +++ b/src/services/ibex/types.ts @@ -38,17 +38,27 @@ export type IbexInvoiceArgs = { } export interface CryptoReceiveOption { - id: string - currency: string + id?: string + currencyId: number network: string + name?: string +} + +export interface IbexCurrency { + id: IbexCurrencyId name: string + isFiat: boolean + symbol: string + accountEnabled: boolean } export interface CryptoReceiveInfo { id: string wallet_id: string option_id: string - address: string + data: { + address: string + } currency: string network: string created_at: string diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index e104a52df..83282f9a5 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -5,6 +5,7 @@ import { listWalletsByAccountId } from "@app/wallets" import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" import { LockService } from "@services/lock" +import { reconcileByTxHash } from "@services/bridge/reconciliation" import { authenticate, logRequest } from "../middleware" @@ -21,8 +22,16 @@ interface CryptoReceiveResult { const cryptoReceiveHandler = async (req: Request, res: Response) => { const { tx_hash, address, amount, currency, network } = req.body - - if (!tx_hash || !address || !amount || currency !== "USDT" || network !== "tron") { + const normalizedCurrency = String(currency || "").toUpperCase() + const normalizedNetwork = String(network || "").toLowerCase() + + if ( + !tx_hash || + !address || + !amount || + normalizedCurrency !== "USDT" || + normalizedNetwork !== "ethereum" + ) { baseLogger.warn( { tx_hash, address, amount, currency, network }, "Invalid crypto receive payload", @@ -30,8 +39,8 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return res.status(400).json({ error: "Invalid payload" }) } - const lockResult = await LockService().lockPaymentHash( - tx_hash as PaymentHash, + const lockResult = await LockService().lockOnChainTxHash( + tx_hash as OnChainTxHash, async () => { try { const account = await AccountsRepository().findByBridgeEthereumAddress(address) @@ -44,8 +53,8 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { txHash: String(tx_hash), address: String(address), amount: String(amount), - currency: String(currency), - network: String(network), + currency: normalizedCurrency, + network: normalizedNetwork, accountId: account.id, }) if (ibexLog instanceof Error) { @@ -56,6 +65,10 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return { status: "error", code: "internal_error" } as CryptoReceiveResult } + reconcileByTxHash({ txHash: String(tx_hash) }).catch((err) => + baseLogger.error({ err, tx_hash }, "Real-time reconciliation failed"), + ) + const wallets = await listWalletsByAccountId(account.id) if (wallets instanceof Error) { baseLogger.error( @@ -122,4 +135,4 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { router.post(paths.cryptoReceive, authenticate, logRequest, cryptoReceiveHandler) -export { paths, router } +export { cryptoReceiveHandler, paths, router } diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index 1828e3650..234304c2c 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -106,7 +106,18 @@ export const createWithdrawal = async (data: { try { const record = await BridgeWithdrawal.create(data) return record - } catch (error) { + } catch (error: unknown) { + const mongoErr = error as { code?: number } + if (mongoErr.code === 11000) { + const record = await BridgeWithdrawal.findOne({ + accountId: data.accountId, + externalAccountId: data.externalAccountId, + amount: data.amount, + currency: data.currency, + status: "pending", + }) + if (record) return record + } return new RepositoryError(String(error)) } } diff --git a/src/services/mongoose/bridge-reconciliation-orphan.ts b/src/services/mongoose/bridge-reconciliation-orphan.ts index 0150333d2..3e3b6807f 100644 --- a/src/services/mongoose/bridge-reconciliation-orphan.ts +++ b/src/services/mongoose/bridge-reconciliation-orphan.ts @@ -1,6 +1,7 @@ import { BridgeReconciliationOrphan } from "./schema" type OrphanType = "bridge_without_ibex" | "ibex_without_bridge" +type OrphanStatus = "unmatched" | "resolved" export const upsertBridgeReconciliationOrphan = async (data: { orphanKey: string @@ -16,7 +17,7 @@ export const upsertBridgeReconciliationOrphan = async (data: { try { const orphan = await BridgeReconciliationOrphan.findOneAndUpdate( { orphanKey: data.orphanKey }, - { ...data, detectedAt: new Date() }, + { ...data, status: "unmatched", detectedAt: new Date() }, { upsert: true, new: true, setDefaultsOnInsert: true }, ) @@ -25,3 +26,76 @@ export const upsertBridgeReconciliationOrphan = async (data: { return error instanceof Error ? error : new Error(String(error)) } } + +export const resolveOrphansByTxHash = async ( + txHash: string, +): Promise<{ resolvedCount: number } | Error> => { + try { + const now = new Date() + const result = await BridgeReconciliationOrphan.updateMany( + { + txHash: txHash.toLowerCase(), + status: "unmatched", + }, + { $set: { status: "resolved", resolvedAt: now } }, + ) + return { resolvedCount: result.modifiedCount } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} + +export const findOrphans = async ({ + status, + orphanType, + limit = 50, +}: { + status?: OrphanStatus + orphanType?: OrphanType + limit?: number +} = {}): Promise< + | { + id: string + orphanKey: string + orphanType: OrphanType + status: OrphanStatus + transferId?: string + txHash?: string + customerId?: string + amount?: string + currency?: string + triageContext: Record + detectedAt: Date + resolvedAt?: Date + }[] + | Error +> => { + try { + const filter: Record = {} + if (status) filter.status = status + if (orphanType) filter.orphanType = orphanType + + const docs = await BridgeReconciliationOrphan.find(filter) + .sort({ detectedAt: -1 }) + .limit(limit) + .lean() + .exec() + + return docs.map((d) => ({ + id: (d._id as { toString(): string }).toString(), + orphanKey: d.orphanKey as string, + orphanType: d.orphanType as OrphanType, + status: (d.status ?? "unmatched") as OrphanStatus, + transferId: d.transferId as string | undefined, + txHash: d.txHash as string | undefined, + customerId: d.customerId as string | undefined, + amount: d.amount as string | undefined, + currency: d.currency as string | undefined, + triageContext: d.triageContext as Record, + detectedAt: d.detectedAt as Date, + resolvedAt: d.resolvedAt as Date | undefined, + })) + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index 2feb91d87..15a1fd2f5 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -658,6 +658,18 @@ const BridgeWithdrawalSchema = new Schema({ updatedAt: { type: Date, default: Date.now }, }) +// At most one pending row per (account, destination, amount, currency). Partial filter must +// not use $exists:false — MongoDB rejects it for partial indexes ("$not ... $exists"). +// "pending" alone is enough: completed/failed rows are excluded so the same tuple can repeat +// after a terminal status. +BridgeWithdrawalSchema.index( + { accountId: 1, externalAccountId: 1, amount: 1, currency: 1 }, + { + unique: true, + partialFilterExpression: { status: "pending" }, + }, +) + const BridgeDepositLogSchema = new Schema({ eventId: { type: String, required: true, unique: true }, transferId: { type: String, required: true }, @@ -703,6 +715,12 @@ const BridgeReconciliationOrphanSchema = new Schema({ enum: ["bridge_without_ibex", "ibex_without_bridge"], required: true, }, + status: { + type: String, + enum: ["unmatched", "resolved"], + default: "unmatched", + required: true, + }, transferId: { type: String }, txHash: { type: String }, bridgeEventId: { type: String }, @@ -711,10 +729,13 @@ const BridgeReconciliationOrphanSchema = new Schema({ currency: { type: String }, triageContext: { type: Schema.Types.Mixed, required: true }, detectedAt: { type: Date, default: Date.now }, + resolvedAt: { type: Date }, }) BridgeReconciliationOrphanSchema.index({ orphanType: 1, detectedAt: -1 }) BridgeReconciliationOrphanSchema.index({ detectedAt: -1 }) +BridgeReconciliationOrphanSchema.index({ status: 1, detectedAt: -1 }) +BridgeReconciliationOrphanSchema.index({ txHash: 1 }) export const BridgeReconciliationOrphan = mongoose.model( "BridgeReconciliationOrphan", diff --git a/src/services/mongoose/wallets.ts b/src/services/mongoose/wallets.ts index 0a2da5d61..80da0dcce 100644 --- a/src/services/mongoose/wallets.ts +++ b/src/services/mongoose/wallets.ts @@ -4,7 +4,6 @@ import { CouldNotFindWalletFromIdError, CouldNotFindWalletFromOnChainAddressError, CouldNotFindWalletFromOnChainAddressesError, - CouldNotListWalletsFromAccountIdError, CouldNotListWalletsFromWalletCurrencyError, InvalidLnurlError, RepositoryError, @@ -23,6 +22,7 @@ import { ErrorLevel, USDAmount, USDTAmount, WalletCurrency } from "@domain/share import { WalletType } from "@domain/wallets" + import { toObjectId, fromObjectId, parseRepositoryError } from "./utils" import { Wallet } from "./schema" import { AccountsRepository } from "./accounts" @@ -66,6 +66,7 @@ export const WalletsRepository = (): IWalletsRepository => { if (resp instanceof IbexError) return resp const ibexAccountId = resp.id + let lnurlp: string | undefined if (ibexAccountId !== undefined) { const lnurlResp = await Ibex.createLnurlPay({ @@ -118,7 +119,7 @@ export const WalletsRepository = (): IWalletsRepository => { _accountId: toObjectId(accountId), }) if (!result || result.length === 0) { - return new CouldNotListWalletsFromAccountIdError(`accountId: ${accountId}}`) + return [] } return result.map(resultToWallet) } catch (err) { diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts index 635200766..67d6b50c1 100644 --- a/test/flash/unit/services/bridge/index.spec.ts +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -18,6 +18,8 @@ jest.mock("@services/logger", () => ({ })) jest.mock("@services/mongoose/bridge-accounts", () => ({ + createVirtualAccount: jest.fn(), + findVirtualAccountByAccountId: jest.fn(), createWithdrawal: jest.fn(), findPendingWithdrawalWithoutTransfer: jest.fn(), findExternalAccountsByAccountId: jest.fn(), @@ -26,7 +28,15 @@ jest.mock("@services/mongoose/bridge-accounts", () => ({ jest.mock("@services/bridge/client", () => ({ __esModule: true, - default: { createTransfer: jest.fn() }, + default: { createVirtualAccount: jest.fn(), createTransfer: jest.fn() }, +})) + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + getEthereumUsdtOption: jest.fn(), + createCryptoReceiveInfo: jest.fn(), + }, })) jest.mock("@services/ibex/client", () => ({ @@ -83,6 +93,8 @@ import BridgeClient from "@services/bridge/client" import { AccountsRepository } from "@services/mongoose/accounts" import { WalletsRepository } from "@services/mongoose/wallets" import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import IbexClient from "@services/ibex/client" +import { RepositoryError } from "@domain/errors" // ── Fixtures ────────────────────────────────────────────────────────────────── @@ -92,6 +104,9 @@ const AMOUNT = "50" const CUSTOMER_ID = "cust-001" const ETHEREUM_ADDRESS = "ETH_ADDR_001" const TRANSFER_ID = "transfer-bridge-001" +const USDT_WALLET_ID = "ibex-eth-usdt-wallet-001" +const RECEIVE_INFO_ID = "receive-info-001" +const VIRTUAL_ACCOUNT_ID = "virtual-account-001" const mockAccount = { id: ACCOUNT_ID, @@ -119,6 +134,22 @@ const mockTransfer = { state: "pending", } +const mockVirtualAccount = { + id: VIRTUAL_ACCOUNT_ID, + source_deposit_instructions: { + bank_name: "Test Bank", + bank_routing_number: "123456789", + bank_account_number: "123456789012", + }, +} + +const makeWallet = (id: string, currency: string) => ({ + id, + accountId: ACCOUNT_ID, + type: "checking", + currency, +}) + // ── Helpers ─────────────────────────────────────────────────────────────────── const setupGuards = () => { @@ -131,9 +162,9 @@ const setupGuards = () => { findById: jest.fn().mockResolvedValue(mockAccount), }) ;(WalletsRepository as jest.Mock).mockReturnValue({ - listByAccountId: jest - .fn() - .mockResolvedValue([{ id: "wallet-001", currency: "USDT" }]), + listByAccountId: jest.fn().mockResolvedValue([ + { id: "wallet-001", currency: "USDT", type: "checking" }, + ]), }) ;(getBalanceForWallet as jest.Mock).mockResolvedValue(balance) ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ @@ -178,6 +209,181 @@ describe("deriveWithdrawalIdempotencyKey", () => { }) }) +/** + * Linear ENG-296 — ETH-USDT Cash Wallet + Bridge virtual account + * @see https://linear.app/island-bitcoin/issue/ENG-296 + * + * Acceptance ↔ tests (staging steps: `dev/qa/ENG-296-staging-checklist.md`): + * - AC1 IBEX ETH-USDT as Cash Wallet: first `it` — USDT checking persisted/reused, Ibex + * `createCryptoReceiveInfo` uses USDT wallet id; account `bridgeEthereumAddress` updated. + * - AC2 USD not primary: first `it` — `AccountsRepository.update` sets `defaultWalletId` to USDT wallet. + * - AC3 createVirtualAccount E2E: first & second `it` — Bridge + repo; third `it` — idempotent VA return. + */ +describe("createVirtualAccount — ETH-USDT Cash Wallet provisioning (ENG-296)", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("ENG-296 AC1+AC2+AC3: provisions USDT cash wallet, flips default off USD, persists Ibex ETH receive address, creates Bridge VA", async () => { + const usdtWallet = makeWallet(USDT_WALLET_ID, "USDT") + const accountWithoutUsdt = { + ...mockAccount, + defaultWalletId: "legacy-usd-wallet-id", + bridgeEthereumAddress: undefined, + } + + const accountsRepo = { + findById: jest.fn().mockResolvedValue(accountWithoutUsdt), + update: jest.fn().mockResolvedValue({ + ...accountWithoutUsdt, + defaultWalletId: USDT_WALLET_ID, + }), + updateBridgeFields: jest.fn().mockResolvedValue({ + ...accountWithoutUsdt, + defaultWalletId: USDT_WALLET_ID, + bridgeEthereumAddress: ETHEREUM_ADDRESS, + }), + } + ;(AccountsRepository as jest.Mock).mockReturnValue(accountsRepo) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest.fn().mockResolvedValue([makeWallet("legacy-usd-wallet-id", "USD")]), + persistNew: jest.fn().mockResolvedValue(usdtWallet), + }) + ;(BridgeAccountsRepo.findVirtualAccountByAccountId as jest.Mock).mockResolvedValue( + new RepositoryError("not found"), + ) + ;(IbexClient.getEthereumUsdtOption as jest.Mock).mockResolvedValue({ + id: "eth-usdt-option", + currency: "USDT", + network: "ethereum", + name: "Ethereum USDT", + }) + ;(IbexClient.createCryptoReceiveInfo as jest.Mock).mockResolvedValue({ + id: RECEIVE_INFO_ID, + wallet_id: USDT_WALLET_ID, + option_id: "eth-usdt-option", + data: { address: ETHEREUM_ADDRESS }, + currency: "USDT", + network: "ethereum", + created_at: "2026-05-09T00:00:00Z", + }) + ;(BridgeClient.createVirtualAccount as jest.Mock).mockResolvedValue(mockVirtualAccount) + ;(BridgeAccountsRepo.createVirtualAccount as jest.Mock).mockResolvedValue({ + bridgeVirtualAccountId: VIRTUAL_ACCOUNT_ID, + }) + + await BridgeService.createVirtualAccount(ACCOUNT_ID) + + expect(WalletsRepository().persistNew).toHaveBeenCalledWith({ + accountId: ACCOUNT_ID, + type: "checking", + currency: "USDT", + }) + expect(AccountsRepository().update).toHaveBeenCalledWith( + expect.objectContaining({ defaultWalletId: USDT_WALLET_ID }), + ) + expect(IbexClient.createCryptoReceiveInfo).toHaveBeenCalledWith( + USDT_WALLET_ID, + expect.objectContaining({ network: "ethereum", currency: "USDT" }), + ) + expect(accountsRepo.updateBridgeFields).toHaveBeenCalledWith( + ACCOUNT_ID, + expect.objectContaining({ bridgeEthereumAddress: ETHEREUM_ADDRESS }), + ) + expect(BridgeClient.createVirtualAccount).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.objectContaining({ + destination: expect.objectContaining({ + currency: "usdt", + payment_rail: "ethereum", + address: ETHEREUM_ADDRESS, + }), + }), + expect.any(String), + ) + }) + + it("ENG-296 AC1+AC3: reuses existing USDT cash wallet and stored Ethereum address (no extra Ibex receive-info call)", async () => { + const usdtWallet = makeWallet(USDT_WALLET_ID, "USDT") + const accountWithUsdtDefault = { + ...mockAccount, + defaultWalletId: USDT_WALLET_ID, + bridgeEthereumAddress: ETHEREUM_ADDRESS, + } + + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(accountWithUsdtDefault), + update: jest.fn(), + updateBridgeFields: jest.fn(), + }) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest.fn().mockResolvedValue([usdtWallet]), + persistNew: jest.fn(), + }) + ;(BridgeAccountsRepo.findVirtualAccountByAccountId as jest.Mock).mockResolvedValue( + new RepositoryError("not found"), + ) + ;(BridgeClient.createVirtualAccount as jest.Mock).mockResolvedValue(mockVirtualAccount) + ;(BridgeAccountsRepo.createVirtualAccount as jest.Mock).mockResolvedValue({ + bridgeVirtualAccountId: VIRTUAL_ACCOUNT_ID, + }) + + await BridgeService.createVirtualAccount(ACCOUNT_ID) + + expect(WalletsRepository().persistNew).not.toHaveBeenCalled() + expect(AccountsRepository().update).not.toHaveBeenCalled() + expect(IbexClient.createCryptoReceiveInfo).not.toHaveBeenCalled() + expect(BridgeClient.createVirtualAccount).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.objectContaining({ + destination: expect.objectContaining({ address: ETHEREUM_ADDRESS }), + }), + expect.any(String), + ) + }) + + it("ENG-296 AC3 (idempotent): existing VA returns stored bank details without wallet or Ibex side effects", async () => { + const existingVaRecord = { + bridgeVirtualAccountId: VIRTUAL_ACCOUNT_ID, + bankName: "Existing Bank", + routingNumber: "021000021", + accountNumber: "000111222", + accountNumberLast4: "0222", + } + + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + update: jest.fn(), + updateBridgeFields: jest.fn(), + }) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest.fn(), + persistNew: jest.fn(), + }) + ;(BridgeAccountsRepo.findVirtualAccountByAccountId as jest.Mock).mockResolvedValue( + existingVaRecord, + ) + + const result = await BridgeService.createVirtualAccount(ACCOUNT_ID) + + expect(result).toEqual( + expect.objectContaining({ + virtualAccountId: VIRTUAL_ACCOUNT_ID, + bankName: "Existing Bank", + routingNumber: "021000021", + accountNumber: "000111222", + accountNumberLast4: "0222", + }), + ) + expect(WalletsRepository().listByAccountId).not.toHaveBeenCalled() + expect(WalletsRepository().persistNew).not.toHaveBeenCalled() + expect(AccountsRepository().update).not.toHaveBeenCalled() + expect(IbexClient.getEthereumUsdtOption).not.toHaveBeenCalled() + expect(IbexClient.createCryptoReceiveInfo).not.toHaveBeenCalled() + expect(BridgeClient.createVirtualAccount).not.toHaveBeenCalled() + }) +}) + describe("initiateWithdrawal — idempotency key wiring", () => { beforeEach(() => { jest.clearAllMocks() diff --git a/test/flash/unit/services/bridge/reconciliation.spec.ts b/test/flash/unit/services/bridge/reconciliation.spec.ts new file mode 100644 index 000000000..2c4c3a6d6 --- /dev/null +++ b/test/flash/unit/services/bridge/reconciliation.spec.ts @@ -0,0 +1,378 @@ +/** + * Unit tests for Bridge↔IBEX reconciliation + * Covers reconcileByTxHash (real-time) and reconcileBridgeAndIbexDeposits (batch) + */ + +// ── Mocks (must be before imports) ─────────────────────────────────────────── + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/schema", () => ({ + BridgeDepositLog: { findOne: jest.fn(), find: jest.fn() }, + IbexCryptoReceiveLog: { findOne: jest.fn() }, +})) + +jest.mock("@services/mongoose/ibex-crypto-receive-log", () => ({ + findIbexCryptoReceiveLogsSince: jest.fn(), +})) + +jest.mock("@services/mongoose/bridge-reconciliation-orphan", () => ({ + upsertBridgeReconciliationOrphan: jest.fn(), + resolveOrphansByTxHash: jest.fn(), +})) + +jest.mock("@services/pubsub", () => ({ + PubSubService: jest.fn(), +})) + +jest.mock("@domain/pubsub", () => ({ + PubSubDefaultTriggers: { + BridgeReconciliationUpdate: "BRIDGE_RECONCILIATION_UPDATE", + }, +})) + +import { BridgeDepositLog, IbexCryptoReceiveLog } from "@services/mongoose/schema" +import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" +import { + upsertBridgeReconciliationOrphan, + resolveOrphansByTxHash, +} from "@services/mongoose/bridge-reconciliation-orphan" +import { PubSubService } from "@services/pubsub" +import { + reconcileByTxHash, + reconcileBridgeAndIbexDeposits, +} from "@services/bridge/reconciliation" + +// ── Fixtures ────────────────────────────────────────────────────────────────── + +const TX_HASH = "0xABC123def456" +const NORM_HASH = TX_HASH.toLowerCase() + +const BRIDGE_DEPOSIT = { + eventId: "evt_001", + transferId: "tr_001", + customerId: "cust_001", + amount: "100", + currency: "usdt", + destinationTxHash: NORM_HASH, + state: "payment_processed", + createdAt: new Date("2026-01-01T12:00:00Z"), +} + +const IBEX_RECEIVE = { + txHash: NORM_HASH, + address: "0xdeadbeef", + amount: "100", + currency: "USDT", + network: "tron", + accountId: "acc_001", + receivedAt: new Date("2026-01-01T12:00:02Z"), +} + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const mockPublish = jest.fn() + +const makeLeanQuery = (result: unknown) => ({ + lean: () => ({ exec: () => Promise.resolve(result) }), +}) + +beforeEach(() => { + jest.clearAllMocks() + ;(PubSubService as jest.Mock).mockReturnValue({ publish: mockPublish }) + ;(resolveOrphansByTxHash as jest.Mock).mockResolvedValue({ resolvedCount: 0 }) + ;(upsertBridgeReconciliationOrphan as jest.Mock).mockResolvedValue({ id: "orphan_001" }) +}) + +// ── reconcileByTxHash ───────────────────────────────────────────────────────── + +describe("reconcileByTxHash", () => { + describe("both sides found → matched", () => { + beforeEach(() => { + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + }) + + it("returns status matched", async () => { + const result = await reconcileByTxHash({ txHash: TX_HASH }) + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.status).toBe("matched") + expect(result.txHash).toBe(NORM_HASH) + }) + + it("calls resolveOrphansByTxHash with normalized hash", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(resolveOrphansByTxHash).toHaveBeenCalledWith(NORM_HASH) + }) + + it("does NOT call upsertBridgeReconciliationOrphan", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(upsertBridgeReconciliationOrphan).not.toHaveBeenCalled() + }) + + it("publishes a matched event to PubSub", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(mockPublish).toHaveBeenCalledWith( + expect.objectContaining({ + trigger: "BRIDGE_RECONCILIATION_UPDATE", + payload: expect.objectContaining({ + status: "matched", + txHash: NORM_HASH, + transferId: BRIDGE_DEPOSIT.transferId, + customerId: BRIDGE_DEPOSIT.customerId, + amount: BRIDGE_DEPOSIT.amount, + }), + }), + ) + }) + + it("normalizes txHash to lowercase before querying and returning", async () => { + const result = await reconcileByTxHash({ txHash: "0XABC123DEF456" }) + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.txHash).toBe(NORM_HASH) + const [bridgeCall] = (BridgeDepositLog.findOne as jest.Mock).mock.calls + expect(bridgeCall[0].destinationTxHash.$regex.flags).toContain("i") + }) + }) + + describe("only Bridge found → bridge_without_ibex", () => { + beforeEach(() => { + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + }) + + it("returns status unmatched with correct orphanType", async () => { + const result = await reconcileByTxHash({ txHash: TX_HASH }) + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.status).toBe("unmatched") + expect(result.orphanType).toBe("bridge_without_ibex") + }) + + it("does NOT call resolveOrphansByTxHash", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(resolveOrphansByTxHash).not.toHaveBeenCalled() + }) + + it("upserts orphan with key bridge:{hash}", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `bridge:${NORM_HASH}`, + orphanType: "bridge_without_ibex", + txHash: NORM_HASH, + transferId: BRIDGE_DEPOSIT.transferId, + customerId: BRIDGE_DEPOSIT.customerId, + }), + ) + }) + + it("publishes an unmatched event to PubSub", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(mockPublish).toHaveBeenCalledWith( + expect.objectContaining({ + payload: expect.objectContaining({ + status: "unmatched", + orphanType: "bridge_without_ibex", + }), + }), + ) + }) + }) + + describe("only IBEX found → ibex_without_bridge", () => { + beforeEach(() => { + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + }) + + it("returns status unmatched with correct orphanType", async () => { + const result = await reconcileByTxHash({ txHash: TX_HASH }) + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.status).toBe("unmatched") + expect(result.orphanType).toBe("ibex_without_bridge") + }) + + it("upserts orphan with key ibex:{hash}", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `ibex:${NORM_HASH}`, + orphanType: "ibex_without_bridge", + txHash: NORM_HASH, + }), + ) + }) + }) + + describe("self-healing: second call with both sides resolves orphan", () => { + it("resolves orphan when called again after missing side arrives", async () => { + // First call: only Bridge + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + await reconcileByTxHash({ txHash: TX_HASH }) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledTimes(1) + + jest.clearAllMocks() + ;(PubSubService as jest.Mock).mockReturnValue({ publish: mockPublish }) + ;(resolveOrphansByTxHash as jest.Mock).mockResolvedValue({ resolvedCount: 1 }) + + // Second call: both sides present (IBEX webhook arrived) + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + const result = await reconcileByTxHash({ txHash: TX_HASH }) + + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.status).toBe("matched") + expect(resolveOrphansByTxHash).toHaveBeenCalledWith(NORM_HASH) + expect(upsertBridgeReconciliationOrphan).not.toHaveBeenCalled() + }) + }) + + describe("Bridge query uses payment_processed state filter", () => { + it("passes state: payment_processed to BridgeDepositLog.findOne", async () => { + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + await reconcileByTxHash({ txHash: TX_HASH }) + expect(BridgeDepositLog.findOne).toHaveBeenCalledWith( + expect.objectContaining({ state: "payment_processed" }), + ) + }) + }) +}) + +// ── reconcileBridgeAndIbexDeposits (batch) ──────────────────────────────────── + +describe("reconcileBridgeAndIbexDeposits", () => { + const makeBridgeFind = (deposits: unknown[]) => ({ + lean: () => ({ exec: () => Promise.resolve(deposits) }), + }) + + describe("all deposits matched", () => { + it("returns zero orphans when every Bridge deposit has a matching IBEX receive", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([BRIDGE_DEPOSIT])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.scannedBridge).toBe(1) + expect(result.scannedIbex).toBe(1) + expect(result.bridgeWithoutIbex).toBe(0) + expect(result.ibexWithoutBridge).toBe(0) + expect(upsertBridgeReconciliationOrphan).not.toHaveBeenCalled() + }) + }) + + describe("Bridge deposit with no matching IBEX receive", () => { + it("flags as bridge_without_ibex orphan", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([BRIDGE_DEPOSIT])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.bridgeWithoutIbex).toBe(1) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `bridge:${NORM_HASH}`, + orphanType: "bridge_without_ibex", + txHash: NORM_HASH, + transferId: BRIDGE_DEPOSIT.transferId, + }), + ) + }) + }) + + describe("Bridge deposit with no destinationTxHash", () => { + it("flags as bridge-no-tx:{transferId} orphan", async () => { + const depositNoHash = { ...BRIDGE_DEPOSIT, destinationTxHash: undefined } + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([depositNoHash])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.bridgeWithoutIbex).toBe(1) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `bridge-no-tx:${BRIDGE_DEPOSIT.transferId}`, + orphanType: "bridge_without_ibex", + }), + ) + }) + }) + + describe("IBEX receive with no matching Bridge deposit", () => { + it("flags as ibex_without_bridge orphan", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.ibexWithoutBridge).toBe(1) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `ibex:${NORM_HASH}`, + orphanType: "ibex_without_bridge", + txHash: IBEX_RECEIVE.txHash, + }), + ) + }) + }) + + describe("batch uses payment_processed state filter", () => { + it("passes state: payment_processed to BridgeDepositLog.find", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + + await reconcileBridgeAndIbexDeposits() + expect(BridgeDepositLog.find).toHaveBeenCalledWith( + expect.objectContaining({ state: "payment_processed" }), + ) + }) + }) + + describe("mixed scenario", () => { + it("counts matched and unmatched independently", async () => { + const deposit2 = { ...BRIDGE_DEPOSIT, transferId: "tr_002", destinationTxHash: "0xother" } + const ibex2 = { ...IBEX_RECEIVE, txHash: "0xorphan_ibex" } + + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue( + makeBridgeFind([BRIDGE_DEPOSIT, deposit2]), + ) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE, ibex2]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + // BRIDGE_DEPOSIT ↔ IBEX_RECEIVE match (same hash) + // deposit2 has no ibex → bridge_without_ibex + // ibex2 has no bridge → ibex_without_bridge + expect(result.scannedBridge).toBe(2) + expect(result.scannedIbex).toBe(2) + expect(result.bridgeWithoutIbex).toBe(1) + expect(result.ibexWithoutBridge).toBe(1) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledTimes(2) + }) + }) + + describe("error handling", () => { + it("returns an Error when findIbexCryptoReceiveLogsSince fails", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue( + new Error("mongo connection lost"), + ) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).toBeInstanceOf(Error) + }) + }) +}) diff --git a/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts b/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts new file mode 100644 index 000000000..b3249c702 --- /dev/null +++ b/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts @@ -0,0 +1,117 @@ +jest.mock("@services/ibex/webhook-server/middleware", () => ({ + authenticate: jest.fn((_req, _res, next) => next()), + logRequest: jest.fn((_req, _res, next) => next()), +})) + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/ibex-crypto-receive-log", () => ({ + createIbexCryptoReceiveLog: jest.fn(), +})) + +jest.mock("@app/wallets", () => ({ + listWalletsByAccountId: jest.fn(), +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/lock", () => ({ + LockService: jest.fn(), +})) + +jest.mock("@services/bridge/reconciliation", () => ({ + reconcileByTxHash: jest.fn().mockResolvedValue({ status: "matched" }), +})) + +import { cryptoReceiveHandler } from "@services/ibex/webhook-server/routes/crypto-receive" +import { AccountsRepository } from "@services/mongoose/accounts" +import { createIbexCryptoReceiveLog } from "@services/mongoose/ibex-crypto-receive-log" +import { listWalletsByAccountId } from "@app/wallets" +import { LockService } from "@services/lock" +import { WalletCurrency } from "@domain/shared" + +const ACCOUNT_ID = "account-001" as AccountId +const WALLET_ID = "wallet-usdt-001" as WalletId +const ADDRESS = "0xabc123" +const TX_HASH = "tx-001" + +const makeResponse = () => { + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + } + return res +} + +describe("cryptoReceiveHandler", () => { + beforeEach(() => { + jest.clearAllMocks() + ;(LockService as jest.Mock).mockReturnValue({ + lockOnChainTxHash: jest.fn((_hash, fn) => fn()), + }) + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findByBridgeEthereumAddress: jest.fn().mockResolvedValue({ id: ACCOUNT_ID }), + }) + ;(createIbexCryptoReceiveLog as jest.Mock).mockResolvedValue({ id: "log-001" }) + ;(listWalletsByAccountId as jest.Mock).mockResolvedValue([ + { id: WALLET_ID, currency: WalletCurrency.Usdt }, + ]) + }) + + it("accepts Ethereum USDT receive webhooks and normalizes persisted currency/network", async () => { + const res = makeResponse() + + await cryptoReceiveHandler( + { + body: { + tx_hash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "usdt", + network: "Ethereum", + }, + } as never, + res as never, + ) + + expect(AccountsRepository().findByBridgeEthereumAddress).toHaveBeenCalledWith(ADDRESS) + expect(createIbexCryptoReceiveLog).toHaveBeenCalledWith( + expect.objectContaining({ + txHash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "USDT", + network: "ethereum", + accountId: ACCOUNT_ID, + }), + ) + expect(res.status).toHaveBeenCalledWith(200) + expect(res.json).toHaveBeenCalledWith({ status: "success" }) + }) + + it("rejects legacy Tron USDT receive webhooks for the ETH-USDT Cash Wallet path", async () => { + const res = makeResponse() + + await cryptoReceiveHandler( + { + body: { + tx_hash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "USDT", + network: "tron", + }, + } as never, + res as never, + ) + + expect(LockService().lockOnChainTxHash).not.toHaveBeenCalled() + expect(createIbexCryptoReceiveLog).not.toHaveBeenCalled() + expect(res.status).toHaveBeenCalledWith(400) + expect(res.json).toHaveBeenCalledWith({ error: "Invalid payload" }) + }) +}) From bab26d8cd458653a6e7c4e21d92dd12a7c1e63d4 Mon Sep 17 00:00:00 2001 From: Island Bitcoin <34528298+islandbitcoin@users.noreply.github.com> Date: Tue, 19 May 2026 09:32:20 -0400 Subject: [PATCH 11/43] ENG-297 - IBEX USD -> USDT Parity (#355) * ENG-297 fix(wallets): support USDT cash wallet parity * fix(wallets): support USDT intraledger sends --- dev/bin/eng-297-graphql-smoke.ts | 489 ++++++++++++++++++ src/app/payments/send-intraledger.ts | 14 +- src/app/payments/send-lightning.ts | 7 +- src/app/wallets/add-invoice-for-wallet.ts | 14 +- .../wallets/get-transactions-for-wallet.ts | 33 +- src/app/wallets/index.ts | 1 + src/app/wallets/send-on-chain.ts | 9 +- src/app/wallets/usd-wallet-amount.ts | 31 ++ src/app/wallets/validate.ts | 21 +- src/domain/shared/amount.ts | 13 +- src/domain/wallets/payment-input-validator.ts | 32 +- .../ln-noamount-usd-invoice-fee-probe.ts | 22 +- .../ln-noamount-usd-invoice-payment-send.ts | 17 +- .../root/mutation/ln-usd-invoice-fee-probe.ts | 22 +- .../root/mutation/onchain-usd-payment-send.ts | 14 +- .../root/query/on-chain-usd-tx-fee-query.ts | 13 +- src/services/ibex/client.ts | 47 +- src/services/ibex/types.ts | 19 +- .../app/payments/send-intraledger.spec.ts | 262 ++++++++++ .../get-transactions-for-wallet.spec.ts | 38 ++ .../app/wallets/usd-wallet-amount.spec.ts | 27 + test/flash/unit/domain/payments/index.spec.ts | 9 + .../onchain-usd-wallet-validator.spec.ts | 30 ++ .../services/ibex/client-usd-wallet.spec.ts | 135 +++++ 24 files changed, 1230 insertions(+), 89 deletions(-) create mode 100644 dev/bin/eng-297-graphql-smoke.ts create mode 100644 src/app/wallets/usd-wallet-amount.ts create mode 100644 test/flash/unit/app/payments/send-intraledger.spec.ts create mode 100644 test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts create mode 100644 test/flash/unit/app/wallets/usd-wallet-amount.spec.ts create mode 100644 test/flash/unit/domain/wallets/onchain-usd-wallet-validator.spec.ts create mode 100644 test/flash/unit/services/ibex/client-usd-wallet.spec.ts diff --git a/dev/bin/eng-297-graphql-smoke.ts b/dev/bin/eng-297-graphql-smoke.ts new file mode 100644 index 000000000..026893d15 --- /dev/null +++ b/dev/bin/eng-297-graphql-smoke.ts @@ -0,0 +1,489 @@ +import axios from "axios" + +type Args = { + url: string + token: string + first: number + amount: number + paymentRequest?: string + address?: string + strictFixtures: boolean +} + +type GraphQLResponse = { + data?: T + errors?: { message: string; path?: string[] }[] +} + +type Wallet = { + __typename: string + id: string + walletCurrency: string + balance?: number | null + transactions?: { + edges?: { + node?: { + id: string + settlementCurrency: string + settlementAmount: number + settlementDisplayAmount: string + settlementDisplayCurrency: string + settlementDisplayFee: string + settlementFee: number + settlementPrice: { + base: number + offset: number + } + initiationVia: { __typename: string } + settlementVia: { __typename: string } + } + }[] + } | null +} + +type CheckResult = { + name: string + status: "PASS" | "FAIL" | "SKIP" + details?: string +} + +const usage = ` +Usage: + yarn ts-node --transpile-only -r tsconfig-paths/register dev/bin/eng-297-graphql-smoke.ts \\ + --url http://localhost:4002/graphql \\ + --token "$AUTH_TOKEN" \\ + [--payment-request lnbc...] \\ + [--address bc1...] \\ + [--amount 100] + +Required: + --url GraphQL endpoint + --token Bearer token for a test account + +Optional fixtures: + --payment-request Bolt11 invoice for lnUsdInvoiceFeeProbe checks + --address On-chain address for onChainUsdTxFee checks + --amount Fractional cent amount for no-amount/on-chain checks. Default: 100 + --first Transaction page size per wallet. Default: 10 + --strict-fixtures Fail instead of skip checks that require optional fixtures +` + +const parseArgs = (argv: string[]): Args => { + const parsed: Record = {} + + for (let i = 0; i < argv.length; i++) { + const arg = argv[i] + if (arg === "--help" || arg === "-h") { + console.log(usage.trim()) + process.exit(0) + } + if (!arg.startsWith("--")) continue + + const key = arg.slice(2) + if (key === "strict-fixtures") { + parsed[key] = true + continue + } + + const value = argv[i + 1] + if (!value || value.startsWith("--")) { + throw new Error(`Missing value for --${key}`) + } + parsed[key] = value + i++ + } + + const url = (parsed.url || process.env.GRAPHQL_URL) as string | undefined + const token = (parsed.token || process.env.AUTH_TOKEN || process.env.TOKEN) as + | string + | undefined + + if (!url) throw new Error("Missing --url or GRAPHQL_URL") + if (!token) throw new Error("Missing --token, AUTH_TOKEN, or TOKEN") + + return { + url, + token, + first: Number(parsed.first || 10), + amount: Number(parsed.amount || 100), + paymentRequest: (parsed["payment-request"] || parsed.paymentRequest) as + | string + | undefined, + address: parsed.address as string | undefined, + strictFixtures: Boolean(parsed["strict-fixtures"]), + } +} + +const results: CheckResult[] = [] + +const pass = (name: string, details?: string) => { + results.push({ name, status: "PASS", details }) +} + +const fail = (name: string, details?: string) => { + results.push({ name, status: "FAIL", details }) +} + +const skip = (name: string, details?: string) => { + results.push({ name, status: "SKIP", details }) +} + +function assert(condition: unknown, message: string): asserts condition { + if (!condition) throw new Error(message) +} + +const graphql = async ( + args: Args, + operationName: string, + query: string, + variables?: Record, +): Promise => { + const resp = await axios.post>( + args.url, + { operationName, query, variables }, + { + headers: { + Authorization: `Bearer ${args.token}`, + "Content-Type": "application/json", + }, + validateStatus: () => true, + }, + ) + + if (resp.status < 200 || resp.status >= 300) { + throw new Error(`HTTP ${resp.status}: ${JSON.stringify(resp.data)}`) + } + + if (resp.data.errors?.length) { + throw new Error(JSON.stringify(resp.data.errors, null, 2)) + } + + if (!resp.data.data) throw new Error("Missing GraphQL data") + return resp.data.data +} + +const recordCheck = async (name: string, check: () => Promise) => { + try { + const details = await check() + pass(name, details || undefined) + } catch (err) { + fail(name, err instanceof Error ? err.message : `${err}`) + } +} + +const optionalCheck = async ( + args: Args, + name: string, + fixtureDescription: string, + check: () => Promise, +) => { + if (args.strictFixtures) { + await recordCheck(name, check) + return + } + + try { + const details = await check() + pass(name, details || undefined) + } catch (err) { + const message = err instanceof Error ? err.message : `${err}` + if (message.includes("Missing fixture:")) { + skip(name, fixtureDescription) + return + } + fail(name, message) + } +} + +const walletSummary = (wallet: Wallet) => + `${wallet.__typename}/${wallet.walletCurrency}/${wallet.id}` + +const getWallets = async (args: Args): Promise => { + const data = await graphql<{ + me: { + defaultAccount: { + wallets: Wallet[] + } + } | null + }>( + args, + "Eng297WalletsAndTransactions", + ` + query Eng297WalletsAndTransactions($first: Int!) { + me { + defaultAccount { + wallets { + __typename + ... on BTCWallet { + id + walletCurrency + balance + transactions(first: $first) { ...TransactionConnectionFields } + } + ... on UsdWallet { + id + walletCurrency + usdBalance: balance + isExternal + transactions(first: $first) { ...TransactionConnectionFields } + } + ... on UsdtWallet { + id + walletCurrency + usdtBalance: balance + isExternal + transactions(first: $first) { ...TransactionConnectionFields } + } + } + } + } + } + + fragment TransactionConnectionFields on TransactionConnection { + edges { + node { + id + settlementCurrency + settlementAmount + settlementDisplayAmount + settlementDisplayCurrency + settlementDisplayFee + settlementFee + settlementPrice { + base + offset + } + initiationVia { __typename } + settlementVia { __typename } + } + } + } + `, + { first: args.first }, + ) + + const defaultAccount = data.me?.defaultAccount + assert(defaultAccount, "Missing me.defaultAccount") + return defaultAccount.wallets +} + +const requireWallet = (wallets: Wallet[], currency: string): Wallet => { + const wallet = wallets.find((candidate) => candidate.walletCurrency === currency) + assert(wallet, `Missing ${currency} wallet`) + return wallet as Wallet +} + +const assertNoTransactionShapeGaps = (wallet: Wallet) => { + const transactions = wallet.transactions?.edges || [] + for (const edge of transactions) { + const tx = edge.node + if (!tx) { + throw new Error(`${walletSummary(wallet)} has transaction edge without node`) + } + assert(tx.id, `${walletSummary(wallet)} transaction missing id`) + assert( + tx.settlementCurrency, + `${walletSummary(wallet)} transaction ${tx.id} missing settlementCurrency`, + ) + assert( + tx.settlementDisplayCurrency, + `${walletSummary(wallet)} transaction ${tx.id} missing settlementDisplayCurrency`, + ) + assert( + tx.settlementPrice, + `${walletSummary(wallet)} transaction ${tx.id} missing settlementPrice`, + ) + assert( + tx.initiationVia?.__typename, + `${walletSummary(wallet)} transaction ${tx.id} missing initiationVia.__typename`, + ) + assert( + tx.settlementVia?.__typename, + `${walletSummary(wallet)} transaction ${tx.id} missing settlementVia.__typename`, + ) + } +} + +const runLnUsdInvoiceFeeProbe = async (args: Args, wallet: Wallet) => { + if (!args.paymentRequest) throw new Error("Missing fixture: paymentRequest") + + const data = await graphql<{ + lnUsdInvoiceFeeProbe: { + errors: { message: string }[] + amount?: number + invoiceAmount?: number + } + }>( + args, + "Eng297LnUsdInvoiceFeeProbe", + ` + mutation Eng297LnUsdInvoiceFeeProbe($input: LnUsdInvoiceFeeProbeInput!) { + lnUsdInvoiceFeeProbe(input: $input) { + errors { message } + amount + invoiceAmount + } + } + `, + { + input: { + walletId: wallet.id, + paymentRequest: args.paymentRequest, + }, + }, + ) + + const result = data.lnUsdInvoiceFeeProbe + assert( + result.errors.length === 0, + `${walletSummary(wallet)} lnUsdInvoiceFeeProbe errors: ${JSON.stringify(result.errors)}`, + ) + assert(result.amount !== undefined, `${walletSummary(wallet)} missing fee amount`) + assert( + result.invoiceAmount !== undefined, + `${walletSummary(wallet)} missing invoiceAmount`, + ) +} + +const runLnNoAmountUsdInvoiceFeeProbe = async (args: Args, wallet: Wallet) => { + if (!args.paymentRequest) throw new Error("Missing fixture: paymentRequest") + + const data = await graphql<{ + lnNoAmountUsdInvoiceFeeProbe: { + errors: { message: string }[] + amount?: number + invoiceAmount?: number + } + }>( + args, + "Eng297LnNoAmountUsdInvoiceFeeProbe", + ` + mutation Eng297LnNoAmountUsdInvoiceFeeProbe($input: LnNoAmountUsdInvoiceFeeProbeInput!) { + lnNoAmountUsdInvoiceFeeProbe(input: $input) { + errors { message } + amount + invoiceAmount + } + } + `, + { + input: { + walletId: wallet.id, + paymentRequest: args.paymentRequest, + amount: args.amount, + }, + }, + ) + + const result = data.lnNoAmountUsdInvoiceFeeProbe + assert( + result.errors.length === 0, + `${walletSummary(wallet)} lnNoAmountUsdInvoiceFeeProbe errors: ${JSON.stringify(result.errors)}`, + ) + assert(result.amount !== undefined, `${walletSummary(wallet)} missing fee amount`) + assert( + result.invoiceAmount !== undefined, + `${walletSummary(wallet)} missing invoiceAmount`, + ) +} + +const runOnChainUsdTxFee = async (args: Args, wallet: Wallet) => { + if (!args.address) throw new Error("Missing fixture: address") + + const data = await graphql<{ + onChainUsdTxFee: { + amount: number + } + }>( + args, + "Eng297OnChainUsdTxFee", + ` + query Eng297OnChainUsdTxFee( + $walletId: WalletId! + $address: OnChainAddress! + $amount: FractionalCentAmount! + ) { + onChainUsdTxFee(walletId: $walletId, address: $address, amount: $amount) { + amount + } + } + `, + { + walletId: wallet.id, + address: args.address, + amount: args.amount, + }, + ) + + assert( + data.onChainUsdTxFee.amount !== undefined, + `${walletSummary(wallet)} missing onChainUsdTxFee amount`, + ) +} + +const main = async () => { + const args = parseArgs(process.argv.slice(2)) + let wallets: Wallet[] = [] + + await recordCheck("wallet list includes USD and USDT wallets", async () => { + wallets = await getWallets(args) + const usdWallet = requireWallet(wallets, "USD") + const usdtWallet = requireWallet(wallets, "USDT") + return `${walletSummary(usdWallet)}, ${walletSummary(usdtWallet)}` + }) + + if (wallets.length > 0) { + await recordCheck("wallet transaction shapes are render-safe", async () => { + for (const wallet of wallets) assertNoTransactionShapeGaps(wallet) + const counts = wallets.map( + (wallet) => `${wallet.walletCurrency}:${wallet.transactions?.edges?.length || 0}`, + ) + return counts.join(", ") + }) + + for (const currency of ["USD", "USDT"]) { + const wallet = wallets.find((candidate) => candidate.walletCurrency === currency) + if (!wallet) continue + + await optionalCheck( + args, + `${currency} lnUsdInvoiceFeeProbe`, + "skipped; pass --payment-request to exercise Lightning fee probe", + () => runLnUsdInvoiceFeeProbe(args, wallet), + ) + + await optionalCheck( + args, + `${currency} lnNoAmountUsdInvoiceFeeProbe`, + "skipped; pass --payment-request to exercise no-amount Lightning fee probe", + () => runLnNoAmountUsdInvoiceFeeProbe(args, wallet), + ) + + await optionalCheck( + args, + `${currency} onChainUsdTxFee`, + "skipped; pass --address to exercise on-chain fee probe", + () => runOnChainUsdTxFee(args, wallet), + ) + } + } + + for (const result of results) { + const icon = result.status === "PASS" ? "✅" : result.status === "SKIP" ? "⏭️" : "❌" + console.log(`${icon} ${result.status} ${result.name}`) + if (result.details) console.log(` ${result.details}`) + } + + const failed = results.filter((result) => result.status === "FAIL") + if (failed.length > 0) { + console.error(`\n${failed.length} smoke check(s) failed`) + process.exit(1) + } + + console.log("\nENG-297 GraphQL smoke checks completed") +} + +main().catch((err) => { + console.error(err instanceof Error ? err.message : err) + process.exit(1) +}) diff --git a/src/app/payments/send-intraledger.ts b/src/app/payments/send-intraledger.ts index fb7017d09..b38633361 100644 --- a/src/app/payments/send-intraledger.ts +++ b/src/app/payments/send-intraledger.ts @@ -7,6 +7,7 @@ import { } from "@app/prices" import { removeDeviceTokens } from "@app/users/remove-device-tokens" import { validateIsBtcWallet, validateIsUsdWallet } from "@app/wallets" +import { usdWalletAmountFromInput } from "@app/wallets/usd-wallet-amount" import { InvalidLightningPaymentFlowBuilderStateError, @@ -15,11 +16,12 @@ import { } from "@domain/payments" import { AccountLevel, AccountValidator } from "@domain/accounts" import { DisplayAmountsConverter } from "@domain/fiat" -import { BigIntConversionError, checkedToUsdPaymentAmount, ErrorLevel, paymentAmountFromNumber, USDAmount, ValidationError, WalletCurrency } from "@domain/shared" +import { checkedToUsdPaymentAmount, ErrorLevel, paymentAmountFromNumber, ValidationError, WalletCurrency } from "@domain/shared" import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { ResourceExpiredLockServiceError } from "@domain/lock" import { checkedToWalletId, SettlementMethod } from "@domain/wallets" import { DeviceTokensNotRegisteredNotificationsServiceError } from "@domain/notifications" +import { MismatchedCurrencyForWalletError } from "@domain/errors" import { LockService } from "@services/lock" import { LedgerService } from "@services/ledger" @@ -74,8 +76,12 @@ const intraledgerPaymentSendWalletId = async ({ kratosUserId: recipientUserId, } = recipientAccount - const amount = USDAmount.cents(uncheckedAmount.toString()) - if (amount instanceof BigIntConversionError) return amount + if (senderWallet.currency !== recipientWallet.currency) { + return new MismatchedCurrencyForWalletError() + } + + const amount = usdWalletAmountFromInput(uncheckedAmount.toString(), senderWallet.currency) + if (amount instanceof Error) return amount const invoiceResp = await Ibex.addInvoice({ accountId: recipientWalletId, amount, @@ -132,7 +138,7 @@ export const intraledgerPaymentSendWalletIdForBtcWallet = async ( export const intraledgerPaymentSendWalletIdForUsdWallet = async ( args: IntraLedgerPaymentSendWalletIdArgs, ): Promise => { - const validated = await validateIsUsdWallet(args.senderWalletId) + const validated = await validateIsUsdWallet(args.senderWalletId, { includeUsdt: true }) return validated instanceof Error ? validated : intraledgerPaymentSendWalletId(args) } diff --git a/src/app/payments/send-lightning.ts b/src/app/payments/send-lightning.ts index 730de6755..880205c86 100644 --- a/src/app/payments/send-lightning.ts +++ b/src/app/payments/send-lightning.ts @@ -172,7 +172,7 @@ export const payNoAmountInvoiceByWalletIdForBtcWallet = async ( export const payNoAmountInvoiceByWalletIdForUsdWallet = async ( args: PayNoAmountInvoiceByWalletIdArgs, ): Promise => { - const validated = await validateIsUsdWallet(args.senderWalletId) + const validated = await validateIsUsdWallet(args.senderWalletId, { includeUsdt: true }) return validated instanceof Error ? validated : payNoAmountInvoiceByWalletId(args) } @@ -278,7 +278,10 @@ const validateNoAmountInvoicePaymentInputs = async ({ const inputPaymentAmount = senderWallet.currency === WalletCurrency.Btc ? checkedToBtcPaymentAmount(amount) - : checkedToUsdPaymentAmount(amount) + : checkedToUsdPaymentAmount( + amount, + senderWallet.currency as typeof WalletCurrency.Usd | typeof WalletCurrency.Usdt, + ) if (inputPaymentAmount instanceof Error) return inputPaymentAmount return { diff --git a/src/app/wallets/add-invoice-for-wallet.ts b/src/app/wallets/add-invoice-for-wallet.ts index 9a301d04b..49ef30994 100644 --- a/src/app/wallets/add-invoice-for-wallet.ts +++ b/src/app/wallets/add-invoice-for-wallet.ts @@ -20,9 +20,11 @@ import { import Ibex from "@services/ibex/client" import { IbexError, UnexpectedIbexResponse } from "@services/ibex/errors" import { decodeInvoice } from "@domain/bitcoin/lightning/ln-invoice" -import { USDAmount } from "@domain/shared" + import { AddInvoiceResponse201 } from "ibex-client" +import { usdWalletAmountFromInput } from "./usd-wallet-amount" + import { validateIsBtcWallet, validateIsUsdWallet } from "./validate" const defaultBtcExpiration = DEFAULT_EXPIRATIONS["BTC"].delayMinutes @@ -46,7 +48,9 @@ const addInvoiceForSelf = async ({ const limitOk = await checkSelfWalletIdRateLimits(wallet.accountId) if (limitOk instanceof Error) return limitOk - const checkedAmount = amount ? USDAmount.cents(amount.toString()) : undefined + const checkedAmount = amount + ? usdWalletAmountFromInput(amount.toString(), wallet.currency) + : undefined if (checkedAmount instanceof Error) return checkedAmount const resp = await Ibex.addInvoice({ amount: checkedAmount, @@ -77,7 +81,7 @@ export const addInvoiceForSelfForUsdWallet = async ( const expiresIn = checkedToMinutes(args.expiresIn || defaultUsdExpiration) if (expiresIn instanceof Error) return expiresIn - const validated = await validateIsUsdWallet(walletId) + const validated = await validateIsUsdWallet(walletId, { includeUsdt: true }) if (validated instanceof Error) return validated return addInvoiceForSelf({ ...args, walletId, expiresIn }) } @@ -133,7 +137,7 @@ const addInvoiceForRecipient = async ({ const limitOk = await checkRecipientWalletIdRateLimits(wallet.accountId) if (limitOk instanceof Error) return limitOk - const checkedAmount = USDAmount.cents(amount.toString()) + const checkedAmount = usdWalletAmountFromInput(amount.toString(), wallet.currency) if (checkedAmount instanceof Error) return checkedAmount const resp = await Ibex.addInvoice({ amount: checkedAmount, @@ -163,7 +167,7 @@ export const addInvoiceForRecipientForUsdWallet = async ( const expiresIn = checkedToMinutes(args.expiresIn || defaultUsdExpiration) if (expiresIn instanceof Error) return expiresIn - const validated = await validateIsUsdWallet(recipientWalletId) + const validated = await validateIsUsdWallet(recipientWalletId, { includeUsdt: true }) if (validated instanceof Error) return validated return addInvoiceForRecipient({ ...args, recipientWalletId, expiresIn }) diff --git a/src/app/wallets/get-transactions-for-wallet.ts b/src/app/wallets/get-transactions-for-wallet.ts index e8925dd70..4d3bec459 100644 --- a/src/app/wallets/get-transactions-for-wallet.ts +++ b/src/app/wallets/get-transactions-for-wallet.ts @@ -1,4 +1,5 @@ import { PartialResult } from "@app/partial-result" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" import Ibex from "@services/ibex/client" import { IbexError } from "@services/ibex/errors" import { baseLogger } from "@services/logger" @@ -32,9 +33,39 @@ export const getTransactionsForWallets = async ({ }) } +const currencyFromIbexCurrencyId = (currencyId: number | undefined): WalletCurrency | undefined => { + if (currencyId === USDAmount.currencyId) return WalletCurrency.Usd + if (currencyId === USDTAmount.currencyId) return WalletCurrency.Usdt + return undefined +} + export const toWalletTransactions = (ibexResp: GResponse200): IbexTransaction[] => { return ibexResp.map(trx => { - const currency = (trx.currencyId === 3 ? "USD" : "BTC") as WalletCurrency // WalletCurrency: "USD" | "BTC", + const currency = currencyFromIbexCurrencyId(trx.currencyId) + + if (!currency) { + baseLogger.error(`Failed to parse Ibex transaction currency. { WalletId: ${trx.accountId}, TransactionId: ${trx.id}, currencyId: ${trx.currencyId} }`) + return { + walletId: (trx.accountId || "") as WalletId, + settlementAmount: 0 as Satoshis, + settlementFee: 0 as Satoshis, + settlementCurrency: WalletCurrency.Usd, + settlementDisplayAmount: `${trx.amount}`, + settlementDisplayFee: `${trx.networkFee}`, + settlementDisplayPrice: { + base: 0n, + offset: 0n, + displayCurrency: "USD" as DisplayCurrency, + walletCurrency: WalletCurrency.Usd, + }, + createdAt: trx.createdAt ? new Date(trx.createdAt) : new Date(), + id: trx.id || "null", + status: "success" as TxStatus, + memo: null, + initiationVia: { type: "unknown" }, + settlementVia: { type: "unknown" }, + } as UnknownTypeTransaction + } const settlementDisplayPrice: WalletMinorUnitDisplayPrice = { base: trx.exchangeRateCurrencySats ? BigInt(Math.floor(trx.exchangeRateCurrencySats)) : 0n, diff --git a/src/app/wallets/index.ts b/src/app/wallets/index.ts index 3839fed5a..94210271c 100644 --- a/src/app/wallets/index.ts +++ b/src/app/wallets/index.ts @@ -17,6 +17,7 @@ export * from "./settle-payout-txn" export * from "./update-legacy-on-chain-receipt" export * from "./update-pending-invoices" export * from "./validate" +export * from "./usd-wallet-amount" import { WalletsRepository } from "@services/mongoose" diff --git a/src/app/wallets/send-on-chain.ts b/src/app/wallets/send-on-chain.ts index a50ed3034..fddf17f32 100644 --- a/src/app/wallets/send-on-chain.ts +++ b/src/app/wallets/send-on-chain.ts @@ -3,7 +3,8 @@ import { NETWORK } from "@config" import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { checkedToOnChainAddress } from "@domain/bitcoin/onchain" import { UnsupportedCurrencyError } from "@domain/errors" -import { ErrorLevel, USDAmount } from "@domain/shared" +import { ErrorLevel } from "@domain/shared" +import { UsdWalletAmount } from "@services/ibex/types" import { OnchainUsdPaymentValidator } from "@domain/wallets" import { AccountsRepository, WalletsRepository } from "@services/mongoose" @@ -19,15 +20,15 @@ type PayOnChainByWalletIdWithoutCurrencyArgs = { address: string speed: PayoutSpeed memo: string | null - amount?: number | FractionalCentAmount | USDAmount + amount?: number | FractionalCentAmount | UsdWalletAmount } type PayOnChainByUsdArgs = PayOnChainByWalletIdWithoutCurrencyArgs & { - amount: USDAmount + amount: UsdWalletAmount } type PayOnChainByWalletIdForUsdWalletArgs = PayOnChainByWalletIdWithoutCurrencyArgs & { - amount: number | FractionalCentAmount | USDAmount + amount: number | FractionalCentAmount | UsdWalletAmount } /* diff --git a/src/app/wallets/usd-wallet-amount.ts b/src/app/wallets/usd-wallet-amount.ts new file mode 100644 index 000000000..e2c166e4f --- /dev/null +++ b/src/app/wallets/usd-wallet-amount.ts @@ -0,0 +1,31 @@ +import { UnsupportedCurrencyError } from "@domain/errors" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +import { WalletsRepository } from "@services/mongoose" + +export type UsdWalletAmount = USDAmount | USDTAmount + +export const usdWalletAmountFromInput = ( + amount: string | number, + currency: WalletCurrency, +): UsdWalletAmount | ApplicationError => { + const raw = amount.toString() + + if (currency === WalletCurrency.Usd) return USDAmount.cents(raw) + if (currency === WalletCurrency.Usdt) return USDTAmount.smallestUnits(raw) + + return new UnsupportedCurrencyError(`USD wallet amount unsupported for ${currency}`) +} + +export const usdWalletAmountFromWalletId = async ({ + walletId, + amount, +}: { + walletId: WalletId + amount: string | number +}): Promise => { + const wallet = await WalletsRepository().findById(walletId) + if (wallet instanceof Error) return wallet + + return usdWalletAmountFromInput(amount, wallet.currency) +} diff --git a/src/app/wallets/validate.ts b/src/app/wallets/validate.ts index a214ada91..3b22093f5 100644 --- a/src/app/wallets/validate.ts +++ b/src/app/wallets/validate.ts @@ -9,7 +9,7 @@ export const validateIsBtcWallet = async ( const wallet = await WalletsRepository().findById(walletId) if (wallet instanceof Error) return wallet - if (wallet.currency === WalletCurrency.Usd) { + if (wallet.currency !== WalletCurrency.Btc) { return new MismatchedCurrencyForWalletError() } return true @@ -17,11 +17,28 @@ export const validateIsBtcWallet = async ( export const validateIsUsdWallet = async ( walletId: WalletId, + args?: { includeUsdt?: boolean }, ): Promise => { const wallet = await WalletsRepository().findById(walletId) if (wallet instanceof Error) return wallet - if (wallet.currency === WalletCurrency.Btc) { + const isAllowed = + wallet.currency === WalletCurrency.Usd || + (args?.includeUsdt === true && wallet.currency === WalletCurrency.Usdt) + + if (!isAllowed) { + return new MismatchedCurrencyForWalletError() + } + return true +} + +export const validateIsUsdtWallet = async ( + walletId: WalletId, +): Promise => { + const wallet = await WalletsRepository().findById(walletId) + if (wallet instanceof Error) return wallet + + if (wallet.currency !== WalletCurrency.Usdt) { return new MismatchedCurrencyForWalletError() } return true diff --git a/src/domain/shared/amount.ts b/src/domain/shared/amount.ts index 0e867aad4..2f5438784 100644 --- a/src/domain/shared/amount.ts +++ b/src/domain/shared/amount.ts @@ -90,9 +90,16 @@ export const UsdPaymentAmount = (cents: bigint): UsdPaymentAmount => { } } -export const checkedToUsdPaymentAmount = ( +export function checkedToUsdPaymentAmount( amount: number | null, -): UsdPaymentAmount | ValidationError => { +): UsdPaymentAmount | ValidationError +export function checkedToUsdPaymentAmount< + T extends typeof WalletCurrency.Usd | typeof WalletCurrency.Usdt, +>(amount: number | null, currency: T): PaymentAmount | ValidationError +export function checkedToUsdPaymentAmount( + amount: number | null, + currency: typeof WalletCurrency.Usd | typeof WalletCurrency.Usdt = WalletCurrency.Usd, +): UsdPaymentAmount | PaymentAmount | ValidationError { if (amount === null) { return new InvalidUsdPaymentAmountError() } @@ -105,7 +112,7 @@ export const checkedToUsdPaymentAmount = ( return new InvalidUsdPaymentAmountError() } if (!(amount && amount > 0)) return new InvalidUsdPaymentAmountError() - return paymentAmountFromNumber({ amount, currency: WalletCurrency.Usd }) + return paymentAmountFromNumber({ amount, currency }) } export const paymentAmountFromNumber = ({ diff --git a/src/domain/wallets/payment-input-validator.ts b/src/domain/wallets/payment-input-validator.ts index eefe1b093..0491b3786 100644 --- a/src/domain/wallets/payment-input-validator.ts +++ b/src/domain/wallets/payment-input-validator.ts @@ -1,4 +1,10 @@ -import { USDAmount, ValidationError, isUsdWallet, validator } from "@domain/shared" +import { + USDAmount, + USDTAmount, + ValidationError, + WalletCurrency, + validator, +} from "@domain/shared" import { isActiveAccount, walletBelongsToAccount } from "@domain/accounts" import { SendOnchainArgs } from "@services/ibex/types" @@ -8,23 +14,35 @@ import { SendOnchainArgs } from "@services/ibex/types" // else return true // } -const checkOnchainMin = async (o: { amount: USDAmount }) => { +const isUsdWalletForOnChainPayment = async (o: { wallet: Wallet }) => { + if ( + o.wallet.currency === WalletCurrency.Usd || + o.wallet.currency === WalletCurrency.Usdt + ) { + return true + } + return new ValidationError(`Expected USD, got ${o.wallet.currency}`) +} + +const checkOnchainMin = async (o: { amount: USDAmount | USDTAmount }) => { // TODO: Currently relying on Ibex to enforce dust limits // const { dustThreshold } = getOnChainWalletConfig() // const minBtc = BtcAmount.sats(dustThreshold.toString()) // const btcPrice = await PriceService().getUsdCentRealTimePrice(_) // if (btcPrice instanceof PriceServiceError) return new ValidationError(btcPrice) // const minUsd = minBtc.convertAtRate(MoneyAmount.from("50000", WalletCurrency.Usd)) - const minUsd = USDAmount.ZERO - return o.amount.isGreaterThan(minUsd) - ? true - : new ValidationError(`Amount must be greater than ${minUsd.asDollars()}`) + const isGreaterThanZero = + o.amount instanceof USDTAmount + ? o.amount.isGreaterThan(USDTAmount.ZERO) + : o.amount.isGreaterThan(USDAmount.ZERO) + + return isGreaterThanZero ? true : new ValidationError("Amount must be greater than 0") } type SendOnchainArgsWithContext = SendOnchainArgs & { wallet: Wallet; account: Account } export const OnchainUsdPaymentValidator = validator([ - isUsdWallet, + isUsdWalletForOnChainPayment, isActiveAccount, walletBelongsToAccount, checkOnchainMin, diff --git a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe.ts b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe.ts index 1950be8a7..89fe1592f 100644 --- a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe.ts +++ b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe.ts @@ -1,22 +1,15 @@ -import { InvalidFeeProbeStateError } from "@domain/bitcoin/lightning" - -// import { Payments } from "@app" +import { usdWalletAmountFromWalletId, UsdWalletAmount } from "@app/wallets" import { GT } from "@graphql/index" import WalletId from "@graphql/shared/types/scalar/wallet-id" -import CentAmount from "@graphql/public/types/scalar/cent-amount" import CentAmountPayload from "@graphql/public/types/payload/cent-amount" import LnPaymentRequest from "@graphql/shared/types/scalar/ln-payment-request" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" -import { normalizePaymentAmount } from "../../../shared/root/mutation" - // FLASH FORK: import ibex dependencies import Ibex from "@services/ibex/client" -import { IbexError, UnexpectedIbexResponse } from "@services/ibex/errors" -import { GetFeeEstimateResponse200 } from "ibex-client" -import { BigIntConversionError, checkedToUsdPaymentAmount, USDAmount, ValidationError } from "@domain/shared" +import { IbexError } from "@services/ibex/errors" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import { IbexFeeEstimation } from "@services/ibex/types" @@ -55,9 +48,14 @@ const LnNoAmountUsdInvoiceFeeProbeMutation = GT.Field({ // }) // TODO: Move Ibex call to Payments interface - const checkedAmount = USDAmount.cents(amount.toString()) - if (checkedAmount instanceof BigIntConversionError) return checkedAmount - const resp: IbexFeeEstimation | IbexError = await Ibex.getLnFeeEstimation({ + const checkedAmount = await usdWalletAmountFromWalletId({ + walletId, + amount: amount.toString(), + }) + if (checkedAmount instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(checkedAmount)] } + } + const resp: IbexFeeEstimation | IbexError = await Ibex.getLnFeeEstimation({ invoice: paymentRequest as Bolt11, send: checkedAmount, }) diff --git a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts index 0ffbb8aca..e8d8d6227 100644 --- a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts +++ b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts @@ -13,10 +13,10 @@ import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fract // FLASH FORK: import ibex dependencies import { PaymentSendStatus } from "@domain/bitcoin/lightning" +import { usdWalletAmountFromWalletId } from "@app/wallets" import Ibex from "@services/ibex/client" import { IbexError } from "@services/ibex/errors" -import { BigIntConversionError, checkedToUsdPaymentAmount, paymentAmountFromNumber, USDAmount, ValidationError, WalletCurrency } from "@domain/shared" const LnNoAmountUsdInvoicePaymentInput = GT.Input({ name: "LnNoAmountUsdInvoicePaymentInput", @@ -90,9 +90,16 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< if (!domainAccount) throw new Error("Authentication required") // eslint-disable-next-line @typescript-eslint/no-explicit-any - - const usCents = USDAmount.cents(amount.toString()) - if (usCents instanceof BigIntConversionError) return usCents + const usCents = await usdWalletAmountFromWalletId({ + walletId, + amount: amount.toString(), + }) + if (usCents instanceof Error) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(usCents)], + } + } const PayLightningInvoice = await Ibex.payInvoice({ invoice: paymentRequest as Bolt11, accountId: walletId, @@ -102,7 +109,7 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< if (PayLightningInvoice instanceof IbexError) { return { status: "failed", - errors: [mapAndParseErrorForGqlResponse(PayLightningInvoice)] + errors: [mapAndParseErrorForGqlResponse(PayLightningInvoice)], } } diff --git a/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts b/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts index 8c2223ff9..ccfb2c7bb 100644 --- a/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts +++ b/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts @@ -1,7 +1,3 @@ -import { InvalidFeeProbeStateError } from "@domain/bitcoin/lightning" - -// import { Payments } from "@app" - import { GT } from "@graphql/index" import WalletId from "@graphql/shared/types/scalar/wallet-id" import CentAmountPayload from "@graphql/public/types/payload/cent-amount" @@ -10,17 +6,10 @@ import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import { checkedToWalletId } from "@domain/wallets" -import { normalizePaymentAmount } from "../../../shared/root/mutation" - -// FLASH FORK: import ibex dependencies import Ibex from "@services/ibex/client" -import { IbexError, UnexpectedIbexResponse } from "@services/ibex/errors" -import { ValidationError, WalletCurrency } from "@domain/shared" -import { baseLogger } from "@services/logger" -import IError from "@graphql/shared/types/abstract/error" -import USDCentsScalar from "@graphql/shared/types/scalar/usd-cents" -import CentAmount from "@graphql/public/types/scalar/cent-amount" +import { IbexError } from "@services/ibex/errors" +import { WalletsRepository } from "@services/mongoose" // import { IbexRoutes } from "../../../../services/ibex/Routes" // import { requestIBexPlugin } from "../../../../services/ibex/IbexHelper" @@ -86,9 +75,14 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< // uncheckedPaymentRequest: paymentRequest, // }) + const wallet = await WalletsRepository().findById(walletIdChecked) + if (wallet instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(wallet)] } + } + const resp = await Ibex.getLnFeeEstimation({ invoice: paymentRequest as Bolt11, - // send: { currencyId: USDollars.currencyId }, + currency: wallet.currency, }) if (resp instanceof IbexError) return { errors: [mapAndParseErrorForGqlResponse(resp)] } diff --git a/src/graphql/public/root/mutation/onchain-usd-payment-send.ts b/src/graphql/public/root/mutation/onchain-usd-payment-send.ts index bdd8f4b83..b45224a6c 100644 --- a/src/graphql/public/root/mutation/onchain-usd-payment-send.ts +++ b/src/graphql/public/root/mutation/onchain-usd-payment-send.ts @@ -11,7 +11,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { Wallets } from "@app/index" -import { BigIntConversionError, USDAmount } from "@domain/shared" +import { usdWalletAmountFromWalletId } from "@app/wallets" const OnChainUsdPaymentSendInput = GT.Input({ name: "OnChainUsdPaymentSendInput", @@ -67,8 +67,16 @@ const OnChainUsdPaymentSendMutation = GT.Field< } if (!domainAccount) throw new Error("Authentication required") - const usdAmount = USDAmount.cents(amount.toString()) - if (usdAmount instanceof BigIntConversionError) return usdAmount + const usdAmount = await usdWalletAmountFromWalletId({ + walletId, + amount: amount.toString(), + }) + if (usdAmount instanceof Error) { + return { + status: PaymentSendStatus.Failure.value, + errors: [mapAndParseErrorForGqlResponse(usdAmount)], + } + } const result = await Wallets.payOnChainByWalletId({ senderAccount: domainAccount, diff --git a/src/graphql/public/root/query/on-chain-usd-tx-fee-query.ts b/src/graphql/public/root/query/on-chain-usd-tx-fee-query.ts index 90b96e51d..63977876f 100644 --- a/src/graphql/public/root/query/on-chain-usd-tx-fee-query.ts +++ b/src/graphql/public/root/query/on-chain-usd-tx-fee-query.ts @@ -1,11 +1,7 @@ import { PayoutSpeed as DomainPayoutSpeed } from "@domain/bitcoin/onchain" -import { paymentAmountFromNumber, USDAmount, ValidationError, WalletCurrency } from "@domain/shared" - -// import { Wallets } from "@app" +import { usdWalletAmountFromWalletId } from "@app/wallets" import { GT } from "@graphql/index" -import { mapError } from "@graphql/error-map" - import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import OnChainAddress from "@graphql/shared/types/scalar/on-chain-address" import PayoutSpeed from "@graphql/public/types/scalar/payout-speed" @@ -13,8 +9,6 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import OnChainUsdTxFee from "@graphql/public/types/object/onchain-usd-tx-fee" -import { normalizePaymentAmount } from "../../../shared/root/mutation" - // FLASH FORK: import ibex dependencies import Ibex from "@services/ibex/client" @@ -46,7 +40,10 @@ const OnChainUsdTxFeeQuery = GT.Field({ // speed, // }) - const send = USDAmount.cents(amount.toString()) + const send = await usdWalletAmountFromWalletId({ + walletId, + amount: amount.toString(), + }) if (send instanceof Error) return send const resp = await Ibex.estimateOnchainFee(send, address) diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index b5badcb03..8464520e1 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -40,6 +40,7 @@ import { CryptoReceiveInfo, CreateCryptoReceiveInfoRequest, IbexCurrency, + UsdWalletAmount, } from "./types" import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" @@ -61,6 +62,30 @@ const createAccount = async ( return Ibex.createAccount({ name, currencyId }).then(errorHandler) } +const ibexCurrencyIdForUsdAmount = (amount: UsdWalletAmount): IbexCurrencyId => { + if (amount instanceof USDAmount) return USDAmount.currencyId + return USDTAmount.currencyId +} + +const ibexCurrencyIdForUsdWalletCurrency = ( + currency?: WalletCurrency, +): IbexCurrencyId => { + if (currency === WalletCurrency.Usdt) return USDTAmount.currencyId + return USDAmount.currencyId +} + +const parseIbexUsdAmount = ( + amount: number | string, + currencyId: IbexCurrencyId, +): UsdWalletAmount | ParseError => { + const parsed = + currencyId === USDTAmount.currencyId + ? USDTAmount.fromNumber(amount.toString()) + : USDAmount.dollars(amount.toString()) + + return parsed instanceof Error ? new ParseError(parsed) : parsed +} + const parseAccountBalance = ( balance: number | undefined, currency: WalletCurrency, @@ -135,16 +160,16 @@ const invoiceFromHash = async ( return Ibex.invoiceFromHash({ invoice_hash }).then(errorHandler) } -// Only supports USD for now const getLnFeeEstimation = async ( args: GetFeeEstimateArgs, -): Promise => { - const currencyId = USDAmount.currencyId - // const amount = (args.send instanceof IbexCurrency) ? args.send.amount.toString() : undefined +): Promise | IbexError> => { + const currencyId = args.send + ? ibexCurrencyIdForUsdAmount(args.send) + : ibexCurrencyIdForUsdWalletCurrency(args.currency) const resp = await Ibex.getFeeEstimation({ bolt11: args.invoice as string, - amount: args.send?.asDollars(8), + amount: args.send?.toIbex().toString(), currencyId: currencyId.toString(), }) if (resp instanceof Error) return new IbexError(resp) @@ -153,10 +178,10 @@ const getLnFeeEstimation = async ( else if (resp.invoiceAmount === null || resp.invoiceAmount === undefined) return new UnexpectedIbexResponse("invoiceAmount not found.") else { - const fee = USDAmount.dollars(resp.amount) - if (fee instanceof Error) return new ParseError(fee) - const invoiceAmount = USDAmount.dollars(resp.invoiceAmount) - if (invoiceAmount instanceof Error) return new ParseError(invoiceAmount) + const fee = parseIbexUsdAmount(resp.amount, currencyId) + if (fee instanceof Error) return fee + const invoiceAmount = parseIbexUsdAmount(resp.invoiceAmount, currencyId) + if (invoiceAmount instanceof Error) return invoiceAmount return { fee, invoice: invoiceAmount, @@ -193,12 +218,12 @@ const sendOnchain = async ( } const estimateOnchainFee = async ( - send: USDAmount, + send: UsdWalletAmount, address: OnChainAddress, ): Promise => { return Ibex.estimateFeeV2({ "amount": send.toIbex(), - "currency-id": USDAmount.currencyId.toString(), + "currency-id": ibexCurrencyIdForUsdAmount(send).toString(), address, }).then(errorHandler) } diff --git a/src/services/ibex/types.ts b/src/services/ibex/types.ts index 16764d137..c7fde4b71 100644 --- a/src/services/ibex/types.ts +++ b/src/services/ibex/types.ts @@ -1,26 +1,29 @@ -import { USDAmount, USDTAmount } from "@domain/shared" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +export type UsdWalletAmount = USDAmount | USDTAmount export type PayInvoiceArgs = { accountId: IbexAccountId invoice: Bolt11 - send?: USDAmount // must match currency of account + send?: UsdWalletAmount // must match currency of account } export type SendOnchainArgs = { accountId: IbexAccountId // source of funds address: OnChainAddress // destination - amount: USDAmount + amount: UsdWalletAmount } // Ibex supports fee estimation in different currencies export type GetFeeEstimateArgs = { invoice: Bolt11 - send?: USDAmount + send?: UsdWalletAmount + currency?: WalletCurrency } -export type IbexFeeEstimation = { - fee: USDAmount - invoice: USDAmount +export type IbexFeeEstimation = { + fee: T + invoice: T } export type IbexAccountDetails = { @@ -32,7 +35,7 @@ export type IbexAccountDetails = { export type IbexInvoiceArgs = { accountId: IbexAccountId - amount?: USDAmount + amount?: UsdWalletAmount memo: string expiration?: Seconds } diff --git a/test/flash/unit/app/payments/send-intraledger.spec.ts b/test/flash/unit/app/payments/send-intraledger.spec.ts new file mode 100644 index 000000000..179ff1259 --- /dev/null +++ b/test/flash/unit/app/payments/send-intraledger.spec.ts @@ -0,0 +1,262 @@ +const mockAddInvoice = jest.fn() +const mockPayInvoice = jest.fn() +const mockFindWalletById = jest.fn() +const mockFindAccountById = jest.fn() + +jest.mock("@config", () => ({ + getCallbackServiceConfig: jest.fn(() => ({})), + getValuesToSkipProbe: jest.fn(() => []), +})) + +jest.mock("@services/tracing", () => ({ + addAttributesToCurrentSpan: jest.fn(), + recordExceptionInCurrentSpan: jest.fn(), +})) + +jest.mock("@app/prices", () => ({ + btcFromUsdMidPriceFn: jest.fn(), + getCurrentPriceAsDisplayPriceRatio: jest.fn(), + usdFromBtcMidPriceFn: jest.fn(), +})) + +jest.mock("@app/wallets", () => { + const { MismatchedCurrencyForWalletError } = jest.requireActual("@domain/errors") + const { WalletCurrency } = jest.requireActual("@domain/shared") + + const validateIsBtcWallet = jest.fn(async () => true) + const validateIsUsdWallet = jest.fn(async (walletId, args) => { + const wallet = await mockFindWalletById(walletId) + if (wallet instanceof Error) return wallet + + if ( + wallet.currency === WalletCurrency.Usd || + (args?.includeUsdt === true && wallet.currency === WalletCurrency.Usdt) + ) { + return true + } + + return new MismatchedCurrencyForWalletError() + }) + + return { validateIsBtcWallet, validateIsUsdWallet } +}) + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + addInvoice: (...args: unknown[]) => mockAddInvoice(...args), + payInvoice: (...args: unknown[]) => mockPayInvoice(...args), + }, +})) + +jest.mock("@services/mongoose", () => ({ + AccountsRepository: jest.fn(() => ({ + findById: (...args: unknown[]) => mockFindAccountById(...args), + })), + WalletsRepository: jest.fn(() => ({ + findById: (...args: unknown[]) => mockFindWalletById(...args), + })), + UsersRepository: jest.fn(), +})) + +jest.mock("@services/dealer-price", () => ({ + DealerPriceService: jest.fn(() => ({})), +})) + +jest.mock("@services/lock", () => ({ + LockService: jest.fn(() => ({})), +})) + +jest.mock("@services/ledger", () => ({ + LedgerService: jest.fn(() => ({})), +})) + +jest.mock("@services/ledger/facade", () => ({})) + +jest.mock("@services/notifications", () => ({ + NotificationsService: jest.fn(() => ({})), +})) + +jest.mock("@services/svix", () => ({ + CallbackService: jest.fn(() => ({})), +})) + +jest.mock("@app/payments/helpers", () => ({ + addContactsAfterSend: jest.fn(), + checkIntraledgerLimits: jest.fn(async () => true), + checkTradeIntraAccountLimits: jest.fn(async () => true), + getPriceRatioForLimits: jest.fn(async () => ({})), +})) + +import { intraledgerPaymentSendWalletIdForUsdWallet } from "@app/payments/send-intraledger" +import { MismatchedCurrencyForWalletError } from "@domain/errors" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +const senderUsdWalletId = "11111111-1111-4111-8111-111111111111" as WalletId +const senderUsdtWalletId = "22222222-2222-4222-8222-222222222222" as WalletId +const recipientUsdWalletId = "33333333-3333-4333-8333-333333333333" as WalletId +const recipientUsdtWalletId = "44444444-4444-4444-8444-444444444444" as WalletId + +const activeAccount = (id: string) => + ({ + id, + status: "active", + level: 1, + }) as unknown as Account + +const wallet = ({ + id, + accountId, + currency, +}: { + id: string + accountId: string + currency: string +}) => + ({ + id, + accountId, + currency, + }) as unknown as Wallet + +describe("intraledgerPaymentSendWalletIdForUsdWallet", () => { + beforeEach(() => { + jest.clearAllMocks() + + mockFindAccountById.mockImplementation(async (accountId: AccountId) => + activeAccount(accountId as string), + ) + mockAddInvoice.mockResolvedValue({ invoice: { bolt11: "lnbc1recipient" } }) + mockPayInvoice.mockResolvedValue({ status: 2 }) + }) + + it("keeps USD to USD using cent amount semantics", async () => { + mockFindWalletById.mockImplementation(async (walletId: WalletId) => { + if (walletId === senderUsdWalletId) { + return wallet({ + id: senderUsdWalletId, + accountId: "sender-account", + currency: WalletCurrency.Usd, + }) + } + return wallet({ + id: recipientUsdWalletId, + accountId: "recipient-account", + currency: WalletCurrency.Usd, + }) + }) + + const result = await intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: senderUsdWalletId, + recipientWalletId: recipientUsdWalletId, + amount: 19446, + memo: "USD intraledger", + }) + + expect(result).toEqual({ value: "success" }) + expect(mockAddInvoice).toHaveBeenCalledWith({ + accountId: recipientUsdWalletId, + amount: expect.any(USDAmount), + memo: "USD intraledger", + }) + expect(mockAddInvoice.mock.calls[0][0].amount.asCents()).toBe("19446") + expect(mockAddInvoice.mock.calls[0][0].amount.toIbex()).toBe(194.46) + expect(mockPayInvoice).toHaveBeenCalledWith({ + accountId: senderUsdWalletId, + invoice: "lnbc1recipient", + }) + }) + + it("sends USDT to USDT using USDT micro-unit amount semantics", async () => { + mockFindWalletById.mockImplementation(async (walletId: WalletId) => { + if (walletId === senderUsdtWalletId) { + return wallet({ + id: senderUsdtWalletId, + accountId: "sender-account", + currency: WalletCurrency.Usdt, + }) + } + return wallet({ + id: recipientUsdtWalletId, + accountId: "recipient-account", + currency: WalletCurrency.Usdt, + }) + }) + + const result = await intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: senderUsdtWalletId, + recipientWalletId: recipientUsdtWalletId, + amount: 19446, + memo: "USDT intraledger", + }) + + expect(result).toEqual({ value: "success" }) + expect(mockAddInvoice).toHaveBeenCalledWith({ + accountId: recipientUsdtWalletId, + amount: expect.any(USDTAmount), + memo: "USDT intraledger", + }) + expect(mockAddInvoice.mock.calls[0][0].amount.asSmallestUnits()).toBe("19446") + expect(mockAddInvoice.mock.calls[0][0].amount.toIbex()).toBe(0.019446) + expect(mockPayInvoice).toHaveBeenCalledWith({ + accountId: senderUsdtWalletId, + invoice: "lnbc1recipient", + }) + }) + + it("rejects USD to USDT as a mixed-currency intraledger payment", async () => { + mockFindWalletById.mockImplementation(async (walletId: WalletId) => { + if (walletId === senderUsdWalletId) { + return wallet({ + id: senderUsdWalletId, + accountId: "sender-account", + currency: WalletCurrency.Usd, + }) + } + return wallet({ + id: recipientUsdtWalletId, + accountId: "recipient-account", + currency: WalletCurrency.Usdt, + }) + }) + + const result = await intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: senderUsdWalletId, + recipientWalletId: recipientUsdtWalletId, + amount: 100, + memo: "mixed currency", + }) + + expect(result).toBeInstanceOf(MismatchedCurrencyForWalletError) + expect(mockAddInvoice).not.toHaveBeenCalled() + expect(mockPayInvoice).not.toHaveBeenCalled() + }) + + it("rejects USDT to USD as a mixed-currency intraledger payment", async () => { + mockFindWalletById.mockImplementation(async (walletId: WalletId) => { + if (walletId === senderUsdtWalletId) { + return wallet({ + id: senderUsdtWalletId, + accountId: "sender-account", + currency: WalletCurrency.Usdt, + }) + } + return wallet({ + id: recipientUsdWalletId, + accountId: "recipient-account", + currency: WalletCurrency.Usd, + }) + }) + + const result = await intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: senderUsdtWalletId, + recipientWalletId: recipientUsdWalletId, + amount: 100, + memo: "mixed currency", + }) + + expect(result).toBeInstanceOf(MismatchedCurrencyForWalletError) + expect(mockAddInvoice).not.toHaveBeenCalled() + expect(mockPayInvoice).not.toHaveBeenCalled() + }) +}) diff --git a/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts b/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts new file mode 100644 index 000000000..d47e1775f --- /dev/null +++ b/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts @@ -0,0 +1,38 @@ +import { toWalletTransactions } from "@app/wallets/get-transactions-for-wallet" +import { WalletCurrency } from "@domain/shared" +import { GResponse200 } from "ibex-client" + +describe("toWalletTransactions", () => { + it("maps IBEX USDT currency id to USDT wallet currency", () => { + const [transaction] = toWalletTransactions([ + { + id: "trx-id", + accountId: "wallet-id", + amount: 19446, + currencyId: 29, + transactionTypeId: 1, + createdAt: "2026-05-13T00:00:00.000Z", + }, + ] as GResponse200) + + expect(transaction.settlementCurrency).toBe(WalletCurrency.Usdt) + expect(transaction.settlementAmount).toBe(19446) + }) + + it("does not silently classify unknown IBEX currency ids as BTC", () => { + const [transaction] = toWalletTransactions([ + { + id: "trx-id", + accountId: "wallet-id", + amount: 100, + currencyId: 999, + transactionTypeId: 1, + createdAt: "2026-05-13T00:00:00.000Z", + }, + ] as GResponse200) + + expect(transaction.settlementCurrency).not.toBe(WalletCurrency.Btc) + expect(transaction.initiationVia.type).toBe("unknown") + expect(transaction.settlementVia.type).toBe("unknown") + }) +}) diff --git a/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts b/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts new file mode 100644 index 000000000..4c2573745 --- /dev/null +++ b/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts @@ -0,0 +1,27 @@ +import { usdWalletAmountFromInput } from "@app/wallets/usd-wallet-amount" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +describe("usdWalletAmountFromInput", () => { + it("treats USD wallet input as cents", () => { + const amount = usdWalletAmountFromInput("19446", WalletCurrency.Usd) + + expect(amount).toBeInstanceOf(USDAmount) + expect((amount as USDAmount).asCents()).toBe("19446") + expect((amount as USDAmount).toIbex()).toBe(194.46) + }) + + it("treats USDT wallet input as micro-USDT", () => { + const amount = usdWalletAmountFromInput("19446", WalletCurrency.Usdt) + + expect(amount).toBeInstanceOf(USDTAmount) + expect((amount as USDTAmount).asSmallestUnits()).toBe("19446") + expect((amount as USDTAmount).asNumber()).toBe("0.019446") + expect((amount as USDTAmount).toIbex()).toBe(0.019446) + }) + + it("rejects BTC", () => { + const amount = usdWalletAmountFromInput("19446", WalletCurrency.Btc) + + expect(amount).toBeInstanceOf(Error) + }) +}) diff --git a/test/flash/unit/domain/payments/index.spec.ts b/test/flash/unit/domain/payments/index.spec.ts index 2aecb5119..95f28c06a 100644 --- a/test/flash/unit/domain/payments/index.spec.ts +++ b/test/flash/unit/domain/payments/index.spec.ts @@ -37,6 +37,15 @@ describe("checkedToBtcPaymentAmount", () => { }) describe("checkedToUsdPaymentAmount", () => { + it("returns USDT payment amounts when called with a USDT wallet currency", () => { + expect(checkedToUsdPaymentAmount(19446, WalletCurrency.Usdt)).toEqual( + expect.objectContaining({ + currency: WalletCurrency.Usdt, + amount: 19446n, + }), + ) + }) + it("errors on null", () => { expect(checkedToUsdPaymentAmount(null)).toBeInstanceOf(InvalidUsdPaymentAmountError) }) diff --git a/test/flash/unit/domain/wallets/onchain-usd-wallet-validator.spec.ts b/test/flash/unit/domain/wallets/onchain-usd-wallet-validator.spec.ts new file mode 100644 index 000000000..241dae0f7 --- /dev/null +++ b/test/flash/unit/domain/wallets/onchain-usd-wallet-validator.spec.ts @@ -0,0 +1,30 @@ +import { AccountStatus } from "@domain/accounts" +import { OnchainUsdPaymentValidator } from "@domain/wallets" +import { USDTAmount, WalletCurrency, isValidated } from "@domain/shared" + +const account = { + id: "account-id" as AccountId, + status: AccountStatus.Active, +} as Account + +const usdtWallet = { + id: "wallet-id" as WalletId, + accountId: account.id, + currency: WalletCurrency.Usdt, +} as Wallet + +describe("Onchain USD wallet payment validation", () => { + it("accepts USDT USD wallets", async () => { + const amount = USDTAmount.smallestUnits("19446") as USDTAmount + + const result = await OnchainUsdPaymentValidator({ + account, + wallet: usdtWallet, + accountId: usdtWallet.id as IbexAccountId, + address: "0xabc" as OnChainAddress, + amount, + }) + + expect(isValidated(result)).toBe(true) + }) +}) diff --git a/test/flash/unit/services/ibex/client-usd-wallet.spec.ts b/test/flash/unit/services/ibex/client-usd-wallet.spec.ts new file mode 100644 index 000000000..0c6dc89e4 --- /dev/null +++ b/test/flash/unit/services/ibex/client-usd-wallet.spec.ts @@ -0,0 +1,135 @@ +const mockAddInvoice = jest.fn() +const mockGetFeeEstimation = jest.fn() +const mockEstimateFeeV2 = jest.fn() + +jest.mock("@services/ibex/cache", () => ({ + Redis: { + get: jest.fn(), + set: jest.fn(), + delete: jest.fn(), + }, +})) + +jest.mock("ibex-client", () => { + class AuthenticationError extends Error {} + class ApiError extends Error {} + class UnexpectedResponseError extends Error {} + class IbexClientError extends Error {} + + return { + __esModule: true, + default: jest.fn().mockImplementation(() => ({ + authentication: { + storage: { + getAccessToken: jest.fn(), + setAccessToken: jest.fn(), + setRefreshToken: jest.fn(), + }, + }, + addInvoice: (...args: unknown[]) => mockAddInvoice(...args), + getFeeEstimation: (...args: unknown[]) => mockGetFeeEstimation(...args), + estimateFeeV2: (...args: unknown[]) => mockEstimateFeeV2(...args), + })), + AuthenticationError, + ApiError, + UnexpectedResponseError, + IbexClientError, + } +}) + +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import Ibex from "@services/ibex/client" + +describe("IBEX USD wallet amount handling", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("creates USDT invoices using decimal USDT amount", async () => { + const amount = USDTAmount.smallestUnits("19446") as USDTAmount + mockAddInvoice.mockResolvedValue({ invoice: { bolt11: "lnbc1" } }) + + await Ibex.addInvoice({ + accountId: "ibex-usdt-account" as IbexAccountId, + amount, + memo: "usdt invoice", + }) + + expect(mockAddInvoice).toHaveBeenCalledWith( + expect.objectContaining({ + accountId: "ibex-usdt-account", + amount: 0.019446, + }), + ) + }) + + it("estimates LN fees with USDT currency id and parses USDT amounts", async () => { + const amount = USDTAmount.smallestUnits("19446") as USDTAmount + mockGetFeeEstimation.mockResolvedValue({ amount: 0.000123, invoiceAmount: 0.019446 }) + + const result = await Ibex.getLnFeeEstimation({ + invoice: "lnbc1" as Bolt11, + send: amount, + }) + + expect(mockGetFeeEstimation).toHaveBeenCalledWith({ + bolt11: "lnbc1", + amount: "0.019446", + currencyId: "29", + }) + expect(result).not.toBeInstanceOf(Error) + expect((result as { fee: USDTAmount }).fee).toBeInstanceOf(USDTAmount) + expect((result as { fee: USDTAmount }).fee.asSmallestUnits()).toBe("123") + expect((result as { invoice: USDTAmount }).invoice.asSmallestUnits()).toBe("19446") + }) + + it("estimates fixed-amount LN fees with USDT currency id when no send amount is provided", async () => { + mockGetFeeEstimation.mockResolvedValue({ amount: 0.000123, invoiceAmount: 0.019446 }) + + const result = await Ibex.getLnFeeEstimation({ + invoice: "lnbc1" as Bolt11, + currency: WalletCurrency.Usdt, + }) + + expect(mockGetFeeEstimation).toHaveBeenCalledWith({ + bolt11: "lnbc1", + amount: undefined, + currencyId: "29", + }) + expect(result).not.toBeInstanceOf(Error) + expect((result as { fee: USDTAmount }).fee).toBeInstanceOf(USDTAmount) + expect((result as { fee: USDTAmount }).fee.asSmallestUnits()).toBe("123") + }) + + it("keeps USD LN fee estimation behavior unchanged", async () => { + const amount = USDAmount.cents("19446") as USDAmount + mockGetFeeEstimation.mockResolvedValue({ amount: 0.12, invoiceAmount: 194.46 }) + + const result = await Ibex.getLnFeeEstimation({ + invoice: "lnbc1" as Bolt11, + send: amount, + }) + + expect(mockGetFeeEstimation).toHaveBeenCalledWith({ + bolt11: "lnbc1", + amount: "194.46", + currencyId: "3", + }) + expect(result).not.toBeInstanceOf(Error) + expect((result as { fee: USDAmount }).fee).toBeInstanceOf(USDAmount) + expect((result as { fee: USDAmount }).fee.asCents()).toBe("12") + }) + + it("estimates on-chain fees with USDT currency id", async () => { + const amount = USDTAmount.smallestUnits("19446") as USDTAmount + mockEstimateFeeV2.mockResolvedValue({ fee: 0.000123 }) + + await Ibex.estimateOnchainFee(amount, "0xabc" as OnChainAddress) + + expect(mockEstimateFeeV2).toHaveBeenCalledWith({ + "amount": 0.019446, + "currency-id": "29", + "address": "0xabc", + }) + }) +}) From 6e15c4c58f0f6e5412074354f35f496040d0dcf4 Mon Sep 17 00:00:00 2001 From: Forge0x Date: Tue, 19 May 2026 12:16:55 -0400 Subject: [PATCH 12/43] fix(graphql): serialize USDT fee probe amounts (#358) --- src/graphql/shared/types/scalar/usd-cents.ts | 5 ++++- .../shared/types/scalar/usd-cents.spec.ts | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts diff --git a/src/graphql/shared/types/scalar/usd-cents.ts b/src/graphql/shared/types/scalar/usd-cents.ts index c285f546b..d9e505d0e 100644 --- a/src/graphql/shared/types/scalar/usd-cents.ts +++ b/src/graphql/shared/types/scalar/usd-cents.ts @@ -1,4 +1,4 @@ -import { USDAmount } from "@domain/shared" +import { USDAmount, USDTAmount } from "@domain/shared" import { GT } from "@graphql/index" const USDCentsScalar = GT.Scalar({ @@ -16,6 +16,9 @@ const USDCentsScalar = GT.Scalar({ if (value instanceof USDAmount) { return Number(value.asCents(2)) } + if (value instanceof USDTAmount) { + return Number(value.asSmallestUnits()) + } else throw new Error(`Failed to serialize USDAmount: ${value}`) } }) diff --git a/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts b/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts new file mode 100644 index 000000000..497ab3903 --- /dev/null +++ b/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts @@ -0,0 +1,18 @@ +import { USDAmount, USDTAmount } from "@domain/shared" +import USDCentsScalar from "@graphql/shared/types/scalar/usd-cents" + +describe("USDCentsScalar", () => { + it("serializes USD amounts as cents", () => { + const amount = USDAmount.cents("123") + if (amount instanceof Error) throw amount + + expect(USDCentsScalar.serialize(amount)).toBe(123) + }) + + it("serializes USDT amounts as micro-USDT units", () => { + const amount = USDTAmount.smallestUnits("1234567") + if (amount instanceof Error) throw amount + + expect(USDCentsScalar.serialize(amount)).toBe(1234567) + }) +}) From e2fb12eabc13116ff83161153cc1ac1ac4dee7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olaniran=20=E2=9A=A1?= <93789719+heyolaniran@users.noreply.github.com> Date: Fri, 22 May 2026 00:45:29 +0100 Subject: [PATCH 13/43] Feat/bridge reconciliation (#360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add timeout for Bridge request * feat(bridge): ENG-350 — reset withdrawal state on transfer.failed and surface failure reason * feat(bridge): ENG-350 harden transfer webhook edge cases and notify users * feat(bridge): ENG-350 introduce full KYC status lifecycle with offboarded support * refactor(bridge): rename BridgeDepositLog model to BridgeDeposits * refactor(ibex): rename IbexCryptoReceiveLog model to IbexCryptoReceive * refactor(bridge): rename BridgeReplayLog model to BridgeReplay * fix(bridge) : ENG-289 timeout increase to 15s * feat: adding full kyc status to reflect them stored as bridgeKycStatus in mongoDB instead of just pending state * feat: Kyc status * fix: Duplicated webhook event message * fix: Duplicated webhook deposit event message * fix: Virtual Account Edge case --- dev/config/base-config.yaml | 3 +- .../bridge/send-withdrawal-notification.ts | 112 ++++++++++ src/config/locales/en.json | 11 + src/config/locales/es.json | 11 + src/config/schema.ts | 2 + src/config/schema.types.d.ts | 1 + src/domain/accounts/index.types.d.ts | 20 +- src/graphql/error-map.ts | 8 + src/graphql/public/schema.graphql | 1 + .../public/types/object/bridge-withdrawal.ts | 1 + src/services/bridge/client.ts | 87 +++++--- src/services/bridge/errors.ts | 12 + src/services/bridge/index.ts | 210 ++++++++++++++++-- src/services/bridge/index.types.d.ts | 50 ++++- src/services/bridge/reconciliation.ts | 12 +- .../bridge/webhook-server/routes/deposit.ts | 25 ++- .../bridge/webhook-server/routes/kyc.ts | 84 ++++++- .../bridge/webhook-server/routes/replay.ts | 37 ++- .../bridge/webhook-server/routes/transfer.ts | 124 +++++++++-- .../webhook-server/transfer-direction.ts | 29 +++ .../webhook-server/routes/crypto-receive.ts | 4 +- src/services/mongoose/accounts.ts | 4 +- src/services/mongoose/bridge-accounts.ts | 33 ++- src/services/mongoose/bridge-deposit-log.ts | 6 +- src/services/mongoose/bridge-replay-log.ts | 10 +- .../mongoose/ibex-crypto-receive-log.ts | 10 +- src/services/mongoose/schema.ts | 34 +-- src/services/mongoose/schema.types.d.ts | 2 +- .../send-withdrawal-notification.spec.ts | 122 ++++++++++ .../bridge/webhook-server/replay.spec.ts | 32 +++ .../bridge/webhook-server/transfer.spec.ts | 189 ++++++++++++++++ 31 files changed, 1136 insertions(+), 150 deletions(-) create mode 100644 src/app/bridge/send-withdrawal-notification.ts create mode 100644 src/services/bridge/webhook-server/transfer-direction.ts create mode 100644 test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts create mode 100644 test/flash/unit/services/bridge/webhook-server/transfer.spec.ts diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index cc4a36249..fc232cf4c 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -16,7 +16,8 @@ bridge: enabled: true apiKey: "sk-test-3bd6463c9cd77c3d8858c60b9997d0c6" baseUrl: "https://api.sandbox.bridge.xyz/v0" - minWithdrawalAmount: 10 + minWithdrawalAmount: 2 + timeoutMs: 15000 webhook: port: 4009 replaySecret: "also-not-so-secret" diff --git a/src/app/bridge/send-withdrawal-notification.ts b/src/app/bridge/send-withdrawal-notification.ts new file mode 100644 index 000000000..8adf90f30 --- /dev/null +++ b/src/app/bridge/send-withdrawal-notification.ts @@ -0,0 +1,112 @@ +import { getI18nInstance } from "@config" +import { checkedToAccountId } from "@domain/accounts" +import { getLanguageOrDefault } from "@domain/locale" +import { + DeviceTokensNotRegisteredNotificationsServiceError, + FlashNotificationCategories, + NotificationsServiceError, +} from "@domain/notifications" +import { removeDeviceTokens } from "@app/users/remove-device-tokens" +import { baseLogger } from "@services/logger" +import { AccountsRepository } from "@services/mongoose/accounts" +import { UsersRepository } from "@services/mongoose/users" +import { + PushNotificationsService, + SendFilteredPushNotificationStatus, +} from "@services/notifications/push-notifications" + +const i18n = getI18nInstance() + +const formatWithdrawalAmount = (amount: string, currency: string): string => + `${amount} ${currency.toUpperCase()}` + +export type BridgeWithdrawalNotificationOutcome = "completed" | "failed" + +export const sendBridgeWithdrawalNotification = async ({ + accountId: accountIdRaw, + amount, + currency, + outcome, + failureReason, +}: { + accountId: string + amount: string + currency: string + outcome: BridgeWithdrawalNotificationOutcome + failureReason?: string +}): Promise => { + const accountId = checkedToAccountId(accountIdRaw) + if (accountId instanceof Error) return accountId + + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + + const user = await UsersRepository().findById(account.kratosUserId) + if (user instanceof Error) return user + + const locale = getLanguageOrDefault(user.language) + const formattedAmount = formatWithdrawalAmount(amount, currency) + const phraseBase = `notification.bridgeWithdrawal.${outcome}` + + const title = i18n.__({ phrase: `${phraseBase}.title`, locale }) + const bodyPhrase = + outcome === "failed" && failureReason + ? `${phraseBase}.bodyWithReason` + : `${phraseBase}.body` + const body = i18n.__( + { phrase: bodyPhrase, locale }, + { + amount: formattedAmount, + reason: failureReason ?? "", + }, + ) + + const result = await PushNotificationsService().sendFilteredNotification({ + deviceTokens: user.deviceTokens, + title, + body, + notificationCategory: FlashNotificationCategories.Cashout, + notificationSettings: account.notificationSettings, + data: { + type: `bridge_withdrawal_${outcome}`, + amount, + currency, + ...(failureReason ? { failureReason } : {}), + }, + }) + + if (result instanceof NotificationsServiceError) return result + + if (result.status === SendFilteredPushNotificationStatus.Filtered) { + return true + } + + return true +} + +export const sendBridgeWithdrawalNotificationBestEffort = async ( + args: Parameters[0], +): Promise => { + const result = await sendBridgeWithdrawalNotification(args) + + if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) { + const accountId = checkedToAccountId(args.accountId) + if (accountId instanceof Error) return + + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return + + await removeDeviceTokens({ + userId: account.kratosUserId, + deviceTokens: result.tokens, + }) + return + } + + if (result instanceof Error) { + baseLogger.warn( + { accountId: args.accountId, outcome: args.outcome, error: result }, + "Failed to send Bridge withdrawal push notification", + ) + } +} diff --git a/src/config/locales/en.json b/src/config/locales/en.json index c9bd32b95..55eafc82d 100644 --- a/src/config/locales/en.json +++ b/src/config/locales/en.json @@ -40,6 +40,17 @@ "cashout": { "body": "Your cashout of {{amount}} has been deposited to your bank account.", "title": "Cashout" + }, + "bridgeWithdrawal": { + "completed": { + "body": "Your withdrawal of {{amount}} has been sent to your bank account.", + "title": "Withdrawal complete" + }, + "failed": { + "body": "Your withdrawal of {{amount}} could not be completed.", + "bodyWithReason": "Your withdrawal of {{amount}} could not be completed: {{reason}}.", + "title": "Withdrawal failed" + } } } } \ No newline at end of file diff --git a/src/config/locales/es.json b/src/config/locales/es.json index 122b16b47..7325c9fe0 100644 --- a/src/config/locales/es.json +++ b/src/config/locales/es.json @@ -36,6 +36,17 @@ "bodyDisplayCurrency": "+{{baseCurrencyAmount}}{{baseCurrencyName}} ({{displayCurrencyAmount}})", "title": "Transacción {{walletCurrency}}" } + }, + "bridgeWithdrawal": { + "completed": { + "body": "Su retiro de {{amount}} se envió a su cuenta bancaria.", + "title": "Retiro completado" + }, + "failed": { + "body": "No se pudo completar su retiro de {{amount}}.", + "bodyWithReason": "No se pudo completar su retiro de {{amount}}: {{reason}}.", + "title": "Retiro fallido" + } } } } diff --git a/src/config/schema.ts b/src/config/schema.ts index c97482021..72f08a6f8 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -648,6 +648,8 @@ export const configSchema = { enabled: { type: "boolean" }, apiKey: { type: "string" }, baseUrl: { type: "string" }, + minWithdrawalAmount: { type: "number" }, + timeoutMs: { type: "integer", default: 10000 }, webhook: { type: "object", properties: { diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index f13477a2d..dad0fb31c 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -47,6 +47,7 @@ type BridgeConfig = { apiKey: string baseUrl: string minWithdrawalAmount: number + timeoutMs?: number webhook: BridgeWebhook } diff --git a/src/domain/accounts/index.types.d.ts b/src/domain/accounts/index.types.d.ts index 3fed86428..1b39d3c32 100644 --- a/src/domain/accounts/index.types.d.ts +++ b/src/domain/accounts/index.types.d.ts @@ -88,7 +88,7 @@ type Account = { erpParty?: string // Lookup key to Customer in ERPNext. Required for Account level > 1 // Bridge integration: bridgeCustomerId?: BridgeCustomerId - bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeKycStatus?: "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" bridgeEthereumAddress?: string } @@ -135,10 +135,10 @@ type LimitsCheckerFn = (args: LimiterCheckInputs) => Promise[]) => Promise< | { - volumeTotalLimit: UsdPaymentAmount - volumeUsed: UsdPaymentAmount - volumeRemaining: UsdPaymentAmount - } + volumeTotalLimit: UsdPaymentAmount + volumeUsed: UsdPaymentAmount + volumeRemaining: UsdPaymentAmount + } | ValidationError > @@ -150,10 +150,10 @@ type AccountLimitsChecker = { type AccountLimitsVolumes = | { - volumesIntraledger: LimitsVolumesFn - volumesWithdrawal: LimitsVolumesFn - volumesTradeIntraAccount: LimitsVolumesFn - } + volumesIntraledger: LimitsVolumesFn + volumesWithdrawal: LimitsVolumesFn + volumesTradeIntraAccount: LimitsVolumesFn + } | ValidationError type AccountValidator = { @@ -179,7 +179,7 @@ interface IAccountsRepository { id: AccountId, fields: { bridgeCustomerId?: BridgeCustomerId - bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeKycStatus?: "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" bridgeEthereumAddress?: string }, ): Promise diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index c53a4b37f..9f93e8875 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -501,6 +501,10 @@ export const mapError = (error: ApplicationError): CustomApolloError => { message = "KYC verification was rejected" return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeKycOffboardedError": + message = "Your account has been offboarded from Bridge. Please contact support." + return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeCustomerNotFoundError": message = "Bridge customer not found" return new ValidationInternalError({ message, logger: baseLogger }) @@ -517,6 +521,10 @@ export const mapError = (error: ApplicationError): CustomApolloError => { message = "Request timed out" return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeTransferFailedError": + message = error.message || "Transfer failed" + return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeWebhookValidationError": message = "Invalid webhook signature" return new ValidationInternalError({ message, logger: baseLogger }) diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index 0ed531044..e2f607717 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -300,6 +300,7 @@ type BridgeWithdrawal { amount: String! createdAt: String! currency: String! + failureReason: String id: ID! status: String! } diff --git a/src/graphql/public/types/object/bridge-withdrawal.ts b/src/graphql/public/types/object/bridge-withdrawal.ts index f29ba250d..0c6298215 100644 --- a/src/graphql/public/types/object/bridge-withdrawal.ts +++ b/src/graphql/public/types/object/bridge-withdrawal.ts @@ -7,6 +7,7 @@ const BridgeWithdrawal = GT.Object({ amount: { type: GT.NonNull(GT.String) }, currency: { type: GT.NonNull(GT.String) }, status: { type: GT.NonNull(GT.String) }, + failureReason: { type: GT.String }, createdAt: { type: GT.NonNull(GT.String) }, }), }) diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index e5503fccf..d6d07db65 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -7,7 +7,8 @@ import crypto from "crypto" import { BridgeConfig } from "@config" -import { BridgeCustomerId, BridgeTransferId } from "@domain/primitives/bridge" +import { BridgeCustomerId, BridgeTransferId, BridgeVirtualAccountId } from "@domain/primitives/bridge" +import { BridgeTimeoutError } from "./errors" // ============ Error Handling ============ @@ -66,15 +67,15 @@ export interface Customer { id: string type: "individual" | "business" status?: - | "active" - | "awaiting_questionnaire" - | "rejected" - | "paused" - | "under_review" - | "offboarded" - | "awaiting_ubo" - | "incomplete" - | "not_started" + | "active" + | "awaiting_questionnaire" + | "rejected" + | "paused" + | "under_review" + | "offboarded" + | "awaiting_ubo" + | "incomplete" + | "not_started" has_accepted_terms_of_service?: string created_at: string updated_at: string @@ -365,23 +366,37 @@ export class BridgeClient { } } - const response = await fetch(url, { - method, - headers, - body: body ? JSON.stringify(body) : undefined, - }) - - const responseData = await response.json().catch(() => null) + const timeoutMs = BridgeConfig.timeoutMs ?? 15_000 + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), timeoutMs) + + try { + const response = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + signal: controller.signal, + }) + + const responseData = await response.json().catch(() => null) + + if (!response.ok) { + throw new BridgeApiError( + `Bridge API error: ${response.status} ${response.statusText}`, + response.status, + responseData, + ) + } - if (!response.ok) { - throw new BridgeApiError( - `Bridge API error: ${response.status} ${response.statusText}`, - response.status, - responseData, - ) + return responseData as T + } catch (err) { + if (err instanceof Error && err.name === "AbortError") { + throw new BridgeTimeoutError() + } + throw err + } finally { + clearTimeout(timeoutId) } - - return responseData as T } // ============ Customers ============ @@ -425,6 +440,28 @@ export class BridgeClient { ) } + async getVirtualAccount( + customerId: BridgeCustomerId, virtualAccountId: BridgeVirtualAccountId, idempotencyKey?: string, + ): Promise { + return this.request( + "GET", + `/customers/${customerId}/virtual_accounts/${virtualAccountId}`, + undefined, + idempotencyKey, + ) + } + + + async getVirtualAccountByCustomerId(customerId: BridgeCustomerId): Promise { + const response = await this.request<{ data: VirtualAccount[] }>( + "GET", + `/customers/${customerId}/virtual_accounts`, + ) + + return response.data as VirtualAccount[] + + + } // ============ External Accounts ============ async createExternalAccount( diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index 6f6572b0c..3cef54938 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -45,6 +45,12 @@ export class BridgeKycRejectedError extends BridgeError { } } +export class BridgeKycOffboardedError extends BridgeError { + constructor(message: string = "Your account has been offboarded from Bridge. Please contact support.") { + super(message) + } +} + export class BridgeInsufficientFundsError extends BridgeError { constructor(message: string = "Insufficient funds for withdrawal") { super(message) @@ -77,6 +83,12 @@ export class BridgeDisabledError extends BridgeError { } } +export class BridgeTransferFailedError extends BridgeError { + constructor(reason: string = "Transfer failed") { + super(reason) + } +} + export class BridgeWebhookValidationError extends BridgeError { constructor(message: string = "Invalid webhook signature") { super(message) diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index f5f51e2da..ba0fab74e 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -14,7 +14,7 @@ import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" import { baseLogger } from "@services/logger" import { RepositoryError } from "@domain/errors" -import { toBridgeCustomerId } from "@domain/primitives/bridge" +import { toBridgeCustomerId, toBridgeVirtualAccountId } from "@domain/primitives/bridge" import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" import { USDTAmount, WalletCurrency } from "@domain/shared" import { WalletType } from "@domain/wallets" @@ -30,9 +30,11 @@ import { BridgeAccountLevelError, BridgeKycPendingError, BridgeKycRejectedError, + BridgeKycOffboardedError, BridgeCustomerNotFoundError, } from "./errors" -import BridgeApiClient from "./client" +import BridgeApiClient, { BridgeClient } from "./client" +import { BridgeVirtualAccount } from "@services/mongoose/schema" // ============ Types ============ @@ -70,7 +72,7 @@ type WithdrawalResult = { createdAt: string } -type KycStatusResult = "pending" | "approved" | "rejected" | null +type KycStatusResult = "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" | null type VirtualAccountResult = { bridgeVirtualAccountId: string @@ -199,7 +201,7 @@ const initiateKyc = async ({ const updateResult = await AccountsRepository().updateBridgeFields(accountId, { bridgeCustomerId: customerId, - bridgeKycStatus: "pending", + bridgeKycStatus: "open", }) if (updateResult instanceof Error) { @@ -216,6 +218,14 @@ const initiateKyc = async ({ const bridgeError = error as { statusCode?: number; response?: { existing_kyc_link?: { kyc_link: string; customer_id: string; tos_link: string } } } if (bridgeError?.statusCode === 400 && bridgeError.response?.existing_kyc_link) { + + // store the customer id and the kyc status + const customerId = toBridgeCustomerId(bridgeError.response.existing_kyc_link.customer_id) + const updateResult = await AccountsRepository().updateBridgeFields(accountId, { + bridgeCustomerId: customerId, + bridgeKycStatus: "not_started", + }) + return { kycLink: bridgeError.response.existing_kyc_link.kyc_link, customerId: bridgeError.response.existing_kyc_link.customer_id, @@ -252,22 +262,53 @@ const createVirtualAccount = async ( const account = await checkAccountLevel(accountId) if (account instanceof Error) return account + + const PENDING_BRIDGE_STATUSES = new Set([ + "incomplete", + "awaiting_questionnaire", + "awaiting_ubo", + "under_review", + "paused", + ]) + try { - const customerId = account.bridgeCustomerId - if (!customerId) { + + if (!account.bridgeCustomerId) { return new BridgeCustomerNotFoundError( "Account has no Bridge customer ID. Complete KYC first.", ) } + const customerId = toBridgeCustomerId(account.bridgeCustomerId) + if (!customerId && !account.bridgeKycStatus) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + const customer = await BridgeApiClient.getCustomer(customerId); + + if (customer instanceof Error) { + baseLogger.error( + { accountId, error: customer }, + "Failed to retrieve Bridge customer status" + ) + return customer + } + + let kycStatus = customer.status + // Check KYC status - if (account.bridgeKycStatus === "pending") { - return new BridgeKycPendingError() + if (kycStatus === "offboarded") { + return new BridgeKycOffboardedError() } - if (account.bridgeKycStatus === "rejected") { + if (kycStatus === "rejected") { return new BridgeKycRejectedError() } - if (account.bridgeKycStatus !== "approved") { + if (PENDING_BRIDGE_STATUSES.has(kycStatus!) || kycStatus as string === "open") { + return new BridgeKycPendingError() + } + if (kycStatus !== "active" && kycStatus as string !== "approved") { return new BridgeKycPendingError("KYC not yet completed") } @@ -310,12 +351,7 @@ const createVirtualAccount = async ( ethereumAddress = receiveInfo.data.address } - // Deterministic key so Bridge deduplicates on their side if two calls race past - // the check above before either has written to the repo. - const vaIdempotencyKey = crypto - .createHash("sha256") - .update(`va:${accountId}`) - .digest("hex") + const vaIdempotencyKey = `${accountId}:${crypto.randomUUID()}` // Create Bridge virtual account const virtualAccount = await BridgeApiClient.createVirtualAccount( @@ -593,14 +629,123 @@ const getKycStatus = async (accountId: AccountId): Promise va.destination.address === account.bridgeEthereumAddress) + + if (relatedVa?.status === "activated") { + + // if there's a related VA on Bridge side but it's not in our repo, create it in our repo to keep them in sync + if (existingVa instanceof RepositoryError) { + const repoResult = await BridgeAccountsRepo.createVirtualAccount({ + accountId: accountId as string, + bridgeVirtualAccountId: relatedVa.id, + bankName: relatedVa.source_deposit_instructions.bank_name || "", + routingNumber: relatedVa.source_deposit_instructions.bank_routing_number || "", + accountNumber: relatedVa.source_deposit_instructions.bank_account_number || "", + accountNumberLast4: relatedVa.source_deposit_instructions.bank_account_number?.slice(-4) || "", + }) + if (repoResult instanceof Error) { + baseLogger.error( + { accountId, operation: "getKycStatus", error: repoResult }, + "Failed to create virtual account in repo after KYC approval", + ) + } else { + baseLogger.info( + { accountId, operation: "getKycStatus", virtualAccountId: relatedVa.id }, + "Proactively updated virtual account in repo after KYC approval", + ) + } + } + } else { + + const vaResult = await createVirtualAccount(accountId) + if (vaResult instanceof Error) { + baseLogger.error( + { accountId, operation: "getKycStatus", error: vaResult }, + "Failed to create virtual account after KYC approval", + ) + } else { + baseLogger.info( + { accountId, operation: "getKycStatus", virtualAccountId: vaResult.virtualAccountId }, + "Proactively created virtual account after KYC approval", + ) + } + } + } + + baseLogger.info( + { accountId, operation: "getKycStatus", kycStatus: kycStatus }, + "Bridge operation completed", + ) + + return kycStatus + } catch (error) { + baseLogger.error( + { accountId, operation: "getKycStatus", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } + + } /** @@ -634,6 +779,27 @@ const getVirtualAccount = async ( return null } + // check if the virtual account still exists on Bridge side - if not, delete it from our repo and return null + const bridgeVa = await BridgeApiClient.getVirtualAccount(account.bridgeCustomerId!, toBridgeVirtualAccountId(virtualAccount.bridgeVirtualAccountId!)) + + if (bridgeVa instanceof Error) { + return new BridgeError(`Failed to retrieve virtual account from Bridge: ${bridgeVa.message}`) + } + + // check if the virtual account is still activated on Bridge side - if not, delete it from our repo and return null + if (bridgeVa.status !== "activated") { + + // delete the virtual account from our repo since it's no longer valid + + const deleteResult = await BridgeVirtualAccount.deleteOne({ + bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId! as string + }) + + + return null + } + + const result: VirtualAccountResult = { bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId!, bankName: virtualAccount.bankName, diff --git a/src/services/bridge/index.types.d.ts b/src/services/bridge/index.types.d.ts index 430e4b601..728ee499e 100644 --- a/src/services/bridge/index.types.d.ts +++ b/src/services/bridge/index.types.d.ts @@ -48,7 +48,20 @@ interface BridgeTransfer { readonly destinationAccountId: BridgeVirtualAccountId | BridgeExternalAccountId readonly amount: number readonly currency: string - readonly status: "pending" | "processing" | "completed" | "failed" | "cancelled" + readonly status: + | "awaiting_funds" + | "in_review" + | "funds_received" + | "payment_submitted" + | "payment_processed" + | "undeliverable" + | "returned" + | "refund_in_flight" + | "refunded" + | "refund_failed" + | "missing_return_policy" + | "error" + | "canceled" readonly description?: string readonly createdAt: string readonly updatedAt: string @@ -95,21 +108,43 @@ interface BridgeDepositCompletedEvent extends BridgeWebhookEvent { interface BridgeTransferCompletedEvent extends BridgeWebhookEvent { readonly type: "transfer.completed" readonly data: { - readonly transferId: BridgeTransferId + readonly transfer_id: BridgeTransferId readonly customerId: BridgeCustomerId - readonly amount: number + readonly state: "payment_processed" + readonly amount: string + readonly currency: string + } +} + +interface BridgeTransferPaymentProcessedEvent extends BridgeWebhookEvent { + readonly type: "transfer.payment_processed" + readonly data: { + readonly transfer_id: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly state: "payment_processed" + readonly amount: string readonly currency: string - readonly completedAt: string } } interface BridgeTransferFailedEvent extends BridgeWebhookEvent { readonly type: "transfer.failed" readonly data: { - readonly transferId: BridgeTransferId + readonly transfer_id: BridgeTransferId readonly customerId: BridgeCustomerId - readonly reason: string - readonly failedAt: string + readonly state: + | "undeliverable" + | "returned" + | "refunded" + | "refund_in_flight" + | "refund_failed" + | "missing_return_policy" + | "error" + | "canceled" + readonly reason?: string + readonly return_reason?: string + readonly amount: string + readonly currency: string } } @@ -118,4 +153,5 @@ type BridgeWebhookEventType = | BridgeKycRejectedEvent | BridgeDepositCompletedEvent | BridgeTransferCompletedEvent + | BridgeTransferPaymentProcessedEvent | BridgeTransferFailedEvent diff --git a/src/services/bridge/reconciliation.ts b/src/services/bridge/reconciliation.ts index 891229e10..851ebce90 100644 --- a/src/services/bridge/reconciliation.ts +++ b/src/services/bridge/reconciliation.ts @@ -1,10 +1,10 @@ import { baseLogger } from "@services/logger" -import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" +import { findIbexCryptoReceivesSince } from "@services/mongoose/ibex-crypto-receive-log" import { upsertBridgeReconciliationOrphan, resolveOrphansByTxHash, } from "@services/mongoose/bridge-reconciliation-orphan" -import { BridgeDepositLog, IbexCryptoReceiveLog } from "@services/mongoose/schema" +import { BridgeDeposits, IbexCryptoReceive } from "@services/mongoose/schema" import { PubSubService } from "@services/pubsub" import { PubSubDefaultTriggers } from "@domain/pubsub" @@ -50,14 +50,14 @@ export const reconcileBridgeAndIbexDeposits = async ({ const now = new Date() const since = new Date(now.getTime() - windowMs) - const bridgeDeposits = (await BridgeDepositLog.find({ + const bridgeDeposits = (await BridgeDeposits.find({ createdAt: { $gte: since, $lte: now }, state: "payment_processed", }) .lean() .exec()) as BridgeDepositLike[] - const ibexReceivesResult = await findIbexCryptoReceiveLogsSince({ since, until: now }) + const ibexReceivesResult = await findIbexCryptoReceivesSince({ since, until: now }) if (ibexReceivesResult instanceof Error) return ibexReceivesResult const ibexReceives = ibexReceivesResult as IbexReceiveLike[] @@ -180,13 +180,13 @@ export const reconcileByTxHash = async ({ try { const [bridgeDeposit, ibexReceive] = await Promise.all([ - BridgeDepositLog.findOne({ + BridgeDeposits.findOne({ destinationTxHash: { $regex: new RegExp(`^${normalizedHash}$`, "i") }, state: "payment_processed", }) .lean() .exec(), - IbexCryptoReceiveLog.findOne({ + IbexCryptoReceive.findOne({ txHash: { $regex: new RegExp(`^${normalizedHash}$`, "i") }, }) .lean() diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index a87352b1a..c5d02b4a1 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -9,25 +9,17 @@ import { Request, Response } from "express" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" -import { createBridgeDepositLog } from "@services/mongoose/bridge-deposit-log" +import { createBridgeDeposit } from "@services/mongoose/bridge-deposit-log" import { reconcileByTxHash } from "@services/bridge/reconciliation" export const depositHandler = async (req: Request, res: Response) => { const { event_id, event_object } = req.body const { id, state, amount, currency, on_behalf_of, receipt } = event_object ?? {} - if (!id || !event_id) { + if (!id || !event_id || !amount || !on_behalf_of) { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency: lock on the transfer id + state so each state transition is processed once - const lockKey = `bridge-deposit:${id}:${state}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) - if (lockResult instanceof Error) { - baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") - return res.status(200).json({ status: "already_processed" }) - } - try { baseLogger.info( { @@ -48,10 +40,10 @@ export const depositHandler = async (req: Request, res: Response) => { "Bridge deposit event", ) - const depositLog = await createBridgeDepositLog({ + const depositLog = await createBridgeDeposit({ eventId: event_id, transferId: id, - customerId: on_behalf_of ?? "", + customerId: on_behalf_of, state, amount: String(amount), currency, @@ -78,6 +70,15 @@ export const depositHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to persist deposit log" }) } + // Idempotency: lock on event_id after successful DB write — aligns with the DB unique + // constraint on eventId and ensures Bridge retries are not blocked on transient failures + const lockKey = `bridge-deposit:${event_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) + if (lockResult instanceof Error) { + baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") + return res.status(200).json({ status: "already_processed" }) + } + if (state === "payment_processed" && receipt?.destination_tx_hash) { reconcileByTxHash({ txHash: receipt.destination_tx_hash }).catch((err) => baseLogger.error({ err, event_id, id }, "Real-time reconciliation failed"), diff --git a/src/services/bridge/webhook-server/routes/kyc.ts b/src/services/bridge/webhook-server/routes/kyc.ts index 2706b7499..965b38c1f 100644 --- a/src/services/bridge/webhook-server/routes/kyc.ts +++ b/src/services/bridge/webhook-server/routes/kyc.ts @@ -1,6 +1,14 @@ /** * Bridge KYC Webhook Handler - * Handles kyc.approved and kyc.rejected events from Bridge.xyz + * Handles all kyc.* events from Bridge.xyz + * + * Bridge kyc_status → internal bridgeKycStatus mapping: + * not_started → "open" + * incomplete | awaiting_questionnaire | awaiting_ubo + * | under_review | paused → "pending" + * approved → "approved" + * rejected → "rejected" + * offboarded → "offboarded" */ import { Request, Response } from "express" @@ -18,14 +26,6 @@ export const kycHandler = async (req: Request, res: Response) => { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency check using customer_id + event as lock key - const lockKey = `bridge-kyc:${customer_id}:${event_object.kyc_status}:${event_object.id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) - if (lockResult instanceof Error) { - baseLogger.info({ customer_id, event_id }, "Duplicate Bridge KYC webhook") - return res.status(200).json({ status: "already_processed" }) - } - try { const bridgeCustomerId = toBridgeCustomerId(customer_id) const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) @@ -37,8 +37,55 @@ export const kycHandler = async (req: Request, res: Response) => { return res.status(503).json({ error: "Account not ready" }) } + // Idempotency check — acquire lock after account is found so 503 retries are not blocked + const lockKey = `bridge-kyc:${event_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) + if (lockResult instanceof Error) { + baseLogger.info({ customer_id, event_id }, "Duplicate Bridge KYC webhook") + return res.status(200).json({ status: "already_processed" }) + } + + const PENDING_BRIDGE_STATUSES = new Set([ + "incomplete", + "awaiting_questionnaire", + "awaiting_ubo", + "under_review", + "paused", + ]) + // Update KYC status based on event - if (kyc_status === "approved") { + if (kyc_status === "not_started") { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: "not_started", + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.info({ accountId: account.id, customer_id }, "Bridge KYC not started") + } else if (PENDING_BRIDGE_STATUSES.has(kyc_status)) { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: kyc_status, + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.info( + { accountId: account.id, customer_id, kyc_status }, + "Bridge KYC moved to pending", + ) + } else if (kyc_status === "approved") { const result = await AccountsRepository().updateBridgeFields(account.id, { bridgeKycStatus: "approved", }) @@ -86,6 +133,23 @@ export const kycHandler = async (req: Request, res: Response) => { }, "Bridge KYC rejected", ) + } else if (kyc_status === "offboarded") { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: "offboarded", + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.warn( + { accountId: account.id, customer_id }, + "Bridge KYC offboarded", + ) } return res.status(200).json({ status: "success" }) diff --git a/src/services/bridge/webhook-server/routes/replay.ts b/src/services/bridge/webhook-server/routes/replay.ts index 6316e2c8c..33b499bd9 100644 --- a/src/services/bridge/webhook-server/routes/replay.ts +++ b/src/services/bridge/webhook-server/routes/replay.ts @@ -6,7 +6,12 @@ import { BridgeConfig } from "@config" import { baseLogger } from "@services/logger" -import { createBridgeReplayLog } from "@services/mongoose/bridge-replay-log" +import { createBridgeReplay } from "@services/mongoose/bridge-replay-log" + +import { + isOutboundBridgeWithdrawal, + transferReplayEventTypeForStatus, +} from "../transfer-direction" import { depositHandler } from "./deposit" import { kycHandler } from "./kyc" @@ -41,18 +46,35 @@ const toRouteKey = (bridgeEventType: string): RouteKey | null => { const resolveReplayEventType = ({ eventType, eventObjectStatus, + eventObject, }: { eventType: string eventObjectStatus?: string + eventObject?: Record }): string => { const routeFromEventType = toRouteKey(eventType) if (routeFromEventType) return eventType if (eventObjectStatus && DEPOSIT_EVENT_TYPES.has(eventObjectStatus)) { + if (isOutboundBridgeWithdrawal(eventObject)) { + const transferEvent = transferReplayEventTypeForStatus(eventObjectStatus) + if (transferEvent) return transferEvent + } return eventObjectStatus } - if (eventObjectStatus === "approved" || eventObjectStatus === "rejected") { + const KYC_BRIDGE_STATUSES = new Set([ + "not_started", + "incomplete", + "awaiting_questionnaire", + "awaiting_ubo", + "under_review", + "approved", + "rejected", + "paused", + "offboarded", + ]) + if (eventObjectStatus && KYC_BRIDGE_STATUSES.has(eventObjectStatus)) { return `kyc.${eventObjectStatus}` } @@ -82,6 +104,8 @@ const toHandlerBody = ({ state: eventObject.state, amount: eventObject.amount, currency: eventObject.currency, + reason: eventObject.reason, + return_reason: eventObject.return_reason, }, } } @@ -138,10 +162,13 @@ export const replayHandler = async (req: Request, res: Response) => { return res.status(400).json({ error: "event_object must be an object" }) } + const eventObjectTyped = event_object as Record + const normalizedEventType = resolveReplayEventType({ eventType: event_type, eventObjectStatus: typeof event_object_status === "string" ? event_object_status : undefined, + eventObject: eventObjectTyped, }) const routeKey = toRouteKey(normalizedEventType) @@ -149,8 +176,6 @@ export const replayHandler = async (req: Request, res: Response) => { if (!routeKey) { return res.status(400).json({ error: "Unsupported event_type for replay" }) } - - const eventObjectTyped = event_object as Record const eventId: string = typeof event_id === "string" ? event_id @@ -173,7 +198,7 @@ export const replayHandler = async (req: Request, res: Response) => { } if (dry_run) { - const dryRunLog = await createBridgeReplayLog({ + const dryRunLog = await createBridgeReplay({ ...logBase, httpStatus: 0, httpResponse: { dry_run: true }, @@ -217,7 +242,7 @@ export const replayHandler = async (req: Request, res: Response) => { await HANDLERS[routeKey](fakeReq, fakeRes) - const logResult = await createBridgeReplayLog({ + const logResult = await createBridgeReplay({ ...logBase, httpStatus: handlerStatus, httpResponse: handlerBody, diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts index 0863a074b..a24a32fdc 100644 --- a/src/services/bridge/webhook-server/routes/transfer.ts +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -4,38 +4,88 @@ */ import { Request, Response } from "express" +import { sendBridgeWithdrawalNotificationBestEffort } from "@app/bridge/send-withdrawal-notification" import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" import { toBridgeTransferId } from "@domain/primitives/bridge" +const TERMINAL_FAILURE_STATES = new Set([ + "undeliverable", + "returned", + "refunded", + "refund_failed", + "missing_return_policy", + "error", + "canceled", +]) + +const TRANSIENT_STATES = new Set(["refund_in_flight"]) + +const transferLockKey = ( + transferId: string, + event: string, + state: string | undefined, +): IdempotencyKey => + `bridge-transfer:${transferId}:${event}:${state ?? ""}` as IdempotencyKey + +const markProcessed = async ( + transferId: string, + event: string, + state: string | undefined, +): Promise<"success" | "already_processed"> => { + const lockResult = await LockService().lockIdempotencyKey( + transferLockKey(transferId, event, state), + ) + if (lockResult instanceof Error) return "already_processed" + return "success" +} + export const transferHandler = async (req: Request, res: Response) => { const { event, data } = req.body - const { transfer_id, state, amount, currency } = data + const { transfer_id, state, amount, currency, reason, return_reason } = data if (!transfer_id || !event) { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency check using transfer_id as lock key - const lockKey = `bridge-transfer:${transfer_id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) - if (lockResult instanceof Error) { - baseLogger.info({ transfer_id }, "Duplicate Bridge transfer webhook") - return res.status(200).json({ status: "already_processed" }) - } - try { const bridgeTransferId = toBridgeTransferId(transfer_id) - // Update withdrawal status based on event - if (event === "transfer.completed") { + if (TRANSIENT_STATES.has(state)) { + baseLogger.info( + { transfer_id, state, event }, + "Bridge transfer in transient state — awaiting terminal event", + ) + return res.status(200).json({ status: "ignored_transient_state" }) + } + + const isCompletion = + event === "transfer.completed" || + event === "transfer.payment_processed" || + state === "payment_processed" + + const isFailure = event === "transfer.failed" || TERMINAL_FAILURE_STATES.has(state) + + if (!isCompletion && !isFailure) { + baseLogger.info({ transfer_id, state, event }, "Bridge transfer event not handled") + return res.status(200).json({ status: "ignored" }) + } + + if (isCompletion) { const result = await BridgeAccountsRepo.updateWithdrawalStatus( bridgeTransferId, "completed", ) if (result instanceof Error) { + if (result.message === BridgeAccountsRepo.BRIDGE_WITHDRAWAL_NOT_FOUND) { + baseLogger.warn( + { transfer_id }, + "Withdrawal not found for transfer webhook — Bridge may retry after bridgeTransferId is written", + ) + return res.status(503).json({ error: "Withdrawal not ready" }) + } baseLogger.error( { transfer_id, error: result }, "Failed to update withdrawal status", @@ -43,23 +93,56 @@ export const transferHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to update status" }) } + const lockStatus = await markProcessed(transfer_id, event, state) + if (lockStatus === "already_processed") { + return res.status(200).json({ status: "already_processed" }) + } + baseLogger.info( { transfer_id, + state, amount, currency, }, "Bridge transfer completed", ) - // TODO: Send push notification to user - } else if (event === "transfer.failed") { + await sendBridgeWithdrawalNotificationBestEffort({ + accountId: result.accountId, + amount: result.amount, + currency: result.currency, + outcome: "completed", + }) + } else if (isFailure) { + const failureReason = + state === "refund_failed" + ? (return_reason as string | undefined) + : event === "transfer.failed" + ? (reason as string | undefined) + : ((reason as string | undefined) ?? (return_reason as string | undefined)) + const result = await BridgeAccountsRepo.updateWithdrawalStatus( bridgeTransferId, "failed", + failureReason, ) if (result instanceof Error) { + if (result.message === BridgeAccountsRepo.BRIDGE_WITHDRAWAL_NOT_FOUND) { + baseLogger.warn( + { transfer_id }, + "Withdrawal not found for transfer webhook — Bridge may retry after bridgeTransferId is written", + ) + return res.status(503).json({ error: "Withdrawal not ready" }) + } + if (result.message.startsWith("Withdrawal already ")) { + baseLogger.info( + { transfer_id, state, error: result.message }, + "Ignoring Bridge transfer failure — withdrawal already terminal", + ) + return res.status(200).json({ status: "already_terminal" }) + } baseLogger.error( { transfer_id, error: result }, "Failed to update withdrawal status", @@ -67,17 +150,30 @@ export const transferHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to update status" }) } + const lockStatus = await markProcessed(transfer_id, event, state) + if (lockStatus === "already_processed") { + return res.status(200).json({ status: "already_processed" }) + } + baseLogger.warn( { transfer_id, state, amount, currency, + reason, + return_reason, }, "Bridge transfer failed", ) - // TODO: Send push notification to user + await sendBridgeWithdrawalNotificationBestEffort({ + accountId: result.accountId, + amount: result.amount, + currency: result.currency, + outcome: "failed", + failureReason: result.failureReason ?? failureReason, + }) } return res.status(200).json({ status: "success" }) diff --git a/src/services/bridge/webhook-server/transfer-direction.ts b/src/services/bridge/webhook-server/transfer-direction.ts new file mode 100644 index 000000000..6e785d0ea --- /dev/null +++ b/src/services/bridge/webhook-server/transfer-direction.ts @@ -0,0 +1,29 @@ +/** + * Distinguishes Bridge outbound withdrawals (USDT → ACH) from inbound deposits + * when replaying status_transitioned events that share the same state names. + */ +export const isOutboundBridgeWithdrawal = ( + eventObject: Record | undefined, +): boolean => { + if (!eventObject) return false + const source = eventObject.source as Record | undefined + const destination = eventObject.destination as Record | undefined + return source?.payment_rail === "ethereum" && destination?.payment_rail === "ach" +} + +const OUTBOUND_WITHDRAWAL_REPLAY_STATUSES = new Set([ + "payment_processed", + "undeliverable", + "returned", + "refunded", + "refund_failed", + "missing_return_policy", + "error", + "canceled", +]) + +export const transferReplayEventTypeForStatus = (status: string): string | null => { + if (!OUTBOUND_WITHDRAWAL_REPLAY_STATUSES.has(status)) return null + if (status === "payment_processed") return "transfer.payment_processed" + return "transfer.failed" +} diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index 83282f9a5..d03e72676 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -1,6 +1,6 @@ import express, { Request, Response } from "express" import { AccountsRepository } from "@services/mongoose/accounts" -import { createIbexCryptoReceiveLog } from "@services/mongoose/ibex-crypto-receive-log" +import { createIbexCryptoReceive } from "@services/mongoose/ibex-crypto-receive-log" import { listWalletsByAccountId } from "@app/wallets" import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" @@ -49,7 +49,7 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return { status: "error", code: "account_not_found" } as CryptoReceiveResult } - const ibexLog = await createIbexCryptoReceiveLog({ + const ibexLog = await createIbexCryptoReceive({ txHash: String(tx_hash), address: String(address), amount: String(amount), diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index 853a7a93b..4cc8fb43f 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -176,7 +176,7 @@ export const AccountsRepository = (): IAccountsRepository => { id: AccountId, fields: { bridgeCustomerId?: BridgeCustomerId - bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeKycStatus?: "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" bridgeEthereumAddress?: string }, ): Promise => { @@ -291,6 +291,6 @@ const translateToAccount = (result: AccountRecord): Account => ({ kratosUserId: result.kratosUserId as UserId, displayCurrency: (result.displayCurrency || UsdDisplayCurrency) as DisplayCurrency, bridgeCustomerId: result.bridgeCustomerId as BridgeCustomerId | undefined, - bridgeKycStatus: result.bridgeKycStatus, + bridgeKycStatus: (result.bridgeKycStatus === "pending" ? "open" : result.bridgeKycStatus) as Account["bridgeKycStatus"], bridgeEthereumAddress: result.bridgeEthereumAddress, }) diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index 234304c2c..c045295be 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -95,6 +95,18 @@ export const updateExternalAccountStatus = async ( // ============ Withdrawals ============ +export const BRIDGE_WITHDRAWAL_NOT_FOUND = "Withdrawal not found" + +export const BRIDGE_FAILURE_REASON_MAX_LENGTH = 512 + +export const truncateBridgeFailureReason = ( + reason: string | undefined, +): string | undefined => { + if (reason === undefined) return undefined + if (reason.length <= BRIDGE_FAILURE_REASON_MAX_LENGTH) return reason + return `${reason.slice(0, BRIDGE_FAILURE_REASON_MAX_LENGTH - 3)}...` +} + export const createWithdrawal = async (data: { accountId: string bridgeTransferId?: string @@ -171,14 +183,29 @@ export const findWithdrawalsByAccountId = async (accountId: string) => { export const updateWithdrawalStatus = async ( bridgeTransferId: BridgeTransferId, status: "pending" | "completed" | "failed", + failureReason?: string, ) => { try { + const update: Record = { status, updatedAt: new Date() } + const truncatedReason = truncateBridgeFailureReason(failureReason) + if (truncatedReason !== undefined) update.failureReason = truncatedReason + const record = await BridgeWithdrawal.findOneAndUpdate( - { bridgeTransferId }, - { status, updatedAt: new Date() }, + { bridgeTransferId, status: "pending" }, + update, { new: true }, ) - return record || new RepositoryError("Withdrawal not found") + if (record) return record + + const existing = await BridgeWithdrawal.findOne({ bridgeTransferId }) + if (!existing) return new RepositoryError(BRIDGE_WITHDRAWAL_NOT_FOUND) + + // Idempotent: duplicate webhook after we already reached this terminal status. + if (existing.status === status) return existing + + return new RepositoryError( + `Withdrawal already ${existing.status}, cannot transition to ${status}`, + ) } catch (error) { return new RepositoryError(String(error)) } diff --git a/src/services/mongoose/bridge-deposit-log.ts b/src/services/mongoose/bridge-deposit-log.ts index f71f37abd..953cd8a3d 100644 --- a/src/services/mongoose/bridge-deposit-log.ts +++ b/src/services/mongoose/bridge-deposit-log.ts @@ -1,6 +1,6 @@ -import { BridgeDepositLog } from "./schema" +import { BridgeDeposits } from "./schema" -export const createBridgeDepositLog = async (data: { +export const createBridgeDeposit = async (data: { eventId: string transferId: string customerId: string @@ -14,7 +14,7 @@ export const createBridgeDepositLog = async (data: { destinationTxHash?: string }): Promise<{ id: string } | Error> => { try { - const log = await BridgeDepositLog.create(data) + const log = await BridgeDeposits.create(data) return { id: log._id.toString() } } catch (error) { return error instanceof Error ? error : new Error(String(error)) diff --git a/src/services/mongoose/bridge-replay-log.ts b/src/services/mongoose/bridge-replay-log.ts index d1842be8c..361994991 100644 --- a/src/services/mongoose/bridge-replay-log.ts +++ b/src/services/mongoose/bridge-replay-log.ts @@ -1,6 +1,6 @@ -import { BridgeReplayLog } from "./schema" +import { BridgeReplay } from "./schema" -export const createBridgeReplayLog = async (data: { +export const createBridgeReplay = async (data: { eventId: string eventType: string eventPayload: Record @@ -14,7 +14,7 @@ export const createBridgeReplayLog = async (data: { dryRun?: boolean }): Promise<{ id: string } | Error> => { try { - const log = await BridgeReplayLog.create(data) + const log = await BridgeReplay.create(data) return { id: log._id.toString() } } catch (error) { @@ -22,7 +22,7 @@ export const createBridgeReplayLog = async (data: { } } -export const findBridgeReplayLogs = async (filter?: { +export const findBridgeReplays = async (filter?: { eventType?: string dryRun?: boolean limit?: number @@ -32,7 +32,7 @@ export const findBridgeReplayLogs = async (filter?: { if (filter?.eventType !== undefined) queryFilter.eventType = filter.eventType if (filter?.dryRun !== undefined) queryFilter.dryRun = filter.dryRun - return await BridgeReplayLog.find(queryFilter) + return await BridgeReplay.find(queryFilter) .sort({ replayedAt: -1 }) .limit(filter?.limit ?? 100) .lean() diff --git a/src/services/mongoose/ibex-crypto-receive-log.ts b/src/services/mongoose/ibex-crypto-receive-log.ts index e657c9e89..490330f09 100644 --- a/src/services/mongoose/ibex-crypto-receive-log.ts +++ b/src/services/mongoose/ibex-crypto-receive-log.ts @@ -1,6 +1,6 @@ -import { IbexCryptoReceiveLog } from "./schema" +import { IbexCryptoReceive } from "./schema" -export const createIbexCryptoReceiveLog = async (data: { +export const createIbexCryptoReceive = async (data: { txHash: string address: string amount: string @@ -9,7 +9,7 @@ export const createIbexCryptoReceiveLog = async (data: { accountId?: string }): Promise<{ id: string } | Error> => { try { - const log = await IbexCryptoReceiveLog.findOneAndUpdate( + const log = await IbexCryptoReceive.findOneAndUpdate( { txHash: data.txHash }, { ...data, receivedAt: new Date() }, { upsert: true, new: true, setDefaultsOnInsert: true }, @@ -21,7 +21,7 @@ export const createIbexCryptoReceiveLog = async (data: { } } -export const findIbexCryptoReceiveLogsSince = async ({ +export const findIbexCryptoReceivesSince = async ({ since, until = new Date(), }: { @@ -40,7 +40,7 @@ export const findIbexCryptoReceiveLogsSince = async ({ | Error > => { try { - return await IbexCryptoReceiveLog.find({ + return await IbexCryptoReceive.find({ receivedAt: { $gte: since, $lte: until }, }) .sort({ receivedAt: -1 }) diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index 15a1fd2f5..ef7b0ebf9 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -48,6 +48,7 @@ interface IBridgeWithdrawalRecord { amount: string currency: string status: "pending" | "completed" | "failed" + failureReason?: string externalAccountId: string createdAt: Date updatedAt: Date @@ -338,7 +339,7 @@ const AccountSchema = new Schema( }, bridgeKycStatus: { type: String, - enum: ["pending", "approved", "rejected"], + enum: ["open", "not_started", "incomplete", "awaiting_questionnaire", "awaiting_ubo", "under_review", "paused", "approved", "rejected", "offboarded"], required: false, }, bridgeEthereumAddress: { @@ -653,6 +654,7 @@ const BridgeWithdrawalSchema = new Schema({ amount: { type: String, required: true }, currency: { type: String, required: true }, status: { type: String, enum: ["pending", "completed", "failed"], default: "pending" }, + failureReason: { type: String, maxlength: 512 }, externalAccountId: { type: String, required: true }, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now }, @@ -670,7 +672,7 @@ BridgeWithdrawalSchema.index( }, ) -const BridgeDepositLogSchema = new Schema({ +const BridgeDepositsSchema = new Schema({ eventId: { type: String, required: true, unique: true }, transferId: { type: String, required: true }, customerId: { type: String, required: true }, @@ -685,12 +687,12 @@ const BridgeDepositLogSchema = new Schema({ createdAt: { type: Date, default: Date.now }, }) -BridgeDepositLogSchema.index({ transferId: 1 }) -BridgeDepositLogSchema.index({ customerId: 1, createdAt: -1 }) +BridgeDepositsSchema.index({ transferId: 1 }) +BridgeDepositsSchema.index({ customerId: 1, createdAt: -1 }) -export const BridgeDepositLog = mongoose.model("BridgeDepositLog", BridgeDepositLogSchema) +export const BridgeDeposits = mongoose.model("BridgeDeposits", BridgeDepositsSchema) -const IbexCryptoReceiveLogSchema = new Schema({ +const IbexCryptoReceiveSchema = new Schema({ txHash: { type: String, required: true, unique: true }, address: { type: String, required: true }, amount: { type: String, required: true }, @@ -700,12 +702,12 @@ const IbexCryptoReceiveLogSchema = new Schema({ receivedAt: { type: Date, default: Date.now }, }) -IbexCryptoReceiveLogSchema.index({ receivedAt: -1 }) -IbexCryptoReceiveLogSchema.index({ address: 1, receivedAt: -1 }) +IbexCryptoReceiveSchema.index({ receivedAt: -1 }) +IbexCryptoReceiveSchema.index({ address: 1, receivedAt: -1 }) -export const IbexCryptoReceiveLog = mongoose.model( - "IbexCryptoReceiveLog", - IbexCryptoReceiveLogSchema, +export const IbexCryptoReceive = mongoose.model( + "IbexCryptoReceive", + IbexCryptoReceiveSchema, ) const BridgeReconciliationOrphanSchema = new Schema({ @@ -742,7 +744,7 @@ export const BridgeReconciliationOrphan = mongoose.model( BridgeReconciliationOrphanSchema, ) -const BridgeReplayLogSchema = new Schema({ +const BridgeReplaySchema = new Schema({ eventId: { type: String, required: true }, eventType: { type: String, required: true }, eventPayload: { type: Schema.Types.Mixed, required: true }, @@ -756,11 +758,11 @@ const BridgeReplayLogSchema = new Schema({ dryRun: { type: Boolean, required: true, default: false }, }) -BridgeReplayLogSchema.index({ eventId: 1 }) -BridgeReplayLogSchema.index({ replayedAt: -1 }) -BridgeReplayLogSchema.index({ eventType: 1, replayedAt: -1 }) +BridgeReplaySchema.index({ eventId: 1 }) +BridgeReplaySchema.index({ replayedAt: -1 }) +BridgeReplaySchema.index({ eventType: 1, replayedAt: -1 }) -export const BridgeReplayLog = mongoose.model("BridgeReplayLog", BridgeReplayLogSchema) +export const BridgeReplay = mongoose.model("BridgeReplay", BridgeReplaySchema) export const BridgeVirtualAccount = mongoose.model( "BridgeVirtualAccount", diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 51c86757c..39d5a3da9 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -96,7 +96,7 @@ interface AccountRecord { // Bridge integration: bridgeCustomerId?: string - bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeKycStatus?: "open" | "pending" | "approved" | "rejected" | "offboarded" bridgeEthereumAddress?: string // mongoose in-built functions diff --git a/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts b/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts new file mode 100644 index 000000000..d00d89dfa --- /dev/null +++ b/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts @@ -0,0 +1,122 @@ +import { sendBridgeWithdrawalNotification } from "@app/bridge/send-withdrawal-notification" +import { AccountsRepository } from "@services/mongoose/accounts" +import { UsersRepository } from "@services/mongoose/users" +import { + PushNotificationsService, + SendFilteredPushNotificationStatus, +} from "@services/notifications/push-notifications" +import { getI18nInstance } from "@config" + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/users", () => ({ + UsersRepository: jest.fn(), +})) + +jest.mock("@services/notifications/push-notifications", () => ({ + PushNotificationsService: jest.fn(), + SendFilteredPushNotificationStatus: { + Filtered: "filtered", + Sent: "sent", + }, +})) + +jest.mock("@app/users/remove-device-tokens", () => ({ + removeDeviceTokens: jest.fn(), +})) + +jest.mock("@config", () => { + const mockI18n = { + __: jest.fn().mockImplementation(({ phrase }, options) => `${phrase} ${JSON.stringify(options)}`), + } + return { + getI18nInstance: jest.fn(() => mockI18n), + } +}) + +describe("sendBridgeWithdrawalNotification", () => { + const accountId = "507f1f77bcf86cd799439011" + const mockAccount = { + id: accountId, + kratosUserId: "user-id", + notificationSettings: { push: { enabled: true, disabledCategories: [] } }, + } + const mockUser = { + deviceTokens: ["token-1"], + language: "en", + } + + const sendFilteredNotification = jest.fn().mockResolvedValue({ + status: SendFilteredPushNotificationStatus.Sent, + }) + const mockI18n = getI18nInstance() + + beforeEach(() => { + jest.clearAllMocks() + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + }) + ;(UsersRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockUser), + }) + ;(PushNotificationsService as jest.Mock).mockReturnValue({ + sendFilteredNotification, + }) + ;(getI18nInstance as jest.Mock).mockReturnValue(mockI18n) + }) + + it("sends a completed withdrawal notification with Cashout category", async () => { + const result = await sendBridgeWithdrawalNotification({ + accountId, + amount: "100.00", + currency: "usdt", + outcome: "completed", + }) + + expect(result).toBe(true) + expect(sendFilteredNotification).toHaveBeenCalledWith( + expect.objectContaining({ + deviceTokens: mockUser.deviceTokens, + notificationCategory: "Cashout", + data: expect.objectContaining({ type: "bridge_withdrawal_completed" }), + }), + ) + expect(mockI18n.__).toHaveBeenCalledWith( + expect.objectContaining({ phrase: "notification.bridgeWithdrawal.completed.title" }), + ) + }) + + it("uses bodyWithReason when a failure reason is provided", async () => { + await sendBridgeWithdrawalNotification({ + accountId, + amount: "50.00", + currency: "usdt", + outcome: "failed", + failureReason: "ACH return", + }) + + expect(mockI18n.__).toHaveBeenCalledWith( + expect.objectContaining({ + phrase: "notification.bridgeWithdrawal.failed.bodyWithReason", + }), + expect.objectContaining({ reason: "ACH return" }), + ) + }) + + it("returns true when notification is filtered by user settings", async () => { + sendFilteredNotification.mockResolvedValue({ + status: SendFilteredPushNotificationStatus.Filtered, + }) + + const result = await sendBridgeWithdrawalNotification({ + accountId, + amount: "10.00", + currency: "usdt", + outcome: "completed", + }) + + expect(result).toBe(true) + }) +}) diff --git a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts index ea6c9610b..9b9988b64 100644 --- a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts @@ -176,6 +176,38 @@ describe("replayHandler", () => { ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-001" }) }) + it("routes outbound withdrawal payment_processed replay to transfer handler", async () => { + ;(transferHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-wd-001" }) + + const res = makeRes() + await replayHandler( + makeReq({ + ...BASE_BODY, + event_type: "updated.status_transitioned", + event_object_status: "payment_processed", + event_object: { + id: "tr-withdraw-001", + state: "payment_processed", + amount: "100.00", + currency: "usd", + source: { payment_rail: "ethereum", currency: "usdt" }, + destination: { payment_rail: "ach", currency: "usd" }, + }, + }), + res, + ) + + expect(transferHandler).toHaveBeenCalledTimes(1) + expect(depositHandler).not.toHaveBeenCalled() + const handlerReq = (transferHandler as jest.Mock).mock.calls[0][0] + expect(handlerReq.body.event).toBe("transfer.payment_processed") + }) + test.each(cases)( "%s is routed to the correct handler", async (eventType, handler) => { diff --git a/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts b/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts new file mode 100644 index 000000000..30bb87f31 --- /dev/null +++ b/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts @@ -0,0 +1,189 @@ +jest.mock("@services/lock", () => ({ + LockService: jest.fn(), +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/bridge-accounts", () => ({ + BRIDGE_WITHDRAWAL_NOT_FOUND: "Withdrawal not found", + updateWithdrawalStatus: jest.fn(), +})) + +jest.mock("@app/bridge/send-withdrawal-notification", () => ({ + sendBridgeWithdrawalNotificationBestEffort: jest.fn().mockResolvedValue(undefined), +})) + +import { Request, Response } from "express" +import { LockService } from "@services/lock" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import { sendBridgeWithdrawalNotificationBestEffort } from "@app/bridge/send-withdrawal-notification" +import { transferHandler } from "@services/bridge/webhook-server/routes/transfer" + +const makeRes = () => { + const res = { status: jest.fn(), json: jest.fn() } as unknown as Response + ;(res.status as jest.Mock).mockReturnValue(res) + ;(res.json as jest.Mock).mockReturnValue(res) + return res +} + +const makeReq = (body: Record) => ({ body }) as unknown as Request + +const WITHDRAWAL_RECORD = { id: "wd-1", status: "pending", bridgeTransferId: "tr-abc" } + +const updateFn = BridgeAccountsRepo.updateWithdrawalStatus as jest.Mock +let lockFn: jest.Mock + +beforeEach(() => { + jest.clearAllMocks() + lockFn = jest.fn().mockResolvedValue({}) + updateFn.mockResolvedValue({ ...WITHDRAWAL_RECORD, status: "completed" }) + ;(LockService as jest.Mock).mockReturnValue({ lockIdempotencyKey: lockFn }) +}) + +describe("transferHandler", () => { + it("returns 503 when withdrawal row is not found yet (retryable)", async () => { + const { RepositoryError } = jest.requireActual("@domain/errors") + updateFn.mockResolvedValue(new RepositoryError("Withdrawal not found")) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + data: { transfer_id: "tr-early", state: "canceled", reason: "rejected" }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(503) + expect(lockFn).not.toHaveBeenCalled() + }) + + it("acquires idempotency lock only after a successful status update", async () => { + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { transfer_id: "tr-abc", state: "payment_processed" }, + }), + res, + ) + + expect(updateFn).toHaveBeenCalled() + expect(lockFn).toHaveBeenCalledWith("bridge-transfer:tr-abc:transfer.completed:payment_processed") + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + }) + + it("does not acquire lock when status update fails", async () => { + const { RepositoryError } = jest.requireActual("@domain/errors") + updateFn.mockResolvedValue(new RepositoryError("mongo error")) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + data: { transfer_id: "tr-abc", state: "canceled" }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) + expect(lockFn).not.toHaveBeenCalled() + }) + + it("ignores refund_in_flight without updating withdrawal status", async () => { + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + data: { transfer_id: "tr-abc", state: "refund_in_flight" }, + }), + res, + ) + + expect(updateFn).not.toHaveBeenCalled() + expect(lockFn).not.toHaveBeenCalled() + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + expect((res.json as jest.Mock).mock.calls[0][0]).toEqual({ + status: "ignored_transient_state", + }) + }) + + it("returns already_processed when lock is held after a prior successful run", async () => { + lockFn.mockResolvedValue(new Error("already locked")) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { transfer_id: "tr-abc", state: "payment_processed" }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + expect((res.json as jest.Mock).mock.calls[0][0]).toEqual({ status: "already_processed" }) + }) + + it("sends a push notification after a successful completion", async () => { + updateFn.mockResolvedValue({ + ...WITHDRAWAL_RECORD, + status: "completed", + accountId: "acct-1", + amount: "25.00", + currency: "usdt", + }) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { transfer_id: "tr-abc", state: "payment_processed" }, + }), + res, + ) + + expect(sendBridgeWithdrawalNotificationBestEffort).toHaveBeenCalledWith({ + accountId: "acct-1", + amount: "25.00", + currency: "usdt", + outcome: "completed", + }) + }) + + it("does not send a push notification when the idempotency lock is already held", async () => { + lockFn.mockResolvedValue(new Error("already locked")) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { transfer_id: "tr-abc", state: "payment_processed" }, + }), + res, + ) + + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) + + it("returns 200 already_terminal when failure arrives after completion", async () => { + const { RepositoryError } = jest.requireActual("@domain/errors") + updateFn.mockResolvedValue( + new RepositoryError("Withdrawal already completed, cannot transition to failed"), + ) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + data: { transfer_id: "tr-abc", state: "returned", reason: "ACH return" }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + expect((res.json as jest.Mock).mock.calls[0][0]).toEqual({ status: "already_terminal" }) + expect(lockFn).not.toHaveBeenCalled() + }) +}) From 91c22d3ab54ae665a805407f52ea479904e9dc67 Mon Sep 17 00:00:00 2001 From: Vandana Date: Fri, 29 May 2026 16:23:59 -0400 Subject: [PATCH 14/43] fix: expose usdt amounts as usd cents --- .../2026-05-29-usdt-cent-scale-boundary.md | 431 ++++++++++++++++++ src/app/wallets/usd-wallet-amount.ts | 2 +- src/domain/shared/MoneyAmount.ts | 27 +- src/graphql/shared/types/object/usd-wallet.ts | 2 +- .../shared/types/object/usdt-wallet.ts | 2 +- src/graphql/shared/types/scalar/usd-cents.ts | 4 +- .../app/payments/send-intraledger.spec.ts | 6 +- .../app/wallets/usd-wallet-amount.spec.ts | 8 +- test/flash/unit/domain/shared/Money.spec.ts | 41 +- .../shared/types/scalar/usd-cents.spec.ts | 13 +- 10 files changed, 515 insertions(+), 21 deletions(-) create mode 100644 docs/plans/2026-05-29-usdt-cent-scale-boundary.md diff --git a/docs/plans/2026-05-29-usdt-cent-scale-boundary.md b/docs/plans/2026-05-29-usdt-cent-scale-boundary.md new file mode 100644 index 000000000..f367e2ece --- /dev/null +++ b/docs/plans/2026-05-29-usdt-cent-scale-boundary.md @@ -0,0 +1,431 @@ +# USDT Cent Scale Boundary Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Make Flash app-facing USDT amounts use USD-cent scale, so `$1.00` USDT is represented as `100`, while converting to IBEX USDT micro-units only at provider boundaries. + +**Architecture:** Keep `USDTAmount` internally precise in IBEX-compatible micro-USDT units because provider balances and invoices need six decimals. Add explicitly named USD-cent conversion helpers on `USDTAmount`, use those helpers at GraphQL/app boundaries, and leave `USDTAmount.toIbex()` plus IBEX/Bridge service code as the provider-facing conversion point. The implementation must make each boundary choose a named unit; no app/mobile/API code should pass raw USDT micros by accident. + +**Tech Stack:** TypeScript, Jest, GraphQL type resolvers, Flash `MoneyAmount` domain classes, IBEX adapter. + +--- + +## Branch And Safety Constraints + +- Work directly in `/Users/dread/Documents/Island-Bitcoin/Flash/flash`. +- The checkout is already on `tmp/bridge-rebase-pr-ready`; do not create a child branch. +- Preserve existing dirty/untracked files unless they are part of this task. +- Before editing, run: + +```bash +git status --short --branch +git diff -- src/domain/shared/MoneyAmount.ts src/app/wallets/usd-wallet-amount.ts src/graphql/shared/types/scalar/usd-cents.ts src/graphql/shared/types/object/usdt-wallet.ts test/flash/unit/app/wallets/usd-wallet-amount.spec.ts test/flash/unit/app/payments/send-intraledger.spec.ts test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts +``` + +Expected: branch is `tmp/bridge-rebase-pr-ready`. Existing unrelated changes may be present, but the listed USDT unit files should be understood before editing. + +## Core Invariant + +- App/API/mobile cent scale: + - `$1.00 USD` -> `100` + - `$1.00 USDT` -> `100` +- USDT provider scale inside `USDTAmount`: + - `$1.00 USDT` -> `1_000_000` micro-USDT + - `1` USD cent -> `10_000` micro-USDT +- IBEX boundary: + - `USDTAmount.toIbex()` still returns major USDT, e.g. `194.46`. + - IBEX parser code still uses `USDTAmount.fromNumber(...)` for major USDT balances. + +## Unit Boundary Table + +| Layer | Canonical unit | Allowed helpers | +| --- | --- | --- | +| Mobile/API stable-wallet inputs | USD-equivalent cents | `USDAmount.cents`, `USDTAmount.usdCents` | +| GraphQL wallet balance output | cents; fractional cents only when the field type is `FractionalCentAmount` | `USDAmount.asCents`, `USDTAmount.asUsdCents` | +| Domain/provider storage for USDT | micro-USDT | `USDTAmount.smallestUnits` | +| IBEX request payloads | major USDT number | `USDTAmount.toIbex` | +| IBEX response parsing | major USDT number -> micro-USDT domain amount | `USDTAmount.fromNumber` | + +Rules: +- `USDTAmount.smallestUnits(...)` is provider/internal only. +- `USDTAmount.usdCents(...)` is app/API/mobile input only. +- `USDTAmount.asUsdCents(...)` is app/API/mobile output only. +- Generic helpers such as `MoneyAmount.from(...)`, `asPaymentAmount()`, GraphQL scalar parsing, bridge quote/withdrawal resolvers, and mobile mutation adapters must be audited before implementation is considered complete. + +## Quantization Policy + +Provider-originated USDT balances can contain sub-cent values because IBEX supports six decimals and one USD cent equals `10_000` micro-USDT. + +- Do not quantize before provider validation, balance checks, reconciliation, invoice/payment creation, or persistence. These flows stay in exact micro-USDT. +- For `FractionalCentAmount` balance outputs, expose exact fractional cents to four decimals because `1` micro-USDT equals `0.0001` cent. Example: `9_147_993` micros -> `914.7993` cents. +- For integer-cent inputs from mobile/API, reject fractional cent input unless an existing scalar explicitly supports fractional cents for that mutation. +- For any output field that is contractually integer cents, do not silently round sub-cent USDT. Either return an error for non-cent-clean values or add a separate fractional-cent field/type. + +## Rollout Contract + +This plan targets the development branch `tmp/bridge-rebase-pr-ready`. No production Flash Mobile client currently ships IBEX USDT code that depends on the old micro-USDT app/API semantics, so this is not a production backwards-compatibility migration. + +Rules: +- Treat the USD-cent USDT contract as the canonical contract for this development branch. +- Do not add legacy micro-USDT compatibility code, version flags, or dual-parse heuristics just for old clients unless a concrete caller is found during the audit. +- If the audit finds an existing non-production caller inside this branch that still sends micro-USDT through an app/API path, update that caller to the cent-scale contract. +- If a production mixed-client window is later introduced, stop and write a separate rollout plan before shipping. + +## Numeric Safety Policy + +Existing GraphQL balance fields return JavaScript `number`, so this plan keeps that shape unless the schema is intentionally changed. That has a safe-integer limit. + +- Before adding new `Number(...)` conversions, check whether the field already returns `number`. +- Add at least one max-range test around the largest realistic USDT wallet balance expected by product. +- If a converted cent value can exceed `Number.MAX_SAFE_INTEGER`, stop and change the wire type to a string/BigInt-compatible scalar instead of silently returning an unsafe number. + +## Task 1: Add Red Tests For USDT Cent-Scale Input + +**Files:** +- Modify: `test/flash/unit/app/wallets/usd-wallet-amount.spec.ts` +- Modify: `test/flash/unit/app/payments/send-intraledger.spec.ts` + +**Step 1: Update wallet amount expectations** + +Change the USDT case in `usd-wallet-amount.spec.ts` so an input of `19446` represents `$194.46` USDT. + +Expected assertions: + +```ts +expect(result).toBeInstanceOf(USDTAmount) +expect(result.asSmallestUnits()).toBe("194460000") +expect(result.toIbex()).toBe(194.46) +``` + +**Step 2: Update intraledger expectations** + +In `send-intraledger.spec.ts`, rename the test from micro-unit semantics to cent-scale semantics and update: + +```ts +expect(mockAddInvoice.mock.calls[0][0].amount.asSmallestUnits()).toBe("194460000") +expect(mockAddInvoice.mock.calls[0][0].amount.toIbex()).toBe(194.46) +``` + +**Step 3: Run the focused tests and confirm failure** + +```bash +TEST='test/flash/unit/app/wallets/usd-wallet-amount.spec.ts test/flash/unit/app/payments/send-intraledger.spec.ts' yarn test:unit --runInBand +``` + +Expected: failures still show USDT input `19446` being treated as micro-USDT. + +## Task 2: Add Explicit USDT USD-Cent Helpers + +**Files:** +- Modify: `src/domain/shared/MoneyAmount.ts` + +**Step 1: Add constants near `USDTAmount`** + +```ts +const USDT_MICROS_PER_MAJOR_UNIT = 1_000_000n +const USDT_MICROS_PER_USD_CENT = 10_000n +``` + +Use these constants in `USDTAmount.fromNumber`, `asNumber`, and new helpers to remove magic numbers. + +**Step 2: Add constructor for app/API cent input** + +Add: + +```ts +static usdCents(cents: string | bigint): USDTAmount | BigIntConversionError { + try { + const centAmt = new Money(cents.toString(), "USDTUsdCents", Round.HALF_TO_EVEN) + return new USDTAmount( + centAmt.multiply(USDT_MICROS_PER_USD_CENT.toString()).toFixed(0), + ) + } catch (error) { + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) + } +} +``` + +If `Money.multiply(...)` does not accept the value cleanly in this form, use the same local style as `USDAmount.dollars(...)`: create a `USDTAmount.smallestUnits(USDT_MICROS_PER_USD_CENT)` multiplier and multiply money values. + +**Step 3: Add serializer for app/API cent output** + +Add: + +```ts +asUsdCents(precision: number = 0): string { + return this.money.divide(USDT_MICROS_PER_USD_CENT.toString()).toFixed(precision) +} +``` + +The default precision mirrors the integer-cent expectation. Call `asUsdCents(4)` only for fields whose schema explicitly allows fractional cents, such as `FractionalCentAmount` wallet balances. + +**Step 4: Run the domain compile target indirectly** + +```bash +TEST='test/flash/unit/app/wallets/usd-wallet-amount.spec.ts' yarn test:unit --runInBand +``` + +Expected: it may still fail until Task 3 wires the helper into the app input path, but TypeScript/Jest should compile. + +## Task 3: Wire App-Facing USDT Input To Cent Scale + +**Files:** +- Modify: `src/app/wallets/usd-wallet-amount.ts` + +**Step 1: Change USDT input conversion** + +Replace: + +```ts +if (currency === WalletCurrency.Usdt) return USDTAmount.smallestUnits(raw) +``` + +With: + +```ts +if (currency === WalletCurrency.Usdt) return USDTAmount.usdCents(raw) +``` + +**Step 2: Run focused tests** + +```bash +TEST='test/flash/unit/app/wallets/usd-wallet-amount.spec.ts test/flash/unit/app/payments/send-intraledger.spec.ts' yarn test:unit --runInBand +``` + +Expected: the updated cent-scale tests pass. Mixed-currency tests remain unchanged. + +## Task 4: Add Red Tests For GraphQL USDT Serialization + +**Files:** +- Modify: `test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts` +- Test gap to inspect: there may not be an existing `UsdtWallet.balance` unit spec. If absent, do not create a broad GraphQL integration suite just for this; cover the scalar and keep the resolver change simple. + +**Step 1: Update `USDCents` scalar test** + +Add or update a test proving USDT serializes as USD cents, not micro-USDT: + +```ts +const amount = USDTAmount.smallestUnits("9147993") +if (amount instanceof Error) throw amount + +expect(USDCentsScalar.serialize(amount)).toBe(914.7993) +``` + +This represents `9.147993 USDT`, which should be `914.7993` cents. + +**Step 2: Run scalar test and confirm failure** + +```bash +TEST='test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts' yarn test:unit --runInBand +``` + +Expected: failure currently returns `9147993`. + +## Task 5: Wire GraphQL USDT Output To Cent Scale + +**Files:** +- Modify: `src/graphql/shared/types/scalar/usd-cents.ts` +- Modify: `src/graphql/shared/types/object/usdt-wallet.ts` + +**Step 1: Update scalar serialization** + +Replace: + +```ts +return Number(value.asSmallestUnits()) +``` + +With: + +```ts +return Number(value.asUsdCents(4)) +``` + +**Step 2: Update USDT wallet balance resolver** + +Replace: + +```ts +return Number(balance.asSmallestUnits(8)) +``` + +With: + +```ts +return Number(balance.asUsdCents(4)) +``` + +**Step 3: Run scalar test** + +```bash +TEST='test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts' yarn test:unit --runInBand +``` + +Expected: scalar test passes. + +## Task 6: Audit For Remaining App-Facing Micro-USDT Leaks + +**Files:** +- Inspect only unless tests reveal a real gap. + +**Step 1: Search for USDT smallest-unit usage** + +```bash +rg "asSmallestUnits\\(|USDTAmount\\.smallestUnits|asNumber\\(|toIbex\\(" src test/flash/unit +``` + +**Step 2: Search all GraphQL USDT response surfaces** + +```bash +rg "USDTAmount|WalletCurrency.*Usdt|UsdtWallet|walletCurrency|balance|amount" src/graphql test/flash/unit/graphql +``` + +For each resolver/serializer that can return a USDT amount, document whether it is: +- mobile/API-facing stable-wallet cents, +- provider/internal micros, +- provider major units, +- unrelated non-USDT data. + +Add focused tests for every mobile-visible USDT amount field that changes unit semantics. Do not rely on scalar tests alone if a resolver bypasses the scalar. + +**Step 3: Search constructor/parser/payment helper seams** + +```bash +rg "MoneyAmount\\.from|asPaymentAmount\\(|parseValue\\(|parseLiteral\\(|normalizePaymentAmount|PaymentAmount|USDCents" src test/flash/unit +``` + +**Step 4: Classify each hit** + +Provider/internal uses that should stay micro/major: +- `src/services/ibex/client.ts` +- `src/services/bridge/index.ts` +- cash-wallet cutover fee/audit internals +- persistence or provider parsing code + +App/API-facing uses that should be cent-scale: +- GraphQL wallet balances +- `USDCents` scalar serialization +- wallet amount input helpers used by mobile mutations +- `MoneyAmount.from(..., WalletCurrency.Usdt)` if the caller is app/API/mobile-facing +- `asPaymentAmount()` only if the caller is provider-facing or explicitly expects provider major units +- GraphQL mutation parsing paths that use `USDCents.parseValue` before wallet currency is known + +**Step 5: Patch confirmed app/API leaks** + +Do not broadly replace every `USDTAmount.smallestUnits`. That method is still correct for IBEX provider data, fee audit data, and stored micro-USDT values. + +Specific expected decisions: +- If `MoneyAmount.from(..., WalletCurrency.Usdt)` is used by app/API/mobile code, change USDT there to `USDTAmount.usdCents(...)`. If it is used for persistence/provider rehydration, split it into two named constructors instead of changing it globally. +- Do not change `USDCents.parseValue` to return `USDTAmount`, because the scalar does not know wallet currency. Move currency-specific amount construction into resolvers/helpers that already know the wallet currency, or add a separate USDT-aware input path. +- Audit bridge quote/withdrawal paths and add tests proving mobile cent-scale inputs convert exactly once before provider calls, or proving those paths are provider-only and should keep current units. +- Audit all `asPaymentAmount()` usage. If USDT reaches app/API/mobile through it, override or fence it; otherwise document it as provider-facing only. + +## Task 7: Add Boundary Tests From Review + +**Files:** +- Modify or add focused tests only where the corresponding code path exists. + +**Step 1: Domain helper tests** + +Add tests for: +- `USDTAmount.usdCents("0")` -> `0` micros -> `0` IBEX. +- `USDTAmount.usdCents("1")` -> `10000` micros -> `0.01` IBEX. +- `USDTAmount.usdCents("100")` -> `1000000` micros -> `1` IBEX. +- negative input behavior, matching existing `Money`/amount class conventions. +- `USDTAmount.smallestUnits("19446").asUsdCents(4)` -> `"1.9446"`. +- `USDTAmount.smallestUnits("19999").asUsdCents(4)` -> `"1.9999"`. +- A large amount that remains within GraphQL number safety if the API still returns `number`; if the realistic upper bound is unsafe, change the plan to use a string scalar before implementing. + +**Step 2: Generic constructor/parser tests** + +If `MoneyAmount.from(..., WalletCurrency.Usdt)` is changed or split, add a test that locks the selected semantics. + +If GraphQL mutation input parsing is changed, add a test proving USDT input parsing is cent-scale and USD input parsing is unchanged. + +**Step 3: GraphQL output tests** + +Add resolver/serialization tests for every mobile-visible GraphQL USDT amount field discovered in Task 6. At minimum: +- `USDCents` serialization for a cent-clean USDT value. +- `USDCents` or `FractionalCentAmount` serialization for a non-cent-clean USDT value, according to the quantization policy. +- `UsdtWallet.balance` returns cent-scale/fractional-cent-scale, not raw micro-USDT. + +**Step 4: Development-branch contract test** + +Add a test proving the development-branch contract is unambiguous: +- USDT app/API input `100` means `$1.00` / `1_000_000` micro-USDT. +- There is no silent compatibility path that interprets the same input as `100` micro-USDT. +- If an existing branch-local caller still sends micro-USDT through an app/API path, update that caller and cover it with a focused test. + +**Step 5: Provider boundary tests** + +Add or update one focused provider-boundary test proving a mobile/API input of `19446` cents becomes: + +```ts +amount.asSmallestUnits() === "194460000" +amount.toIbex() === 194.46 +``` + +And that provider-originated `USDTAmount.fromNumber("194.46")` still yields the same internal micros. + +## Task 8: Full Verification + +**Files:** +- All modified files. + +**Step 1: Run focused tests** + +```bash +TEST='test/flash/unit/app/wallets/usd-wallet-amount.spec.ts test/flash/unit/app/payments/send-intraledger.spec.ts test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts' yarn test:unit --runInBand +``` + +Expected: all pass. + +**Step 2: Run build** + +```bash +yarn build +``` + +Expected: build completes successfully. + +**Step 3: Review diff** + +```bash +git diff -- src/domain/shared/MoneyAmount.ts src/app/wallets/usd-wallet-amount.ts src/graphql/shared/types/scalar/usd-cents.ts src/graphql/shared/types/object/usdt-wallet.ts test/flash/unit/app/wallets/usd-wallet-amount.spec.ts test/flash/unit/app/payments/send-intraledger.spec.ts test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts +``` + +Expected: no unrelated files, no IBEX adapter conversion behavior changed except through existing `USDTAmount.toIbex()`. + +## Task 9: Commit Directly On `tmp/bridge-rebase-pr-ready` + +**Files:** +- Stage only the plan and the intentional USDT unit files. + +**Step 1: Stage explicit paths** + +```bash +git add docs/plans/2026-05-29-usdt-cent-scale-boundary.md \ + src/domain/shared/MoneyAmount.ts \ + src/app/wallets/usd-wallet-amount.ts \ + src/graphql/shared/types/scalar/usd-cents.ts \ + src/graphql/shared/types/object/usdt-wallet.ts \ + test/flash/unit/app/wallets/usd-wallet-amount.spec.ts \ + test/flash/unit/app/payments/send-intraledger.spec.ts \ + test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts +``` + +**Step 2: Commit** + +```bash +git commit -m "fix: expose usdt wallet amounts in usd cents" +``` + +## Review Questions For Dual-Model Review + +1. Does adding `USDTAmount.usdCents(...)` and `USDTAmount.asUsdCents(...)` preserve a clear app/API boundary without corrupting provider-facing IBEX precision? +2. Should `USDTAmount.asPaymentAmount()` remain inherited from `MoneyAmount`, or does it create an app-facing micro-unit leak that needs a targeted override? +3. Are GraphQL `USDCents`, `UsdtWallet.balance`, transaction history, and payment/invoice mutation paths fully inventoried for mobile-visible unit semantics? +4. Is the quantization policy correct: exact micros internally, fractional cents only on `FractionalCentAmount`, no silent rounding on integer-cent contracts? +5. Are there bridge withdrawal or quote paths that accept mobile cent-scale USDT input but bypass `usdWalletAmountFromInput`? diff --git a/src/app/wallets/usd-wallet-amount.ts b/src/app/wallets/usd-wallet-amount.ts index e2c166e4f..41b376f1f 100644 --- a/src/app/wallets/usd-wallet-amount.ts +++ b/src/app/wallets/usd-wallet-amount.ts @@ -12,7 +12,7 @@ export const usdWalletAmountFromInput = ( const raw = amount.toString() if (currency === WalletCurrency.Usd) return USDAmount.cents(raw) - if (currency === WalletCurrency.Usdt) return USDTAmount.smallestUnits(raw) + if (currency === WalletCurrency.Usdt) return USDTAmount.usdCents(raw) return new UnsupportedCurrencyError(`USD wallet amount unsupported for ${currency}`) } diff --git a/src/domain/shared/MoneyAmount.ts b/src/domain/shared/MoneyAmount.ts index 7ad965663..9422d2702 100644 --- a/src/domain/shared/MoneyAmount.ts +++ b/src/domain/shared/MoneyAmount.ts @@ -60,8 +60,7 @@ export abstract class MoneyAmount { static from(amount: number | string, currency: WalletCurrency): MoneyAmount | Error { if (currency === WalletCurrency.Usd) return USDAmount.cents(amount.toString()) else if (currency === WalletCurrency.Jmd) return JMDAmount.cents(amount.toString()) - else if (currency === WalletCurrency.Usdt) - return USDTAmount.smallestUnits(amount.toString()) + else if (currency === WalletCurrency.Usdt) return USDTAmount.usdCents(amount.toString()) else return new UnsupportedCurrencyError(`Could not read currency: ${currency}`) } } @@ -193,6 +192,9 @@ export class BtcAmount extends MoneyAmount { } } +const USDT_MICROS_PER_MAJOR_UNIT = 1_000_000n +const USDT_MICROS_PER_USD_CENT = 10_000n + export class USDTAmount extends MoneyAmount { static currencyId: IbexCurrencyId = 29 as IbexCurrencyId @@ -210,10 +212,23 @@ export class USDTAmount extends MoneyAmount { } } + static usdCents(cents: string | bigint): USDTAmount | BigIntConversionError { + try { + const centAmt = new Money(cents.toString(), "USDTUsdCents", Round.HALF_TO_EVEN) + const multiplier = USDTAmount.smallestUnits(USDT_MICROS_PER_USD_CENT) + if (multiplier instanceof BigIntConversionError) return multiplier + return new USDTAmount(multiplier.money.multiply(centAmt).toFixed(0)) + } catch (error) { + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) + } + } + static fromNumber(d: number | string): USDTAmount | BigIntConversionError { try { const usdtAmt = new Money(d.toString(), "USDT", Round.HALF_TO_EVEN) - const multiplier = USDTAmount.smallestUnits(1_000_000n) + const multiplier = USDTAmount.smallestUnits(USDT_MICROS_PER_MAJOR_UNIT) if (multiplier instanceof BigIntConversionError) return multiplier return new USDTAmount(multiplier.money.multiply(usdtAmt).toFixed(0)) } catch (error) { @@ -229,8 +244,12 @@ export class USDTAmount extends MoneyAmount { return this.money.toFixed(precision) } + asUsdCents(precision: number = 0): string { + return this.money.divide(USDT_MICROS_PER_USD_CENT.toString()).toFixed(precision) + } + asNumber(precision: number = 6): string { - return this.money.divide(1_000_000).toFixed(precision) + return this.money.divide(USDT_MICROS_PER_MAJOR_UNIT.toString()).toFixed(precision) } toIbex(): number { diff --git a/src/graphql/shared/types/object/usd-wallet.ts b/src/graphql/shared/types/object/usd-wallet.ts index 27096a000..b417dd33d 100644 --- a/src/graphql/shared/types/object/usd-wallet.ts +++ b/src/graphql/shared/types/object/usd-wallet.ts @@ -59,7 +59,7 @@ const UsdWallet = GT.Object({ throw mapError(balance) } if (balance instanceof USDTAmount) { - return Number(balance.asSmallestUnits(8)) + return Number(balance.asUsdCents(4)) } return Number(balance.asCents(8)) }, diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index 046e21bb7..4bb0c6a1f 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -66,7 +66,7 @@ const UsdtWallet = GT.Object({ throw mapError(balance) } if (balance instanceof USDTAmount) { - return Number(balance.asSmallestUnits(8)) + return Number(balance.asUsdCents(4)) } if (balance instanceof USDAmount) { return Number(balance.asCents(8)) diff --git a/src/graphql/shared/types/scalar/usd-cents.ts b/src/graphql/shared/types/scalar/usd-cents.ts index d9e505d0e..848ae8e4f 100644 --- a/src/graphql/shared/types/scalar/usd-cents.ts +++ b/src/graphql/shared/types/scalar/usd-cents.ts @@ -17,10 +17,10 @@ const USDCentsScalar = GT.Scalar({ return Number(value.asCents(2)) } if (value instanceof USDTAmount) { - return Number(value.asSmallestUnits()) + return Number(value.asUsdCents(4)) } else throw new Error(`Failed to serialize USDAmount: ${value}`) } }) -export default USDCentsScalar \ No newline at end of file +export default USDCentsScalar diff --git a/test/flash/unit/app/payments/send-intraledger.spec.ts b/test/flash/unit/app/payments/send-intraledger.spec.ts index 179ff1259..f92548a46 100644 --- a/test/flash/unit/app/payments/send-intraledger.spec.ts +++ b/test/flash/unit/app/payments/send-intraledger.spec.ts @@ -167,7 +167,7 @@ describe("intraledgerPaymentSendWalletIdForUsdWallet", () => { }) }) - it("sends USDT to USDT using USDT micro-unit amount semantics", async () => { + it("sends USDT to USDT using USD-cent amount semantics", async () => { mockFindWalletById.mockImplementation(async (walletId: WalletId) => { if (walletId === senderUsdtWalletId) { return wallet({ @@ -196,8 +196,8 @@ describe("intraledgerPaymentSendWalletIdForUsdWallet", () => { amount: expect.any(USDTAmount), memo: "USDT intraledger", }) - expect(mockAddInvoice.mock.calls[0][0].amount.asSmallestUnits()).toBe("19446") - expect(mockAddInvoice.mock.calls[0][0].amount.toIbex()).toBe(0.019446) + expect(mockAddInvoice.mock.calls[0][0].amount.asSmallestUnits()).toBe("194460000") + expect(mockAddInvoice.mock.calls[0][0].amount.toIbex()).toBe(194.46) expect(mockPayInvoice).toHaveBeenCalledWith({ accountId: senderUsdtWalletId, invoice: "lnbc1recipient", diff --git a/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts b/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts index 4c2573745..905c76477 100644 --- a/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts +++ b/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts @@ -10,13 +10,13 @@ describe("usdWalletAmountFromInput", () => { expect((amount as USDAmount).toIbex()).toBe(194.46) }) - it("treats USDT wallet input as micro-USDT", () => { + it("treats USDT wallet input as USD cents", () => { const amount = usdWalletAmountFromInput("19446", WalletCurrency.Usdt) expect(amount).toBeInstanceOf(USDTAmount) - expect((amount as USDTAmount).asSmallestUnits()).toBe("19446") - expect((amount as USDTAmount).asNumber()).toBe("0.019446") - expect((amount as USDTAmount).toIbex()).toBe(0.019446) + expect((amount as USDTAmount).asSmallestUnits()).toBe("194460000") + expect((amount as USDTAmount).asNumber()).toBe("194.460000") + expect((amount as USDTAmount).toIbex()).toBe(194.46) }) it("rejects BTC", () => { diff --git a/test/flash/unit/domain/shared/Money.spec.ts b/test/flash/unit/domain/shared/Money.spec.ts index bd545f212..a9e3baacc 100644 --- a/test/flash/unit/domain/shared/Money.spec.ts +++ b/test/flash/unit/domain/shared/Money.spec.ts @@ -1,4 +1,5 @@ -import { JMDAmount, USDAmount, WalletCurrency } from "@domain/shared" +import { JMDAmount, USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import { MoneyAmount } from "@domain/shared/MoneyAmount" import JmdAmount from "@graphql/shared/types/scalar/jmd-amount" describe("Money Amount", () => { @@ -135,4 +136,40 @@ describe("Money Amount", () => { }) }) }) -}) \ No newline at end of file + + describe("USDT Amount", () => { + it("converts USD cents into USDT micro-units", () => { + const amount = USDTAmount.usdCents("100") + if (amount instanceof Error) throw amount + + expect(amount.asSmallestUnits()).toBe("1000000") + expect(amount.asNumber()).toBe("1.000000") + expect(amount.asUsdCents()).toBe("100") + expect(amount.toIbex()).toBe(1) + }) + + it("converts one USD cent into ten thousand USDT micro-units", () => { + const amount = USDTAmount.usdCents("1") + if (amount instanceof Error) throw amount + + expect(amount.asSmallestUnits()).toBe("10000") + expect(amount.asUsdCents()).toBe("1") + expect(amount.toIbex()).toBe(0.01) + }) + + it("preserves provider-originated sub-cent USDT as fractional cents", () => { + const amount = USDTAmount.smallestUnits("19446") + if (amount instanceof Error) throw amount + + expect(amount.asUsdCents(4)).toBe("1.9446") + }) + + it("keeps generic MoneyAmount USDT construction on the app-facing cent contract", () => { + const amount = MoneyAmount.from("100", WalletCurrency.Usdt) + if (amount instanceof Error) throw amount + + expect(amount).toBeInstanceOf(USDTAmount) + expect((amount as USDTAmount).asSmallestUnits()).toBe("1000000") + }) + }) +}) diff --git a/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts b/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts index 497ab3903..0d1b1896f 100644 --- a/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts +++ b/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts @@ -9,10 +9,17 @@ describe("USDCentsScalar", () => { expect(USDCentsScalar.serialize(amount)).toBe(123) }) - it("serializes USDT amounts as micro-USDT units", () => { - const amount = USDTAmount.smallestUnits("1234567") + it("serializes USDT amounts as USD cents", () => { + const amount = USDTAmount.smallestUnits("1230000") if (amount instanceof Error) throw amount - expect(USDCentsScalar.serialize(amount)).toBe(1234567) + expect(USDCentsScalar.serialize(amount)).toBe(123) + }) + + it("serializes USDT sub-cent amounts as fractional USD cents", () => { + const amount = USDTAmount.smallestUnits("9147993") + if (amount instanceof Error) throw amount + + expect(USDCentsScalar.serialize(amount)).toBe(914.7993) }) }) From 836405e32279b2eda0a360da9808e8729fd3f096 Mon Sep 17 00:00:00 2001 From: Vandana Date: Fri, 29 May 2026 17:07:53 -0400 Subject: [PATCH 15/43] fix: keep usdt cent boundaries explicit --- .../2026-05-29-usdt-cent-scale-boundary.md | 14 +++++++------- src/domain/shared/MoneyAmount.ts | 7 ++++--- src/graphql/shared/types/object/usd-wallet.ts | 2 +- .../shared/types/object/usdt-wallet.ts | 2 +- src/graphql/shared/types/scalar/usd-cents.ts | 2 +- test/flash/unit/domain/shared/Money.spec.ts | 19 +++++++++++++++---- .../shared/types/scalar/usd-cents.spec.ts | 4 ++-- 7 files changed, 31 insertions(+), 19 deletions(-) diff --git a/docs/plans/2026-05-29-usdt-cent-scale-boundary.md b/docs/plans/2026-05-29-usdt-cent-scale-boundary.md index f367e2ece..5e6c9fb18 100644 --- a/docs/plans/2026-05-29-usdt-cent-scale-boundary.md +++ b/docs/plans/2026-05-29-usdt-cent-scale-boundary.md @@ -154,12 +154,12 @@ If `Money.multiply(...)` does not accept the value cleanly in this form, use the Add: ```ts -asUsdCents(precision: number = 0): string { - return this.money.divide(USDT_MICROS_PER_USD_CENT.toString()).toFixed(precision) +asUsdCents(): string { + return this.money.divide(USDT_MICROS_PER_USD_CENT.toString()).toFixed(0) } ``` -The default precision mirrors the integer-cent expectation. Call `asUsdCents(4)` only for fields whose schema explicitly allows fractional cents, such as `FractionalCentAmount` wallet balances. +Public app/API output should stay integer-cent based. Provider-originated sub-cent USDT remains exact internally as micro-USDT and is rounded when crossing into public cent fields. **Step 4: Run the domain compile target indirectly** @@ -240,7 +240,7 @@ return Number(value.asSmallestUnits()) With: ```ts -return Number(value.asUsdCents(4)) +return Number(value.asUsdCents()) ``` **Step 2: Update USDT wallet balance resolver** @@ -254,7 +254,7 @@ return Number(balance.asSmallestUnits(8)) With: ```ts -return Number(balance.asUsdCents(4)) +return Number(balance.asUsdCents()) ``` **Step 3: Run scalar test** @@ -334,8 +334,8 @@ Add tests for: - `USDTAmount.usdCents("1")` -> `10000` micros -> `0.01` IBEX. - `USDTAmount.usdCents("100")` -> `1000000` micros -> `1` IBEX. - negative input behavior, matching existing `Money`/amount class conventions. -- `USDTAmount.smallestUnits("19446").asUsdCents(4)` -> `"1.9446"`. -- `USDTAmount.smallestUnits("19999").asUsdCents(4)` -> `"1.9999"`. +- `USDTAmount.smallestUnits("19446").asUsdCents()` -> `"2"`. +- `USDTAmount.smallestUnits("19999").asUsdCents()` -> `"2"`. - A large amount that remains within GraphQL number safety if the API still returns `number`; if the realistic upper bound is unsafe, change the plan to use a string scalar before implementing. **Step 2: Generic constructor/parser tests** diff --git a/src/domain/shared/MoneyAmount.ts b/src/domain/shared/MoneyAmount.ts index 9422d2702..6b89ab13e 100644 --- a/src/domain/shared/MoneyAmount.ts +++ b/src/domain/shared/MoneyAmount.ts @@ -60,7 +60,8 @@ export abstract class MoneyAmount { static from(amount: number | string, currency: WalletCurrency): MoneyAmount | Error { if (currency === WalletCurrency.Usd) return USDAmount.cents(amount.toString()) else if (currency === WalletCurrency.Jmd) return JMDAmount.cents(amount.toString()) - else if (currency === WalletCurrency.Usdt) return USDTAmount.usdCents(amount.toString()) + else if (currency === WalletCurrency.Usdt) + return USDTAmount.smallestUnits(amount.toString()) else return new UnsupportedCurrencyError(`Could not read currency: ${currency}`) } } @@ -244,8 +245,8 @@ export class USDTAmount extends MoneyAmount { return this.money.toFixed(precision) } - asUsdCents(precision: number = 0): string { - return this.money.divide(USDT_MICROS_PER_USD_CENT.toString()).toFixed(precision) + asUsdCents(): string { + return this.money.divide(USDT_MICROS_PER_USD_CENT.toString()).toFixed(0) } asNumber(precision: number = 6): string { diff --git a/src/graphql/shared/types/object/usd-wallet.ts b/src/graphql/shared/types/object/usd-wallet.ts index b417dd33d..305a855bb 100644 --- a/src/graphql/shared/types/object/usd-wallet.ts +++ b/src/graphql/shared/types/object/usd-wallet.ts @@ -59,7 +59,7 @@ const UsdWallet = GT.Object({ throw mapError(balance) } if (balance instanceof USDTAmount) { - return Number(balance.asUsdCents(4)) + return Number(balance.asUsdCents()) } return Number(balance.asCents(8)) }, diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index 4bb0c6a1f..3dd36759a 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -66,7 +66,7 @@ const UsdtWallet = GT.Object({ throw mapError(balance) } if (balance instanceof USDTAmount) { - return Number(balance.asUsdCents(4)) + return Number(balance.asUsdCents()) } if (balance instanceof USDAmount) { return Number(balance.asCents(8)) diff --git a/src/graphql/shared/types/scalar/usd-cents.ts b/src/graphql/shared/types/scalar/usd-cents.ts index 848ae8e4f..5991b4045 100644 --- a/src/graphql/shared/types/scalar/usd-cents.ts +++ b/src/graphql/shared/types/scalar/usd-cents.ts @@ -17,7 +17,7 @@ const USDCentsScalar = GT.Scalar({ return Number(value.asCents(2)) } if (value instanceof USDTAmount) { - return Number(value.asUsdCents(4)) + return Number(value.asUsdCents()) } else throw new Error(`Failed to serialize USDAmount: ${value}`) } diff --git a/test/flash/unit/domain/shared/Money.spec.ts b/test/flash/unit/domain/shared/Money.spec.ts index a9e3baacc..ef6065309 100644 --- a/test/flash/unit/domain/shared/Money.spec.ts +++ b/test/flash/unit/domain/shared/Money.spec.ts @@ -157,19 +157,30 @@ describe("Money Amount", () => { expect(amount.toIbex()).toBe(0.01) }) - it("preserves provider-originated sub-cent USDT as fractional cents", () => { + it("rounds provider-originated sub-cent USDT to integer USD cents", () => { const amount = USDTAmount.smallestUnits("19446") if (amount instanceof Error) throw amount - expect(amount.asUsdCents(4)).toBe("1.9446") + expect(amount.asUsdCents()).toBe("2") }) - it("keeps generic MoneyAmount USDT construction on the app-facing cent contract", () => { + it("keeps generic MoneyAmount USDT construction on internal micro-units", () => { const amount = MoneyAmount.from("100", WalletCurrency.Usdt) if (amount instanceof Error) throw amount expect(amount).toBeInstanceOf(USDTAmount) - expect((amount as USDTAmount).asSmallestUnits()).toBe("1000000") + expect((amount as USDTAmount).asSmallestUnits()).toBe("100") + }) + + it("round-trips USDT JSON without reinterpreting micros as app cents", () => { + const amount = USDTAmount.smallestUnits("19446") + if (amount instanceof Error) throw amount + + const restored = MoneyAmount.fromJSON(amount.toJson()) + if (restored instanceof Error) throw restored + + expect(restored).toBeInstanceOf(USDTAmount) + expect((restored as USDTAmount).asSmallestUnits()).toBe("19446") }) }) }) diff --git a/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts b/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts index 0d1b1896f..acc1a0121 100644 --- a/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts +++ b/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts @@ -16,10 +16,10 @@ describe("USDCentsScalar", () => { expect(USDCentsScalar.serialize(amount)).toBe(123) }) - it("serializes USDT sub-cent amounts as fractional USD cents", () => { + it("serializes USDT sub-cent amounts as rounded integer USD cents", () => { const amount = USDTAmount.smallestUnits("9147993") if (amount instanceof Error) throw amount - expect(USDCentsScalar.serialize(amount)).toBe(914.7993) + expect(USDCentsScalar.serialize(amount)).toBe(915) }) }) From b1af1683885788372e8049164da427493ff2953b Mon Sep 17 00:00:00 2001 From: Forge0x Date: Mon, 1 Jun 2026 17:56:33 -0400 Subject: [PATCH 16/43] feat(cutover): prepare cash wallet migration for bridge (#377) * fix(graphql): make usdt wallet balance nullable (#369) * feat(cutover): add cash wallet migration state primitives * feat(cutover): persist cash wallet cutover state * feat(cutover): add cash wallet write guard * feat(cutover): classify cash wallet migration candidates * feat(cutover): add cash wallet preflight summary * feat(cutover): collect cash wallet discovery results * feat(cutover): plan primary cash wallet migrations * feat(cutover): upsert planned migration records * feat(cutover): prepare primary migration batch * feat(cutover): start migration worker checkpoint * feat(cutover): record migration source balance * feat(cutover): create balance move invoice checkpoint * feat(cutover): send balance move payment checkpoint * feat(cutover): verify balance move checkpoint * feat(cutover): create fee reimbursement invoice checkpoint * feat(cutover): complete fee reimbursement checkpoint * feat(cutover): flip default wallet checkpoint * feat(cutover): complete migration worker checkpoints * feat(cutover): provision destination checkpoint * feat(cutover): dispatch migration worker steps * feat(cutover): run locked migration batches * feat(cutover): build migration step handlers * feat(cutover): wire migration runtime services * feat(cutover): orchestrate primary migration batches * feat(cutover): add migration lifecycle controls * fix(cutover): align migration indexes with run ids * feat(cutover): add operator command script * chore(cutover): format migration state helpers * feat(cutover): preview dry-run migration plan * fix(cutover): satisfy production build types * feat(cutover): add operator controls and verification * feat(cutover): add client-aware cash wallet presentation * fix(cutover): harden operator cutover run (#376) * feat(cutover): add operator dashboard and provisioning tools * fix(cutover): use ibex oauth credentials in local setup * fix(cutover): retry wallet provisioning and refresh stale invoices * fix(cutover): retry ibex rate limits during migration payments * fix(cutover): clarify dashboard run anomalies * chore(cutover): remove local run artifacts * chore(cutover): trim bridge PR noise * chore(cutover): move unit tests to separate PR * Potential fix for pull request finding * Potential fix for pull request finding * Potential fix for pull request finding Co-authored-by: Island Bitcoin <34528298+islandbitcoin@users.noreply.github.com> --- dev/apollo-federation/supergraph.graphql | 27 +- .../cash-wallet-cutover/amount-conversion.ts | 54 + .../cash-wallet-cutover/client-capability.ts | 37 + src/app/cash-wallet-cutover/discovery.ts | 78 ++ src/app/cash-wallet-cutover/errors.ts | 11 + src/app/cash-wallet-cutover/executor.ts | 39 + src/app/cash-wallet-cutover/guard.ts | 77 ++ src/app/cash-wallet-cutover/handlers.ts | 177 ++++ src/app/cash-wallet-cutover/index.ts | 24 + src/app/cash-wallet-cutover/index.types.d.ts | 67 ++ src/app/cash-wallet-cutover/lifecycle.ts | 174 ++++ .../cash-wallet-cutover/migration-records.ts | 28 + .../cash-wallet-cutover/operator-dashboard.ts | 951 +++++++++++++++++ src/app/cash-wallet-cutover/orchestrator.ts | 55 + src/app/cash-wallet-cutover/planner.ts | 41 + src/app/cash-wallet-cutover/preflight.ts | 53 + src/app/cash-wallet-cutover/prepare.ts | 63 ++ .../presentation-for-account.ts | 94 ++ src/app/cash-wallet-cutover/presentation.ts | 82 ++ src/app/cash-wallet-cutover/preview.ts | 54 + .../provision-usdt-wallets.ts | 160 +++ src/app/cash-wallet-cutover/runner.ts | 142 +++ .../cash-wallet-cutover/runtime-services.ts | 331 ++++++ src/app/cash-wallet-cutover/state-machine.ts | 49 + src/app/cash-wallet-cutover/worker.ts | 597 +++++++++++ src/app/errors.ts | 2 + src/app/index.ts | 3 + src/graphql/admin/mutations.ts | 5 +- src/graphql/admin/queries.ts | 2 + .../mutation/cash-wallet-cutover-update.ts | 61 ++ src/graphql/admin/schema.graphql | 36 +- .../types/payload/cash-wallet-cutover.ts | 13 + src/graphql/error-map.ts | 36 + src/graphql/public/queries.ts | 2 + .../mutation/intraledger-usd-payment-send.ts | 22 +- .../ln-noamount-usd-invoice-fee-probe.ts | 28 +- .../ln-noamount-usd-invoice-payment-send.ts | 19 +- .../root/mutation/ln-usd-invoice-create.ts | 21 +- .../root/mutation/ln-usd-invoice-fee-probe.ts | 19 +- .../root/mutation/onchain-usd-payment-send.ts | 31 +- .../root/query/account-default-wallet-id.ts | 14 +- .../root/query/account-default-wallet.ts | 23 +- src/graphql/public/schema.graphql | 22 +- .../public/types/object/business-account.ts | 65 +- .../public/types/object/consumer-account.ts | 47 +- .../shared/root/query/cash-wallet-cutover.ts | 14 + .../types/object/cash-wallet-cutover.ts | 21 + src/graphql/shared/types/object/usd-wallet.ts | 38 +- .../shared/types/object/usdt-wallet.ts | 2 +- .../types/scalar/cash-wallet-cutover-state.ts | 12 + src/scripts/cash-wallet-cutover-dashboard.ts | 983 ++++++++++++++++++ src/scripts/cash-wallet-cutover.ts | 166 +++ src/servers/graphql-main-server.ts | 17 +- src/servers/index.files.d.ts | 1 + src/servers/middlewares/session.ts | 5 +- src/servers/ws-server.ts | 19 +- src/services/ibex/client.ts | 53 +- src/services/mongoose/cash-wallet-cutover.ts | 348 +++++++ src/services/mongoose/index.ts | 1 + src/services/mongoose/schema.ts | 62 ++ src/services/mongoose/schema.types.d.ts | 46 + 61 files changed, 5604 insertions(+), 120 deletions(-) create mode 100644 src/app/cash-wallet-cutover/amount-conversion.ts create mode 100644 src/app/cash-wallet-cutover/client-capability.ts create mode 100644 src/app/cash-wallet-cutover/discovery.ts create mode 100644 src/app/cash-wallet-cutover/errors.ts create mode 100644 src/app/cash-wallet-cutover/executor.ts create mode 100644 src/app/cash-wallet-cutover/guard.ts create mode 100644 src/app/cash-wallet-cutover/handlers.ts create mode 100644 src/app/cash-wallet-cutover/index.ts create mode 100644 src/app/cash-wallet-cutover/index.types.d.ts create mode 100644 src/app/cash-wallet-cutover/lifecycle.ts create mode 100644 src/app/cash-wallet-cutover/migration-records.ts create mode 100644 src/app/cash-wallet-cutover/operator-dashboard.ts create mode 100644 src/app/cash-wallet-cutover/orchestrator.ts create mode 100644 src/app/cash-wallet-cutover/planner.ts create mode 100644 src/app/cash-wallet-cutover/preflight.ts create mode 100644 src/app/cash-wallet-cutover/prepare.ts create mode 100644 src/app/cash-wallet-cutover/presentation-for-account.ts create mode 100644 src/app/cash-wallet-cutover/presentation.ts create mode 100644 src/app/cash-wallet-cutover/preview.ts create mode 100644 src/app/cash-wallet-cutover/provision-usdt-wallets.ts create mode 100644 src/app/cash-wallet-cutover/runner.ts create mode 100644 src/app/cash-wallet-cutover/runtime-services.ts create mode 100644 src/app/cash-wallet-cutover/state-machine.ts create mode 100644 src/app/cash-wallet-cutover/worker.ts create mode 100644 src/graphql/admin/root/mutation/cash-wallet-cutover-update.ts create mode 100644 src/graphql/admin/types/payload/cash-wallet-cutover.ts create mode 100644 src/graphql/shared/root/query/cash-wallet-cutover.ts create mode 100644 src/graphql/shared/types/object/cash-wallet-cutover.ts create mode 100644 src/graphql/shared/types/scalar/cash-wallet-cutover-state.ts create mode 100644 src/scripts/cash-wallet-cutover-dashboard.ts create mode 100644 src/scripts/cash-wallet-cutover.ts create mode 100644 src/services/mongoose/cash-wallet-cutover.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index bb5202f04..09c7b3662 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -343,6 +343,7 @@ type BridgeWithdrawal amount: String! createdAt: String! currency: String! + failureReason: String id: ID! status: String! } @@ -501,6 +502,29 @@ type CashoutOffer walletId: WalletId! } +type CashWalletCutover + @join__type(graph: PUBLIC) +{ + completedAt: Timestamp + cutoverVersion: Int! + pauseReason: String + pausedAt: Timestamp + runId: String + scheduledAt: Timestamp + startedAt: Timestamp + state: CashWalletCutoverState! + updatedAt: Timestamp! + updatedBy: String +} + +enum CashWalletCutoverState + @join__type(graph: PUBLIC) +{ + COMPLETE @join__enumValue(graph: PUBLIC) + IN_PROGRESS @join__enumValue(graph: PUBLIC) + PRE @join__enumValue(graph: PUBLIC) +} + """(Positive) Cent amount (1/100 of a dollar)""" scalar CentAmount @join__type(graph: PUBLIC) @@ -1651,6 +1675,7 @@ type Query btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] businessMapMarkers: [MapMarker!]! + cashWalletCutover: CashWalletCutover! currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload @@ -2039,7 +2064,7 @@ type UsdtWallet implements Wallet @join__type(graph: PUBLIC) { accountId: ID! - balance: FractionalCentAmount! + balance: FractionalCentAmount id: ID! isExternal: Boolean! lnurlp: Lnurl diff --git a/src/app/cash-wallet-cutover/amount-conversion.ts b/src/app/cash-wallet-cutover/amount-conversion.ts new file mode 100644 index 000000000..fd6bd4e09 --- /dev/null +++ b/src/app/cash-wallet-cutover/amount-conversion.ts @@ -0,0 +1,54 @@ +import { InvalidCashWalletCutoverAmountError } from "./errors" + +const USDT_MICROS_PER_USD_CENT = 10_000n + +const parseNonNegativeInteger = ( + value: string, +): bigint | InvalidCashWalletCutoverAmountError => { + if (!/^\d+$/.test(value)) { + return new InvalidCashWalletCutoverAmountError( + `Invalid non-negative integer amount: ${value}`, + ) + } + return BigInt(value) +} + +export const usdCentsToUsdtMicros = ( + usdCents: string, +): string | InvalidCashWalletCutoverAmountError => { + const parsed = parseNonNegativeInteger(usdCents) + if (parsed instanceof Error) return parsed + return (parsed * USDT_MICROS_PER_USD_CENT).toString() +} + +export const feeUsdCentsToUsdtMicros = usdCentsToUsdtMicros + +export const usdtMicrosToUsdCentsCeil = ( + usdtMicros: string, +): string | InvalidCashWalletCutoverAmountError => { + const parsed = parseNonNegativeInteger(usdtMicros) + if (parsed instanceof Error) return parsed + if (parsed === 0n) return "0" + return ((parsed + USDT_MICROS_PER_USD_CENT - 1n) / USDT_MICROS_PER_USD_CENT).toString() +} + +export const destinationShortfallUsdtMicros = ({ + targetUsdtMicros, + startingUsdtMicros, + currentUsdtMicros, +}: { + targetUsdtMicros: string + startingUsdtMicros: string + currentUsdtMicros: string +}): string | InvalidCashWalletCutoverAmountError => { + const target = parseNonNegativeInteger(targetUsdtMicros) + if (target instanceof Error) return target + const starting = parseNonNegativeInteger(startingUsdtMicros) + if (starting instanceof Error) return starting + const current = parseNonNegativeInteger(currentUsdtMicros) + if (current instanceof Error) return current + + const received = current > starting ? current - starting : 0n + if (received >= target) return "0" + return (target - received).toString() +} diff --git a/src/app/cash-wallet-cutover/client-capability.ts b/src/app/cash-wallet-cutover/client-capability.ts new file mode 100644 index 000000000..e30b95fb5 --- /dev/null +++ b/src/app/cash-wallet-cutover/client-capability.ts @@ -0,0 +1,37 @@ +export const CASH_WALLET_USDT_CLIENT_CAPABILITY = "cash-wallet-usdt-v1" + +export type CashWalletPresentation = "legacy_compat" | "usdt" + +export type CashWalletClientCapabilities = { + cashWalletPresentation: CashWalletPresentation + hasUsdtCashWalletSupport: boolean +} + +export const DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES: CashWalletClientCapabilities = { + cashWalletPresentation: "legacy_compat", + hasUsdtCashWalletSupport: false, +} + +export const parseCashWalletClientCapabilities = ( + headers: Record, +): CashWalletClientCapabilities => { + const values = Object.entries(headers).flatMap(([key, raw]) => { + if (key.toLowerCase() !== "x-flash-client-capabilities") return [] + if (typeof raw === "string") return [raw] + if (Array.isArray(raw)) + return raw.filter((value): value is string => typeof value === "string") + return [] + }) + + const capabilities = values + .flatMap((value) => value.split(",")) + .map((value) => value.trim().toLowerCase()) + + const hasUsdtCashWalletSupport = capabilities.includes( + CASH_WALLET_USDT_CLIENT_CAPABILITY, + ) + + if (!hasUsdtCashWalletSupport) return DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES + + return { cashWalletPresentation: "usdt", hasUsdtCashWalletSupport } +} diff --git a/src/app/cash-wallet-cutover/discovery.ts b/src/app/cash-wallet-cutover/discovery.ts new file mode 100644 index 000000000..14304c10b --- /dev/null +++ b/src/app/cash-wallet-cutover/discovery.ts @@ -0,0 +1,78 @@ +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +export type CashWalletCutoverDiscoveryStatus = + | "legacy_default" + | "already_usdt" + | "residual_legacy_usd" + | "missing_legacy_usd" + | "missing_destination_usdt" + +export type CashWalletCutoverDiscovery = { + status: CashWalletCutoverDiscoveryStatus + accountId: AccountId + accountUuid?: AccountUuid + legacyUsdWalletId?: WalletId + destinationUsdtWalletId?: WalletId + previousDefaultWalletId: WalletId +} + +export const classifyCashWalletsForCutover = ({ + account, + wallets, +}: { + account: Account + wallets: Wallet[] +}): CashWalletCutoverDiscovery => { + const legacyUsdWallet = wallets.find( + (wallet) => + wallet.type === WalletType.Checking && wallet.currency === WalletCurrency.Usd, + ) + const destinationUsdtWallet = wallets.find( + (wallet) => + wallet.type === WalletType.Checking && wallet.currency === WalletCurrency.Usdt, + ) + + const base = { + accountId: account.id, + accountUuid: account.uuid, + legacyUsdWalletId: legacyUsdWallet?.id, + destinationUsdtWalletId: destinationUsdtWallet?.id, + previousDefaultWalletId: account.defaultWalletId, + } + + if (!legacyUsdWallet) return { ...base, status: "missing_legacy_usd" } + if (!destinationUsdtWallet) return { ...base, status: "missing_destination_usdt" } + + if (account.defaultWalletId === legacyUsdWallet.id) { + return { ...base, status: "legacy_default" } + } + + if (account.defaultWalletId === destinationUsdtWallet.id) { + return { ...base, status: "already_usdt" } + } + + return { ...base, status: "residual_legacy_usd" } +} + +export const discoverCashWalletCutoverAccounts = async ({ + accountsRepo, + walletsRepo, +}: { + accountsRepo: Pick + walletsRepo: Pick +}): Promise => { + const accounts = accountsRepo.listUnlockedAccounts() + if (accounts instanceof Error) return accounts + + const discoveries: CashWalletCutoverDiscovery[] = [] + + for await (const account of accounts) { + const wallets = await walletsRepo.listByAccountId(account.id) + if (wallets instanceof Error) return wallets + + discoveries.push(classifyCashWalletsForCutover({ account, wallets })) + } + + return discoveries +} diff --git a/src/app/cash-wallet-cutover/errors.ts b/src/app/cash-wallet-cutover/errors.ts new file mode 100644 index 000000000..373dcd742 --- /dev/null +++ b/src/app/cash-wallet-cutover/errors.ts @@ -0,0 +1,11 @@ +import { DomainError, ValidationError } from "@domain/shared" + +export class InvalidCashWalletCutoverAmountError extends ValidationError {} +export class InvalidCashWalletMigrationTransitionError extends ValidationError {} +export class InvalidCashWalletCutoverStateTransitionError extends ValidationError {} +export class CashWalletCutoverInProgressError extends ValidationError {} +export class CashWalletMigrationFailedError extends DomainError {} +export class CashWalletMissingLegacyUsdWalletError extends DomainError {} +export class CashWalletMissingUsdtWalletError extends DomainError {} +export class CashWalletCutoverPreflightError extends DomainError {} +export class CashWalletCutoverTreasuryInsufficientBalanceError extends DomainError {} diff --git a/src/app/cash-wallet-cutover/executor.ts b/src/app/cash-wallet-cutover/executor.ts new file mode 100644 index 000000000..5df68aa00 --- /dev/null +++ b/src/app/cash-wallet-cutover/executor.ts @@ -0,0 +1,39 @@ +type RunnableCashWalletMigrationStatus = Exclude< + CashWalletMigrationStatus, + | "complete" + | "failed" + | "requires_operator_review" + | "skipped_already_migrated" + | "rollback_started" + | "rolled_back" +> + +type CashWalletMigrationStepHandler = ( + migration: CashWalletMigration, +) => Promise + +export type CashWalletMigrationStepHandlers = Record< + RunnableCashWalletMigrationStatus, + CashWalletMigrationStepHandler +> + +const terminalStatuses: CashWalletMigrationStatus[] = [ + "complete", + "failed", + "requires_operator_review", + "skipped_already_migrated", + "rollback_started", + "rolled_back", +] + +export const executeCashWalletMigrationStep = async ({ + migration, + handlers, +}: { + migration: CashWalletMigration + handlers: CashWalletMigrationStepHandlers +}): Promise => { + if (terminalStatuses.includes(migration.status)) return migration + + return handlers[migration.status as RunnableCashWalletMigrationStatus](migration) +} diff --git a/src/app/cash-wallet-cutover/guard.ts b/src/app/cash-wallet-cutover/guard.ts new file mode 100644 index 000000000..214968d4c --- /dev/null +++ b/src/app/cash-wallet-cutover/guard.ts @@ -0,0 +1,77 @@ +import { + CashWalletCutoverInProgressError, + CashWalletMigrationFailedError, +} from "./errors" +import { CashWalletClientCapabilities } from "./client-capability" + +export { CashWalletCutoverInProgressError, CashWalletMigrationFailedError } + +export type CashWalletCutoverRoute = "legacy_usd" | "usdt" +export type CashWalletCutoverPresentation = "legacy_usd" | "legacy_usd_compat" | "usdt" + +export type CashWalletCutoverDecision = { + presentation: CashWalletCutoverPresentation +} + +const ACTIVE_STATUSES: CashWalletMigrationStatus[] = [ + "started", + "provisioned", + "balance_read", + "invoice_created", + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "fee_reimbursed", + "pointer_flipped", + "rollback_started", +] + +export const evaluateCashWalletCutoverGuard = ({ + cutover, + migration, +}: { + cutover: CashWalletCutoverConfig + migration?: CashWalletMigration | null +}): { route: CashWalletCutoverRoute } | ApplicationError => { + if (cutover.state === "pre") return { route: "legacy_usd" } + if (cutover.state === "complete") return { route: "usdt" } + + if (!migration || migration.status === "not_started") return { route: "legacy_usd" } + if ( + migration.status === "complete" || + migration.status === "skipped_already_migrated" + ) { + return { route: "usdt" } + } + if (migration.status === "failed" || migration.status === "requires_operator_review") { + return new CashWalletMigrationFailedError() + } + if (ACTIVE_STATUSES.includes(migration.status)) { + return new CashWalletCutoverInProgressError() + } + + return { route: "legacy_usd" } +} + +export const evaluateCashWalletCutoverPresentation = ({ + cutover, + migration, + client, +}: { + cutover: CashWalletCutoverConfig + migration?: CashWalletMigration | null + client: CashWalletClientCapabilities +}): CashWalletCutoverDecision | ApplicationError => { + const guard = evaluateCashWalletCutoverGuard({ cutover, migration }) + if (guard instanceof Error) return guard + + if (guard.route === "legacy_usd") { + return { presentation: "legacy_usd" } + } + + return { + presentation: client.hasUsdtCashWalletSupport ? "usdt" : "legacy_usd_compat", + } +} diff --git a/src/app/cash-wallet-cutover/handlers.ts b/src/app/cash-wallet-cutover/handlers.ts new file mode 100644 index 000000000..b09756c1d --- /dev/null +++ b/src/app/cash-wallet-cutover/handlers.ts @@ -0,0 +1,177 @@ +import { + completeCashWalletMigration, + createCashWalletMigrationBalanceMoveInvoice, + createCashWalletMigrationFeeReimbursementInvoice, + flipCashWalletMigrationDefaultPointer, + markCashWalletMigrationBalanceMoveSent, + markCashWalletMigrationFeeReimbursed, + provisionCashWalletMigrationDestination, + recordCashWalletMigrationBalance, + sendCashWalletMigrationBalanceMovePayment, + sendCashWalletMigrationFeeReimbursementPayment, + skipCashWalletMigrationFeeReimbursement, + startCashWalletMigration, + verifyCashWalletMigrationBalanceMove, + verifyCashWalletMigrationLegacyZero, +} from "./worker" +import { CashWalletMigrationStepHandlers } from "./executor" + +type CashWalletMigrationTransitionRepository = Parameters< + typeof startCashWalletMigration +>[0]["migrationsRepo"] + +type CashWalletMigrationHandlerServices = { + now(): Date + provisioningService: Parameters< + typeof provisionCashWalletMigrationDestination + >[0]["provisioningService"] + balanceReader: { + readSourceBalanceUsdCents( + migration: CashWalletMigration, + ): Promise + readDestinationBalanceUsdtMicros( + migration: CashWalletMigration, + ): Promise + } + invoiceService: Parameters< + typeof createCashWalletMigrationBalanceMoveInvoice + >[0]["invoiceService"] & + Parameters[0]["invoiceService"] + paymentService: Parameters< + typeof sendCashWalletMigrationBalanceMovePayment + >[0]["paymentService"] + balanceVerifier: Parameters< + typeof verifyCashWalletMigrationBalanceMove + >[0]["balanceVerifier"] + feeService: { + readFeeAmountUsdtMicros( + migration: CashWalletMigration, + ): Promise + } + treasuryService: { + getTreasuryWalletId(): Promise + } + pointerService: Parameters< + typeof flipCashWalletMigrationDefaultPointer + >[0]["pointerService"] + legacyWalletVerifier: Parameters< + typeof verifyCashWalletMigrationLegacyZero + >[0]["legacyWalletVerifier"] +} + +export const createCashWalletMigrationStepHandlers = ({ + migrationsRepo, + services, +}: { + migrationsRepo: CashWalletMigrationTransitionRepository + services: CashWalletMigrationHandlerServices +}): CashWalletMigrationStepHandlers => ({ + not_started: (migration) => + startCashWalletMigration({ + migration, + migrationsRepo, + startedAt: services.now(), + }), + started: (migration) => + provisionCashWalletMigrationDestination({ + migration, + migrationsRepo, + provisioningService: services.provisioningService, + }), + provisioned: async (migration) => { + const sourceBalanceUsdCents = + await services.balanceReader.readSourceBalanceUsdCents(migration) + if (sourceBalanceUsdCents instanceof Error) return sourceBalanceUsdCents + const destinationStartingBalanceUsdtMicros = + await services.balanceReader.readDestinationBalanceUsdtMicros(migration) + if (destinationStartingBalanceUsdtMicros instanceof Error) { + return destinationStartingBalanceUsdtMicros + } + return recordCashWalletMigrationBalance({ + migration, + migrationsRepo, + sourceBalanceUsdCents, + destinationStartingBalanceUsdtMicros, + }) + }, + balance_read: (migration) => { + if (migration.destinationAmountUsdtMicros === "0") { + return flipCashWalletMigrationDefaultPointer({ + migration, + migrationsRepo, + pointerService: services.pointerService, + }) + } + + return createCashWalletMigrationBalanceMoveInvoice({ + migration, + migrationsRepo, + invoiceService: services.invoiceService, + }) + }, + invoice_created: (migration) => + sendCashWalletMigrationBalanceMovePayment({ + migration, + migrationsRepo, + paymentService: services.paymentService, + invoiceService: services.invoiceService, + now: services.now, + }), + balance_move_sending: (migration) => + markCashWalletMigrationBalanceMoveSent({ migration, migrationsRepo }), + balance_move_sent: (migration) => + verifyCashWalletMigrationBalanceMove({ + migration, + migrationsRepo, + balanceVerifier: services.balanceVerifier, + }), + balance_move_verified: async (migration) => { + const feeAmountUsdtMicros = + await services.feeService.readFeeAmountUsdtMicros(migration) + if (feeAmountUsdtMicros instanceof Error) return feeAmountUsdtMicros + if (feeAmountUsdtMicros === "0") { + return skipCashWalletMigrationFeeReimbursement({ + migration, + migrationsRepo, + }) + } + return createCashWalletMigrationFeeReimbursementInvoice({ + migration, + migrationsRepo, + invoiceService: services.invoiceService, + feeAmountUsdtMicros, + }) + }, + fee_reimbursement_invoice_created: async (migration) => { + const treasuryWalletId = await services.treasuryService.getTreasuryWalletId() + if (treasuryWalletId instanceof Error) return treasuryWalletId + return sendCashWalletMigrationFeeReimbursementPayment({ + migration, + migrationsRepo, + paymentService: services.paymentService, + invoiceService: services.invoiceService, + now: services.now, + treasuryWalletId, + }) + }, + fee_reimbursement_sending: (migration) => + markCashWalletMigrationFeeReimbursed({ migration, migrationsRepo }), + fee_reimbursed: (migration) => + flipCashWalletMigrationDefaultPointer({ + migration, + migrationsRepo, + pointerService: services.pointerService, + }), + pointer_flipped: (migration) => + verifyCashWalletMigrationLegacyZero({ + migration, + migrationsRepo, + legacyWalletVerifier: services.legacyWalletVerifier, + }), + legacy_zero_verified: (migration) => + completeCashWalletMigration({ + migration, + migrationsRepo, + completedAt: services.now(), + }), +}) diff --git a/src/app/cash-wallet-cutover/index.ts b/src/app/cash-wallet-cutover/index.ts new file mode 100644 index 000000000..67d632d84 --- /dev/null +++ b/src/app/cash-wallet-cutover/index.ts @@ -0,0 +1,24 @@ +export * from "./amount-conversion" +export * from "./client-capability" +export * from "./errors" +export * from "./presentation" +export * from "./presentation-for-account" +export * from "./state-machine" +export { + evaluateCashWalletCutoverGuard, + evaluateCashWalletCutoverPresentation, +} from "./guard" +export * from "./discovery" +export * from "./preflight" +export * from "./planner" +export * from "./migration-records" +export * from "./prepare" +export * from "./worker" +export * from "./executor" +export * from "./runner" +export * from "./handlers" +export * from "./runtime-services" +export * from "./orchestrator" +export * from "./lifecycle" +export * from "./preview" +export * from "./provision-usdt-wallets" diff --git a/src/app/cash-wallet-cutover/index.types.d.ts b/src/app/cash-wallet-cutover/index.types.d.ts new file mode 100644 index 000000000..3f16650b7 --- /dev/null +++ b/src/app/cash-wallet-cutover/index.types.d.ts @@ -0,0 +1,67 @@ +type CashWalletCutoverState = "pre" | "in_progress" | "complete" + +type CashWalletMigrationStatus = + | "not_started" + | "started" + | "provisioned" + | "balance_read" + | "invoice_created" + | "balance_move_sending" + | "balance_move_sent" + | "balance_move_verified" + | "fee_reimbursement_invoice_created" + | "fee_reimbursement_sending" + | "fee_reimbursed" + | "pointer_flipped" + | "legacy_zero_verified" + | "complete" + | "failed" + | "requires_operator_review" + | "skipped_already_migrated" + | "rollback_started" + | "rolled_back" + +type CashWalletCutoverConfig = { + state: CashWalletCutoverState + scheduledAt?: Date + startedAt?: Date + completedAt?: Date + pausedAt?: Date + pauseReason?: string + updatedBy?: string + cutoverVersion: number + runId?: string + updatedAt: Date +} + +type CashWalletMigration = { + id: string + accountId: AccountId + accountUuid?: AccountUuid + legacyUsdWalletId: WalletId + destinationUsdtWalletId: WalletId + previousDefaultWalletId?: WalletId + cutoverVersion: number + runId: string + status: CashWalletMigrationStatus + sourceBalanceUsdCents?: string + destinationAmountUsdtMicros?: string + destinationStartingBalanceUsdtMicros?: string + feeAmountUsdCents?: string + feeAmountUsdtMicros?: string + balanceMoveInvoicePaymentRequest?: string + balanceMoveInvoicePaymentHash?: string + balanceMovePaymentTransactionId?: string + feeReimbursementInvoicePaymentRequest?: string + feeReimbursementInvoicePaymentHash?: string + feeReimbursementPaymentTransactionId?: string + estimatedFee?: boolean + idempotencyKey: string + attempts: number + lastError?: string + lockedAt?: Date + lockedBy?: string + startedAt?: Date + completedAt?: Date + updatedAt: Date +} diff --git a/src/app/cash-wallet-cutover/lifecycle.ts b/src/app/cash-wallet-cutover/lifecycle.ts new file mode 100644 index 000000000..80e4fd848 --- /dev/null +++ b/src/app/cash-wallet-cutover/lifecycle.ts @@ -0,0 +1,174 @@ +import { CashWalletCutoverRepository } from "@services/mongoose" + +import { + CashWalletCutoverInProgressError, + CashWalletMigrationFailedError, + InvalidCashWalletCutoverStateTransitionError, +} from "./errors" + +const migrationStatuses: CashWalletMigrationStatus[] = [ + "not_started", + "started", + "provisioned", + "balance_read", + "invoice_created", + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "fee_reimbursed", + "pointer_flipped", + "legacy_zero_verified", + "complete", + "failed", + "requires_operator_review", + "skipped_already_migrated", + "rollback_started", + "rolled_back", +] + +type CashWalletCutoverLifecycleRepository = { + getConfig: () => Promise + updateConfig: ( + patch: Partial, + actor?: string, + ) => Promise + listRunnableMigrations: ({ + cutoverVersion, + runId, + limit, + }: { + cutoverVersion: number + runId: string + limit?: number + }) => Promise + countByStatus: ({ + cutoverVersion, + runId, + status, + }: { + cutoverVersion: number + runId: string + status: CashWalletMigrationStatus + }) => Promise +} + +export type CashWalletCutoverStatusReport = { + config: CashWalletCutoverConfig + countsByStatus: Partial> +} + +export const startPrimaryCashWalletCutover = async ({ + cutoverVersion, + runId, + actor, + now = new Date(), + migrationsRepo = CashWalletCutoverRepository(), +}: { + cutoverVersion: number + runId: string + actor: string + now?: Date + migrationsRepo?: CashWalletCutoverLifecycleRepository +}): Promise => { + const config = await migrationsRepo.getConfig() + if (config instanceof Error) return config + + if (config.state === "complete") { + return new InvalidCashWalletCutoverStateTransitionError( + "Cash wallet cutover is already complete", + ) + } + + if (config.state === "in_progress") { + if (config.runId === runId && config.cutoverVersion === cutoverVersion) return config + return new CashWalletCutoverInProgressError( + "Cash wallet cutover is already in progress", + ) + } + + return migrationsRepo.updateConfig( + { + state: "in_progress", + cutoverVersion, + runId, + startedAt: now, + scheduledAt: undefined, + pausedAt: undefined, + pauseReason: undefined, + }, + actor, + ) +} + +export const completePrimaryCashWalletCutover = async ({ + cutoverVersion, + runId, + actor, + now = new Date(), + migrationsRepo = CashWalletCutoverRepository(), +}: { + cutoverVersion: number + runId: string + actor: string + now?: Date + migrationsRepo?: CashWalletCutoverLifecycleRepository +}): Promise => { + const failedCount = await migrationsRepo.countByStatus({ + cutoverVersion, + runId, + status: "failed", + }) + if (failedCount instanceof Error) return failedCount + if (failedCount > 0) return new CashWalletMigrationFailedError() + + const reviewCount = await migrationsRepo.countByStatus({ + cutoverVersion, + runId, + status: "requires_operator_review", + }) + if (reviewCount instanceof Error) return reviewCount + if (reviewCount > 0) return new CashWalletMigrationFailedError() + + const runnable = await migrationsRepo.listRunnableMigrations({ + cutoverVersion, + runId, + limit: 1, + }) + if (runnable instanceof Error) return runnable + if (runnable.length > 0) return new CashWalletCutoverInProgressError() + + return migrationsRepo.updateConfig( + { + state: "complete", + cutoverVersion, + runId, + completedAt: now, + }, + actor, + ) +} + +export const getPrimaryCashWalletCutoverStatus = async ({ + cutoverVersion, + runId, + migrationsRepo = CashWalletCutoverRepository(), +}: { + cutoverVersion: number + runId: string + migrationsRepo?: CashWalletCutoverLifecycleRepository +}): Promise => { + const config = await migrationsRepo.getConfig() + if (config instanceof Error) return config + + const countsByStatus: Partial> = {} + + for (const status of migrationStatuses) { + const count = await migrationsRepo.countByStatus({ cutoverVersion, runId, status }) + if (count instanceof Error) return count + if (count > 0) countsByStatus[status] = count + } + + return { config, countsByStatus } +} diff --git a/src/app/cash-wallet-cutover/migration-records.ts b/src/app/cash-wallet-cutover/migration-records.ts new file mode 100644 index 000000000..9ac33b8d0 --- /dev/null +++ b/src/app/cash-wallet-cutover/migration-records.ts @@ -0,0 +1,28 @@ +import { PrimaryCashWalletMigrationPlan } from "./planner" + +type CashWalletMigrationRecordsRepository = { + upsertMigration( + args: PrimaryCashWalletMigrationPlan, + ): Promise +} + +export type { CashWalletMigrationRecordsRepository } + +export const upsertPrimaryCashWalletMigrationRecords = async ({ + migrationsRepo, + plans, +}: { + migrationsRepo: CashWalletMigrationRecordsRepository + plans: PrimaryCashWalletMigrationPlan[] +}): Promise => { + const migrations: CashWalletMigration[] = [] + + for (const plan of plans) { + const migration = await migrationsRepo.upsertMigration(plan) + if (migration instanceof Error) return migration + + migrations.push(migration) + } + + return migrations +} diff --git a/src/app/cash-wallet-cutover/operator-dashboard.ts b/src/app/cash-wallet-cutover/operator-dashboard.ts new file mode 100644 index 000000000..63d4f8faa --- /dev/null +++ b/src/app/cash-wallet-cutover/operator-dashboard.ts @@ -0,0 +1,951 @@ +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +import { CashWalletCutoverDiscovery } from "./discovery" +import { CashWalletCutoverPreflightReport } from "./preflight" + +export type CashWalletCutoverOperatorManifestAccount = { + batchRunId?: string + index?: number + phone?: string + username?: string + accountId: AccountId + accountUuid?: AccountUuid + expectedUsdWalletId?: WalletId + expectedUsdtWalletId?: WalletId +} + +export type OperatorBalanceStatus = "loading" | "fresh" | "error" + +export type OperatorBalance = { + currency: WalletCurrency + display: string + minorUnits: string + minorUnitsNumber: number + status?: OperatorBalanceStatus + error?: string +} + +export type OperatorWallet = { + id: WalletId + currency: WalletCurrency + expected: boolean + balance: OperatorBalance +} + +export type OperatorAccount = { + batchRunId?: string + index?: number + phone?: string + username?: string + accountId: AccountId + accountUuid?: AccountUuid + expectedUsdWalletId?: WalletId + expectedUsdtWalletId?: WalletId + watchlisted: boolean + defaultWalletId?: WalletId + defaultWalletCurrency?: WalletCurrency + walletCount: number + usdWallets: OperatorWallet[] + usdtWallets: OperatorWallet[] + migrationStatus: CashWalletMigrationStatus | "none" + migrationUpdatedAt?: string + cutoverBalanceAudit?: OperatorCutoverBalanceAudit + anomalies: string[] +} + +export type OperatorCutoverBalanceAudit = { + status: "loading" | "shortfall" | "verified" + sourceUsdCents: number + expectedMinimumUsdtMicros: number + destinationStartingBalanceUsdtMicros: number + currentDestinationBalanceUsdtMicros: number + finalDeltaUsdtMicros: number + roundingSubsidyUsdtMicros: number + shortfallUsdtMicros: number +} + +export type OperatorTreasuryAccount = { + accountId: AccountId + accountUuid?: AccountUuid + role?: string + defaultWalletId?: WalletId + defaultWalletCurrency?: WalletCurrency + walletCount: number + usdWallets: OperatorWallet[] + usdtWallets: OperatorWallet[] + anomalies: string[] +} + +export type OperatorTreasurySummary = { + accounts: number + wallets: number + usdTotalCents: number + usdtTotalMicros: number +} + +export type OperatorReconciliationSummary = { + customerTotalCents: number + treasuryTotalCents: number + systemTotalCents: number +} + +export type CashWalletCutoverOperatorSnapshot = { + generatedAt: string + cutover: { + state: CashWalletCutoverState + cutoverVersion: number + runId?: string + updatedAt?: string + } + preflight?: CashWalletCutoverPreflightReport + summary: { + accounts: number + wallets: { + current: number + target: number + usd: number + usdt: number + missingUsdt: number + } + fundedUsdOnlyAccounts: number + usdTotalCents: number + usdtTotalMicros: number + anomalies: number + watchlistAnomalies: number + canStart: boolean + blockers: number + watchlistAccounts: number + migrationStatuses: Record + } + accounts: OperatorAccount[] + treasury: { + accounts: OperatorTreasuryAccount[] + summary: OperatorTreasurySummary + } + reconciliation: OperatorReconciliationSummary +} + +type AccountManifestRecord = { + index?: number + phone?: string + username?: string + accountId?: string + account?: { id?: string } + id?: string + accountUuid?: string + usdWalletId?: string + usdtWalletId?: string +} + +type ManifestShape = { + runId?: string + accounts?: AccountManifestRecord[] + created?: AccountManifestRecord[] +} + +type BuildSnapshotArgs = { + manifestAccounts: CashWalletCutoverOperatorManifestAccount[] + discoveredAccounts?: CashWalletCutoverDiscovery[] + accountsRepo: Pick + walletsRepo: Pick + migrationsRepo: { + getConfig: () => Promise + findMigrationByAccountId: (args: { + accountId: AccountId + cutoverVersion: number + runId: string + }) => Promise + } + getBalanceForWallet: (args: { + walletId: WalletId + currency?: WalletCurrency + }) => Promise + migrationLookup?: { + cutoverVersion: number + runId: string + } + preflightReport?: CashWalletCutoverPreflightReport + balanceReadAttempts?: number + balanceMode?: "live" | "structural" + treasuryAccountIds?: AccountId[] + now?: Date +} + +export const parseCashWalletCutoverOperatorManifest = ( + input: ManifestShape | AccountManifestRecord[], +): CashWalletCutoverOperatorManifestAccount[] => { + const batchRunId = Array.isArray(input) ? undefined : input.runId + const records = Array.isArray(input) ? input : (input.accounts ?? input.created ?? []) + + const accounts = records.map((record) => { + const accountId = record.accountId ?? record.account?.id ?? record.id + if (!accountId) { + throw new Error("Operator manifest record is missing accountId") + } + + return { + batchRunId, + index: record.index, + phone: record.phone, + username: record.username, + accountId: accountId as AccountId, + accountUuid: record.accountUuid as AccountUuid | undefined, + expectedUsdWalletId: record.usdWalletId as WalletId | undefined, + expectedUsdtWalletId: record.usdtWalletId as WalletId | undefined, + } + }) + + const seen = new Set() + for (const account of accounts) { + if (seen.has(account.accountId)) { + throw new Error(`Duplicate operator manifest accountId: ${account.accountId}`) + } + seen.add(account.accountId) + } + + return accounts +} + +const csvHeaders = [ + "generatedAt", + "cutoverState", + "cutoverVersion", + "cutoverRunId", + "cutoverUpdatedAt", + "watchlisted", + "batchRunId", + "index", + "phone", + "username", + "accountId", + "accountUuid", + "defaultWalletId", + "defaultWalletCurrency", + "expectedUsdWalletId", + "expectedUsdtWalletId", + "walletCount", + "usdWalletIds", + "usdBalanceDisplays", + "usdBalanceMinorUnits", + "usdBalanceStatuses", + "usdtWalletIds", + "usdtBalanceDisplays", + "usdtBalanceMinorUnits", + "usdtBalanceStatuses", + "migrationStatus", + "migrationUpdatedAt", + "cutoverBalanceAudit", + "anomalies", +] + +const csvValue = (value: unknown): string => { + if (value === undefined || value === null) return "" + const text = String(value) + return /[",\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text +} + +const walletIdsCsv = (wallets: OperatorWallet[]) => + wallets.map((wallet) => wallet.id).join(";") + +const walletBalanceDisplaysCsv = (wallets: OperatorWallet[]) => + wallets.map((wallet) => wallet.balance.display).join(";") + +const walletBalanceMinorUnitsCsv = (wallets: OperatorWallet[]) => + wallets.map((wallet) => wallet.balance.minorUnits).join(";") + +const walletBalanceStatusesCsv = (wallets: OperatorWallet[]) => + wallets.map((wallet) => wallet.balance.status ?? "").join(";") + +const cutoverBalanceAuditCsv = (audit?: OperatorCutoverBalanceAudit) => { + if (!audit) return "" + return [ + `status=${audit.status}`, + `expectedMinimumUsdtMicros=${audit.expectedMinimumUsdtMicros}`, + `finalDeltaUsdtMicros=${audit.finalDeltaUsdtMicros}`, + `roundingSubsidyUsdtMicros=${audit.roundingSubsidyUsdtMicros}`, + `shortfallUsdtMicros=${audit.shortfallUsdtMicros}`, + ].join(";") +} + +export const formatCashWalletCutoverOperatorSnapshotCsv = ( + snapshot: CashWalletCutoverOperatorSnapshot, +): string => { + const rows = snapshot.accounts.map((account) => + [ + snapshot.generatedAt, + snapshot.cutover.state, + snapshot.cutover.cutoverVersion, + snapshot.cutover.runId, + snapshot.cutover.updatedAt, + account.watchlisted, + account.batchRunId, + account.index, + account.phone, + account.username, + account.accountId, + account.accountUuid, + account.defaultWalletId, + account.defaultWalletCurrency, + account.expectedUsdWalletId, + account.expectedUsdtWalletId, + account.walletCount, + walletIdsCsv(account.usdWallets), + walletBalanceDisplaysCsv(account.usdWallets), + walletBalanceMinorUnitsCsv(account.usdWallets), + walletBalanceStatusesCsv(account.usdWallets), + walletIdsCsv(account.usdtWallets), + walletBalanceDisplaysCsv(account.usdtWallets), + walletBalanceMinorUnitsCsv(account.usdtWallets), + walletBalanceStatusesCsv(account.usdtWallets), + account.migrationStatus, + account.migrationUpdatedAt, + cutoverBalanceAuditCsv(account.cutoverBalanceAudit), + account.anomalies.join(";"), + ].map(csvValue), + ) + + return [csvHeaders, ...rows].map((row) => row.join(",")).join("\n") +} + +const describeError = (error: Error): string => { + const message = error.message?.split("\n")[0]?.trim() + if (message) return message + + if (error.name && error.name !== "Error") return error.name + + const rendered = String(error) + return rendered && rendered !== "[object Object]" ? rendered : "Unknown error" +} + +const balanceError = (wallet: Wallet, error: Error): OperatorBalance => ({ + currency: wallet.currency, + display: "error", + minorUnits: "0", + minorUnitsNumber: 0, + status: "error", + error: describeError(error), +}) + +export const formatOperatorBalance = ( + wallet: Wallet, + balance: USDAmount | USDTAmount | ApplicationError, +): OperatorBalance => { + if (balance instanceof Error) return balanceError(wallet, balance) + + if (wallet.currency === WalletCurrency.Usdt && balance instanceof USDTAmount) { + const micros = balance.asSmallestUnits() + return { + currency: WalletCurrency.Usdt, + display: `${(Number(micros) / 1_000_000).toFixed(2)} USDT`, + minorUnits: micros, + minorUnitsNumber: Number(micros), + status: "fresh", + } + } + + if (wallet.currency === WalletCurrency.Usd && balance instanceof USDAmount) { + const cents = balance.asCents() + return { + currency: WalletCurrency.Usd, + display: `$${balance.asDollars(2)}`, + minorUnits: cents, + minorUnitsNumber: Number(cents), + status: "fresh", + } + } + + return { + currency: wallet.currency, + display: "unexpected currency", + minorUnits: "0", + minorUnitsNumber: 0, + status: "error", + error: `Expected ${wallet.currency} balance`, + } +} + +const loadingBalance = (wallet: Wallet): OperatorBalance => ({ + currency: wallet.currency, + display: "loading", + minorUnits: "0", + minorUnitsNumber: 0, + status: "loading", +}) + +const summarizeWallet = async ({ + wallet, + expectedWalletId, + getBalanceForWallet, + balanceReadAttempts, + balanceMode, +}: { + wallet: Wallet + expectedWalletId?: WalletId + getBalanceForWallet: BuildSnapshotArgs["getBalanceForWallet"] + balanceReadAttempts: number + balanceMode: BuildSnapshotArgs["balanceMode"] +}): Promise => { + if (balanceMode === "structural") { + return { + id: wallet.id, + currency: wallet.currency, + expected: expectedWalletId === undefined || expectedWalletId === wallet.id, + balance: loadingBalance(wallet), + } + } + + let balance: USDAmount | USDTAmount | ApplicationError = new Error( + "Balance read was not attempted", + ) as ApplicationError + + for (let attempt = 0; attempt < balanceReadAttempts; attempt++) { + balance = await getBalanceForWallet({ + walletId: wallet.id, + currency: wallet.currency, + }) + if (!(balance instanceof Error)) break + } + + return { + id: wallet.id, + currency: wallet.currency, + expected: expectedWalletId === undefined || expectedWalletId === wallet.id, + balance: formatOperatorBalance(wallet, balance), + } +} + +const increment = (record: Record, key: string) => { + record[key] = (record[key] ?? 0) + 1 +} + +const accountsForDashboard = ({ + manifestAccounts, + discoveredAccounts, +}: { + manifestAccounts: CashWalletCutoverOperatorManifestAccount[] + discoveredAccounts?: CashWalletCutoverDiscovery[] +}): OperatorAccountInput[] => { + const manifestByAccountId = new Map( + manifestAccounts.map((account) => [account.accountId, account]), + ) + + if (!discoveredAccounts) { + return manifestAccounts.map((account) => ({ ...account, watchlisted: true })) + } + + const merged = discoveredAccounts.map((discovery) => { + const manifestAccount = manifestByAccountId.get(discovery.accountId) + return { + ...manifestAccount, + accountId: discovery.accountId, + accountUuid: manifestAccount?.accountUuid ?? discovery.accountUuid, + expectedUsdWalletId: + manifestAccount?.expectedUsdWalletId ?? discovery.legacyUsdWalletId, + expectedUsdtWalletId: + manifestAccount?.expectedUsdtWalletId ?? discovery.destinationUsdtWalletId, + watchlisted: manifestAccount !== undefined, + } + }) + + const discoveredAccountIds = new Set(merged.map((account) => account.accountId)) + const missingManifestAccounts = manifestAccounts + .filter((account) => !discoveredAccountIds.has(account.accountId)) + .map((account) => ({ ...account, watchlisted: true })) + + return [...merged, ...missingManifestAccounts] +} + +type OperatorAccountInput = CashWalletCutoverOperatorManifestAccount & { + watchlisted: boolean +} + +const usdTotalCentsForAccounts = ( + accounts: Array<{ usdWallets: OperatorWallet[] }>, +): number => + accounts.reduce( + (sum, account) => + sum + + account.usdWallets.reduce( + (walletSum, wallet) => walletSum + wallet.balance.minorUnitsNumber, + 0, + ), + 0, + ) + +const usdtTotalMicrosForAccounts = ( + accounts: Array<{ usdtWallets: OperatorWallet[] }>, +): number => + accounts.reduce( + (sum, account) => + sum + + account.usdtWallets.reduce( + (walletSum, wallet) => walletSum + wallet.balance.minorUnitsNumber, + 0, + ), + 0, + ) + +const parseIntegerAmount = (value?: string): number | undefined => { + if (value === undefined || !/^\d+$/.test(value)) return undefined + return Number(value) +} + +const computeCutoverBalanceAudit = ({ + migration, + usdtWallets, +}: { + migration?: CashWalletMigration | null + usdtWallets: OperatorWallet[] +}): OperatorCutoverBalanceAudit | undefined => { + if (!migration || migration.status !== "complete") return undefined + + const sourceUsdCents = parseIntegerAmount(migration.sourceBalanceUsdCents) + const expectedMinimumUsdtMicros = parseIntegerAmount( + migration.destinationAmountUsdtMicros, + ) + const destinationStartingBalanceUsdtMicros = parseIntegerAmount( + migration.destinationStartingBalanceUsdtMicros, + ) + + if ( + sourceUsdCents === undefined || + expectedMinimumUsdtMicros === undefined || + destinationStartingBalanceUsdtMicros === undefined + ) { + return undefined + } + + const destinationWallet = usdtWallets.find( + (wallet) => wallet.id === migration.destinationUsdtWalletId, + ) + if (!destinationWallet) return undefined + + if (destinationWallet.balance.status === "loading") { + return { + status: "loading", + sourceUsdCents, + expectedMinimumUsdtMicros, + destinationStartingBalanceUsdtMicros, + currentDestinationBalanceUsdtMicros: 0, + finalDeltaUsdtMicros: 0, + roundingSubsidyUsdtMicros: 0, + shortfallUsdtMicros: 0, + } + } + + const currentDestinationBalanceUsdtMicros = + destinationWallet.balance.minorUnitsNumber + const finalDeltaUsdtMicros = Math.max( + 0, + currentDestinationBalanceUsdtMicros - destinationStartingBalanceUsdtMicros, + ) + const shortfallUsdtMicros = Math.max( + 0, + expectedMinimumUsdtMicros - finalDeltaUsdtMicros, + ) + const roundingSubsidyUsdtMicros = Math.max( + 0, + finalDeltaUsdtMicros - expectedMinimumUsdtMicros, + ) + + return { + status: shortfallUsdtMicros > 0 ? "shortfall" : "verified", + sourceUsdCents, + expectedMinimumUsdtMicros, + destinationStartingBalanceUsdtMicros, + currentDestinationBalanceUsdtMicros, + finalDeltaUsdtMicros, + roundingSubsidyUsdtMicros, + shortfallUsdtMicros, + } +} + +export const refreshOperatorAccountCutoverBalanceAudit = < + T extends { + expectedUsdtWalletId?: WalletId + usdtWallets: OperatorWallet[] + cutoverBalanceAudit?: OperatorCutoverBalanceAudit + }, +>( + account: T, +): T => { + const audit = account.cutoverBalanceAudit + if (!audit) return account + + const destinationWallet = + account.usdtWallets.find((wallet) => wallet.id === account.expectedUsdtWalletId) ?? + account.usdtWallets.find((wallet) => wallet.expected) ?? + account.usdtWallets[0] + if (!destinationWallet) return account + + if (destinationWallet.balance.status === "loading") { + return { + ...account, + cutoverBalanceAudit: { + ...audit, + status: "loading", + currentDestinationBalanceUsdtMicros: 0, + finalDeltaUsdtMicros: 0, + roundingSubsidyUsdtMicros: 0, + shortfallUsdtMicros: 0, + }, + } + } + + const currentDestinationBalanceUsdtMicros = + destinationWallet.balance.minorUnitsNumber + const finalDeltaUsdtMicros = Math.max( + 0, + currentDestinationBalanceUsdtMicros - + audit.destinationStartingBalanceUsdtMicros, + ) + const shortfallUsdtMicros = Math.max( + 0, + audit.expectedMinimumUsdtMicros - finalDeltaUsdtMicros, + ) + const roundingSubsidyUsdtMicros = Math.max( + 0, + finalDeltaUsdtMicros - audit.expectedMinimumUsdtMicros, + ) + + return { + ...account, + cutoverBalanceAudit: { + ...audit, + status: shortfallUsdtMicros > 0 ? "shortfall" : "verified", + currentDestinationBalanceUsdtMicros, + finalDeltaUsdtMicros, + roundingSubsidyUsdtMicros, + shortfallUsdtMicros, + }, + } +} + +const combinedTotalCents = ({ + usdTotalCents, + usdtTotalMicros, +}: { + usdTotalCents: number + usdtTotalMicros: number +}) => usdTotalCents + usdtTotalMicros / 10_000 + +const treasurySummary = ( + accounts: OperatorTreasuryAccount[], +): OperatorTreasurySummary => ({ + accounts: accounts.length, + wallets: accounts.reduce((sum, account) => sum + account.walletCount, 0), + usdTotalCents: usdTotalCentsForAccounts(accounts), + usdtTotalMicros: usdtTotalMicrosForAccounts(accounts), +}) + +const reconciliationSummary = ({ + customerUsdTotalCents, + customerUsdtTotalMicros, + treasury, +}: { + customerUsdTotalCents: number + customerUsdtTotalMicros: number + treasury: OperatorTreasurySummary +}): OperatorReconciliationSummary => { + const customerTotalCents = combinedTotalCents({ + usdTotalCents: customerUsdTotalCents, + usdtTotalMicros: customerUsdtTotalMicros, + }) + const treasuryTotalCents = combinedTotalCents({ + usdTotalCents: treasury.usdTotalCents, + usdtTotalMicros: treasury.usdtTotalMicros, + }) + return { + customerTotalCents, + treasuryTotalCents, + systemTotalCents: customerTotalCents + treasuryTotalCents, + } +} + +const summarizeTreasuryAccount = async ({ + accountId, + accountsRepo, + walletsRepo, + getBalanceForWallet, + balanceReadAttempts, + balanceMode, +}: { + accountId: AccountId + accountsRepo: BuildSnapshotArgs["accountsRepo"] + walletsRepo: BuildSnapshotArgs["walletsRepo"] + getBalanceForWallet: BuildSnapshotArgs["getBalanceForWallet"] + balanceReadAttempts: number + balanceMode: BuildSnapshotArgs["balanceMode"] +}): Promise => { + const account = await accountsRepo.findById(accountId) + if (account instanceof Error) { + return { + accountId, + walletCount: 0, + usdWallets: [], + usdtWallets: [], + anomalies: ["missing_account"], + } + } + + const rawWallets = await walletsRepo.listByAccountId(account.id) + if (rawWallets instanceof Error) throw rawWallets + + const cashWallets = rawWallets.filter((wallet) => wallet.type === WalletType.Checking) + const usdWalletsRaw = cashWallets.filter( + (wallet) => wallet.currency === WalletCurrency.Usd, + ) + const usdtWalletsRaw = cashWallets.filter( + (wallet) => wallet.currency === WalletCurrency.Usdt, + ) + const defaultWallet = cashWallets.find((wallet) => wallet.id === account.defaultWalletId) + + const [usdWallets, usdtWallets] = await Promise.all([ + Promise.all( + usdWalletsRaw.map((wallet) => + summarizeWallet({ + wallet, + getBalanceForWallet, + balanceReadAttempts, + balanceMode, + }), + ), + ), + Promise.all( + usdtWalletsRaw.map((wallet) => + summarizeWallet({ + wallet, + getBalanceForWallet, + balanceReadAttempts, + balanceMode, + }), + ), + ), + ]) + + return { + accountId: account.id, + accountUuid: account.uuid, + role: account.role ?? "funder", + defaultWalletId: account.defaultWalletId, + defaultWalletCurrency: defaultWallet?.currency, + walletCount: rawWallets.length, + usdWallets, + usdtWallets, + anomalies: [...usdWallets, ...usdtWallets].some( + (wallet) => wallet.balance.error !== undefined, + ) + ? ["balance_error"] + : [], + } +} + +export const buildCashWalletCutoverOperatorSnapshot = async ({ + manifestAccounts, + discoveredAccounts, + accountsRepo, + walletsRepo, + migrationsRepo, + getBalanceForWallet, + migrationLookup, + preflightReport, + balanceReadAttempts = 1, + balanceMode = "live", + treasuryAccountIds = [], + now = new Date(), +}: BuildSnapshotArgs): Promise => { + const config = await migrationsRepo.getConfig() + if (config instanceof Error) throw config + const lookup = + migrationLookup ?? + (config.runId + ? { + cutoverVersion: config.cutoverVersion, + runId: config.runId, + } + : undefined) + + const accounts: OperatorAccount[] = [] + const migrationStatuses: Record = {} + const operatorAccounts = accountsForDashboard({ manifestAccounts, discoveredAccounts }) + + for (const dashboardAccount of operatorAccounts) { + const anomalies: string[] = [] + const account = await accountsRepo.findById(dashboardAccount.accountId) + if (account instanceof Error) { + increment(migrationStatuses, "none") + accounts.push({ + ...dashboardAccount, + walletCount: 0, + usdWallets: [], + usdtWallets: [], + migrationStatus: "none", + anomalies: ["missing_account"], + }) + continue + } + + const rawWallets = await walletsRepo.listByAccountId(account.id) + if (rawWallets instanceof Error) throw rawWallets + + const cashWallets = rawWallets.filter((wallet) => wallet.type === WalletType.Checking) + const usdWalletsRaw = cashWallets.filter( + (wallet) => wallet.currency === WalletCurrency.Usd, + ) + const usdtWalletsRaw = cashWallets.filter( + (wallet) => wallet.currency === WalletCurrency.Usdt, + ) + const defaultWallet = cashWallets.find( + (wallet) => wallet.id === account.defaultWalletId, + ) + + if (usdWalletsRaw.length === 0) anomalies.push("missing_usd") + if (usdtWalletsRaw.length === 0) anomalies.push("missing_usdt") + if (usdWalletsRaw.length > 1) anomalies.push("duplicate_usd") + if (usdtWalletsRaw.length > 1) anomalies.push("duplicate_usdt") + if (!defaultWallet) anomalies.push("default_not_cash") + + const [usdWallets, usdtWallets] = await Promise.all([ + Promise.all( + usdWalletsRaw.map((wallet) => + summarizeWallet({ + wallet, + expectedWalletId: dashboardAccount.expectedUsdWalletId, + getBalanceForWallet, + balanceReadAttempts, + balanceMode, + }), + ), + ), + Promise.all( + usdtWalletsRaw.map((wallet) => + summarizeWallet({ + wallet, + expectedWalletId: dashboardAccount.expectedUsdtWalletId, + getBalanceForWallet, + balanceReadAttempts, + balanceMode, + }), + ), + ), + ]) + + if ( + [...usdWallets, ...usdtWallets].some((wallet) => wallet.balance.error !== undefined) + ) { + anomalies.push("balance_error") + } + if ([...usdWallets, ...usdtWallets].some((wallet) => !wallet.expected)) { + anomalies.push("unexpected_wallet_id") + } + + let migrationStatus: CashWalletMigrationStatus | "none" = "none" + let migrationUpdatedAt: string | undefined + let migration: CashWalletMigration | null = null + if (lookup) { + const migrationResult = await migrationsRepo.findMigrationByAccountId({ + accountId: account.id, + cutoverVersion: lookup.cutoverVersion, + runId: lookup.runId, + }) + if (migrationResult instanceof Error) throw migrationResult + migration = migrationResult + if (migration) { + migrationStatus = migration.status + migrationUpdatedAt = migration.updatedAt?.toISOString() + if (migration.status === "failed") anomalies.push("migration_failed") + if (migration.status === "requires_operator_review") { + anomalies.push("migration_requires_review") + } + } + } + increment(migrationStatuses, migrationStatus) + + accounts.push({ + ...dashboardAccount, + accountUuid: account.uuid ?? dashboardAccount.accountUuid, + defaultWalletId: account.defaultWalletId, + defaultWalletCurrency: defaultWallet?.currency, + walletCount: rawWallets.length, + usdWallets, + usdtWallets, + migrationStatus, + migrationUpdatedAt, + cutoverBalanceAudit: computeCutoverBalanceAudit({ migration, usdtWallets }), + anomalies, + }) + } + + const treasuryAccounts = await Promise.all( + treasuryAccountIds.map((accountId) => + summarizeTreasuryAccount({ + accountId, + accountsRepo, + walletsRepo, + getBalanceForWallet, + balanceReadAttempts, + balanceMode, + }), + ), + ) + const treasury = treasurySummary(treasuryAccounts) + const usdTotalCents = usdTotalCentsForAccounts(accounts) + const usdtTotalMicros = usdtTotalMicrosForAccounts(accounts) + const missingUsdt = accounts.filter( + (account) => account.usdtWallets.length === 0, + ).length + const blockers = accounts.filter( + (account) => + account.anomalies.includes("missing_usd") || + account.anomalies.includes("missing_usdt"), + ).length + + const reconciliation = reconciliationSummary({ + customerUsdTotalCents: usdTotalCents, + customerUsdtTotalMicros: usdtTotalMicros, + treasury, + }) + + return { + generatedAt: now.toISOString(), + cutover: { + state: config.state, + cutoverVersion: config.cutoverVersion, + runId: config.runId, + updatedAt: config.updatedAt?.toISOString(), + }, + preflight: preflightReport, + summary: { + accounts: accounts.length, + wallets: { + current: accounts.reduce((sum, account) => sum + account.walletCount, 0), + target: accounts.length * 2, + usd: accounts.reduce((sum, account) => sum + account.usdWallets.length, 0), + usdt: accounts.reduce((sum, account) => sum + account.usdtWallets.length, 0), + missingUsdt, + }, + fundedUsdOnlyAccounts: accounts.filter( + (account) => + account.usdtWallets.length === 0 && + account.usdWallets.some((wallet) => wallet.balance.minorUnitsNumber > 0), + ).length, + usdTotalCents, + usdtTotalMicros, + anomalies: accounts.filter((account) => account.anomalies.length > 0).length, + watchlistAnomalies: accounts.filter( + (account) => account.watchlisted && account.anomalies.length > 0, + ).length, + canStart: blockers === 0, + blockers, + watchlistAccounts: accounts.filter((account) => account.watchlisted).length, + migrationStatuses, + }, + accounts, + treasury: { + accounts: treasuryAccounts, + summary: treasury, + }, + reconciliation, + } +} diff --git a/src/app/cash-wallet-cutover/orchestrator.ts b/src/app/cash-wallet-cutover/orchestrator.ts new file mode 100644 index 000000000..3e58b17c6 --- /dev/null +++ b/src/app/cash-wallet-cutover/orchestrator.ts @@ -0,0 +1,55 @@ +import { CashWalletCutoverRepository } from "@services/mongoose" + +import { createCashWalletMigrationStepHandlers } from "./handlers" +import { createCashWalletMigrationRuntimeServices } from "./runtime-services" +import { executeCashWalletMigrationStep } from "./executor" +import { runCashWalletMigrationBatch } from "./runner" + +type PrimaryCashWalletCutoverBatchRepository = Parameters< + typeof runCashWalletMigrationBatch +>[0]["migrationsRepo"] & + Parameters[0]["migrationsRepo"] + +type PrimaryCashWalletCutoverRuntimeServices = Parameters< + typeof createCashWalletMigrationStepHandlers +>[0]["services"] + +export const runPrimaryCashWalletCutoverBatch = ({ + cutoverVersion, + runId, + workerId, + limit, + stepDelayMs, + lockStaleBefore, + migrationsRepo = CashWalletCutoverRepository(), + runtimeServices = createCashWalletMigrationRuntimeServices(), +}: { + cutoverVersion: number + runId: string + workerId: string + limit?: number + stepDelayMs?: number + lockStaleBefore: Date + migrationsRepo?: PrimaryCashWalletCutoverBatchRepository + runtimeServices?: PrimaryCashWalletCutoverRuntimeServices +}) => { + const handlers = createCashWalletMigrationStepHandlers({ + migrationsRepo, + services: runtimeServices, + }) + + return runCashWalletMigrationBatch({ + cutoverVersion, + runId, + workerId, + limit, + stepDelayMs, + lockStaleBefore, + migrationsRepo, + executor: (migration) => + executeCashWalletMigrationStep({ + migration, + handlers, + }), + }) +} diff --git a/src/app/cash-wallet-cutover/planner.ts b/src/app/cash-wallet-cutover/planner.ts new file mode 100644 index 000000000..0a1e2031c --- /dev/null +++ b/src/app/cash-wallet-cutover/planner.ts @@ -0,0 +1,41 @@ +import { CashWalletCutoverDiscovery } from "./discovery" + +type PrimaryCashWalletMigrationPlan = { + accountId: AccountId + accountUuid?: AccountUuid + legacyUsdWalletId: WalletId + destinationUsdtWalletId: WalletId + previousDefaultWalletId: WalletId + cutoverVersion: number + runId: string + idempotencyKey: string +} + +export type { PrimaryCashWalletMigrationPlan } + +export const buildPrimaryCashWalletMigrationPlan = ({ + cutoverVersion, + runId, + discoveries, +}: { + cutoverVersion: number + runId: string + discoveries: CashWalletCutoverDiscovery[] +}): PrimaryCashWalletMigrationPlan[] => + discoveries.flatMap((discovery) => { + if (discovery.status !== "legacy_default") return [] + if (!discovery.legacyUsdWalletId || !discovery.destinationUsdtWalletId) return [] + + return [ + { + accountId: discovery.accountId, + accountUuid: discovery.accountUuid, + legacyUsdWalletId: discovery.legacyUsdWalletId, + destinationUsdtWalletId: discovery.destinationUsdtWalletId, + previousDefaultWalletId: discovery.previousDefaultWalletId, + cutoverVersion, + runId, + idempotencyKey: `cash-wallet-cutover:${runId}:${discovery.accountId}`, + }, + ] + }) diff --git a/src/app/cash-wallet-cutover/preflight.ts b/src/app/cash-wallet-cutover/preflight.ts new file mode 100644 index 000000000..df05bfddf --- /dev/null +++ b/src/app/cash-wallet-cutover/preflight.ts @@ -0,0 +1,53 @@ +import { CashWalletCutoverDiscovery } from "./discovery" + +type CashWalletCutoverPreflightBlocker = { + accountId: AccountId + reason: "missing_legacy_usd" | "missing_destination_usdt" +} + +type CashWalletCutoverPreflightReport = { + cutoverVersion: number + runId: string + totalAccounts: number + migrationCandidates: number + alreadyUsdt: number + residualLegacyUsd: number + blockers: number + blockerAccounts: CashWalletCutoverPreflightBlocker[] + canStart: boolean +} + +export type { CashWalletCutoverPreflightReport } + +export const buildCashWalletCutoverPreflightReport = ({ + cutoverVersion, + runId, + discoveries, +}: { + cutoverVersion: number + runId: string + discoveries: CashWalletCutoverDiscovery[] +}): CashWalletCutoverPreflightReport => { + const blockerAccounts = discoveries.flatMap(({ accountId, status }) => { + if (status !== "missing_legacy_usd" && status !== "missing_destination_usdt") { + return [] + } + + return [{ accountId, reason: status }] + }) + + return { + cutoverVersion, + runId, + totalAccounts: discoveries.length, + migrationCandidates: discoveries.filter(({ status }) => status === "legacy_default") + .length, + alreadyUsdt: discoveries.filter(({ status }) => status === "already_usdt").length, + residualLegacyUsd: discoveries.filter( + ({ status }) => status === "residual_legacy_usd", + ).length, + blockers: blockerAccounts.length, + blockerAccounts, + canStart: blockerAccounts.length === 0, + } +} diff --git a/src/app/cash-wallet-cutover/prepare.ts b/src/app/cash-wallet-cutover/prepare.ts new file mode 100644 index 000000000..35793b208 --- /dev/null +++ b/src/app/cash-wallet-cutover/prepare.ts @@ -0,0 +1,63 @@ +import { + buildCashWalletCutoverPreflightReport, + CashWalletCutoverPreflightReport, +} from "./preflight" +import { discoverCashWalletCutoverAccounts } from "./discovery" +import { + buildPrimaryCashWalletMigrationPlan, + PrimaryCashWalletMigrationPlan, +} from "./planner" +import { + upsertPrimaryCashWalletMigrationRecords, + CashWalletMigrationRecordsRepository, +} from "./migration-records" + +type PreparePrimaryCashWalletCutoverResult = { + report: CashWalletCutoverPreflightReport + plannedMigrations: PrimaryCashWalletMigrationPlan[] + migrations: CashWalletMigration[] +} + +export const preparePrimaryCashWalletCutover = async ({ + cutoverVersion, + runId, + accountsRepo, + walletsRepo, + migrationsRepo, +}: { + cutoverVersion: number + runId: string + accountsRepo: Pick + walletsRepo: Pick + migrationsRepo: CashWalletMigrationRecordsRepository +}): Promise => { + const discoveries = await discoverCashWalletCutoverAccounts({ + accountsRepo, + walletsRepo, + }) + if (discoveries instanceof Error) return discoveries + + const report = buildCashWalletCutoverPreflightReport({ + cutoverVersion, + runId, + discoveries, + }) + + if (!report.canStart) { + return { report, plannedMigrations: [], migrations: [] } + } + + const plannedMigrations = buildPrimaryCashWalletMigrationPlan({ + cutoverVersion, + runId, + discoveries, + }) + + const migrations = await upsertPrimaryCashWalletMigrationRecords({ + migrationsRepo, + plans: plannedMigrations, + }) + if (migrations instanceof Error) return migrations + + return { report, plannedMigrations, migrations } +} diff --git a/src/app/cash-wallet-cutover/presentation-for-account.ts b/src/app/cash-wallet-cutover/presentation-for-account.ts new file mode 100644 index 000000000..53483ea19 --- /dev/null +++ b/src/app/cash-wallet-cutover/presentation-for-account.ts @@ -0,0 +1,94 @@ +import { WalletsRepository, CashWalletCutoverRepository } from "@services/mongoose" + +import { CashWalletClientCapabilities } from "./client-capability" +import { CashWalletCutoverPreflightError } from "./errors" +import { evaluateCashWalletCutoverPresentation } from "./guard" +import { + CashWalletPresentationResult, + resolveCashWalletPresentation, +} from "./presentation" + +type CashWalletPresentationMigrationsRepository = { + getConfig: () => Promise + findMigrationByAccountId: ({ + accountId, + cutoverVersion, + runId, + }: { + accountId: AccountId + cutoverVersion: number + runId: string + }) => Promise +} + +type CashWalletPresentationWalletsRepository = { + listByAccountId: (accountId: AccountId) => Promise +} + +export const resolveCashWalletPresentationForAccount = async ({ + account, + client, + migrationsRepo = CashWalletCutoverRepository(), + walletsRepo = WalletsRepository(), +}: { + account: Account + client: CashWalletClientCapabilities + migrationsRepo?: CashWalletPresentationMigrationsRepository + walletsRepo?: CashWalletPresentationWalletsRepository +}): Promise => { + const cutover = await migrationsRepo.getConfig() + if (cutover instanceof Error) return cutover + + let migration: CashWalletMigration | null | undefined + if (cutover.state === "in_progress") { + if (!cutover.runId) return new CashWalletCutoverPreflightError() + + const foundMigration = await migrationsRepo.findMigrationByAccountId({ + accountId: account.id, + cutoverVersion: cutover.cutoverVersion, + runId: cutover.runId, + }) + if (foundMigration instanceof Error) return foundMigration + migration = foundMigration + } + + const decision = evaluateCashWalletCutoverPresentation({ + cutover, + migration, + client, + }) + if (decision instanceof Error) return decision + + const wallets = await walletsRepo.listByAccountId(account.id) + if (wallets instanceof Error) return wallets + + return resolveCashWalletPresentation({ decision, wallets }) +} + +export const resolveCashWalletMutationWalletIdForAccount = async ({ + account, + walletId, + client, + migrationsRepo, + walletsRepo, +}: { + account: Account + walletId: WalletId + client: CashWalletClientCapabilities + migrationsRepo?: CashWalletPresentationMigrationsRepository + walletsRepo?: CashWalletPresentationWalletsRepository +}): Promise => { + const presentation = await resolveCashWalletPresentationForAccount({ + account, + client, + migrationsRepo, + walletsRepo, + }) + if (presentation instanceof Error) return presentation + + if (walletId === presentation.legacyUsdWallet?.id) { + return presentation.activeSettlementWallet.id + } + + return walletId +} diff --git a/src/app/cash-wallet-cutover/presentation.ts b/src/app/cash-wallet-cutover/presentation.ts new file mode 100644 index 000000000..7c41478a8 --- /dev/null +++ b/src/app/cash-wallet-cutover/presentation.ts @@ -0,0 +1,82 @@ +import { WalletCurrency } from "@domain/shared" + +import { CashWalletCutoverDecision } from "./guard" +import { + CashWalletMissingLegacyUsdWalletError, + CashWalletMissingUsdtWalletError, +} from "./errors" + +export type CashWalletPresentationResult = { + wallets: Wallet[] + defaultWalletId: WalletId + legacyUsdWallet?: Wallet + activeSettlementWallet: Wallet +} + +export const resolveCashWalletPresentation = ({ + decision, + wallets, +}: { + decision: CashWalletCutoverDecision + wallets: Wallet[] +}): CashWalletPresentationResult | ApplicationError => { + const legacyUsdWallet = wallets.find((wallet) => wallet.currency === WalletCurrency.Usd) + const usdtWallet = wallets.find((wallet) => wallet.currency === WalletCurrency.Usdt) + const nonCashWallets = wallets.filter( + (wallet) => + wallet.currency !== WalletCurrency.Usd && wallet.currency !== WalletCurrency.Usdt, + ) + + if (decision.presentation === "usdt") { + if (!usdtWallet) return new CashWalletMissingUsdtWalletError() + + return { + wallets: [...nonCashWallets, usdtWallet], + defaultWalletId: usdtWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + } + } + + if (!legacyUsdWallet) return new CashWalletMissingLegacyUsdWalletError() + + if (decision.presentation === "legacy_usd_compat") { + if (!usdtWallet) return new CashWalletMissingUsdtWalletError() + + return { + wallets: [...nonCashWallets, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + } + } + + return { + wallets: [...nonCashWallets, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: legacyUsdWallet, + } +} + +export const cashWalletTransactionWalletIdsForPresentation = ({ + walletIds, + presentation, +}: { + walletIds?: WalletId[] + presentation: CashWalletPresentationResult +}): WalletId[] => { + const selectedWalletIds = walletIds ?? presentation.wallets.map((wallet) => wallet.id) + + if (!presentation.legacyUsdWallet) return selectedWalletIds + + return Array.from( + new Set( + selectedWalletIds.map((walletId) => + walletId === presentation.legacyUsdWallet?.id + ? presentation.activeSettlementWallet.id + : walletId, + ), + ), + ) +} diff --git a/src/app/cash-wallet-cutover/preview.ts b/src/app/cash-wallet-cutover/preview.ts new file mode 100644 index 000000000..2cca59bc9 --- /dev/null +++ b/src/app/cash-wallet-cutover/preview.ts @@ -0,0 +1,54 @@ +import { AccountsRepository, WalletsRepository } from "@services/mongoose" + +import { discoverCashWalletCutoverAccounts } from "./discovery" +import { + buildCashWalletCutoverPreflightReport, + CashWalletCutoverPreflightReport, +} from "./preflight" +import { + buildPrimaryCashWalletMigrationPlan, + PrimaryCashWalletMigrationPlan, +} from "./planner" + +export const previewPrimaryCashWalletCutover = async ({ + cutoverVersion, + runId, + accountsRepo = AccountsRepository(), + walletsRepo = WalletsRepository(), +}: { + cutoverVersion: number + runId: string + accountsRepo?: Pick + walletsRepo?: Pick +}): Promise< + | { + report: CashWalletCutoverPreflightReport + plannedMigrations: PrimaryCashWalletMigrationPlan[] + } + | RepositoryError +> => { + const discoveries = await discoverCashWalletCutoverAccounts({ + accountsRepo, + walletsRepo, + }) + if (discoveries instanceof Error) return discoveries + + const report = buildCashWalletCutoverPreflightReport({ + cutoverVersion, + runId, + discoveries, + }) + + if (!report.canStart) { + return { report, plannedMigrations: [] } + } + + return { + report, + plannedMigrations: buildPrimaryCashWalletMigrationPlan({ + cutoverVersion, + runId, + discoveries, + }), + } +} diff --git a/src/app/cash-wallet-cutover/provision-usdt-wallets.ts b/src/app/cash-wallet-cutover/provision-usdt-wallets.ts new file mode 100644 index 000000000..b66a1be53 --- /dev/null +++ b/src/app/cash-wallet-cutover/provision-usdt-wallets.ts @@ -0,0 +1,160 @@ +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +import { discoverCashWalletCutoverAccounts } from "./discovery" +import { InvalidCashWalletCutoverStateTransitionError } from "./errors" +import { + buildCashWalletCutoverPreflightReport, + CashWalletCutoverPreflightReport, +} from "./preflight" + +type ProvisionedCashWalletUsdtWallet = { + accountId: AccountId + walletId?: WalletId +} + +type FailedCashWalletUsdtWalletProvision = { + accountId: AccountId + error: string +} + +type ProvisionPrimaryCashWalletUsdtWalletsResult = { + before: CashWalletCutoverPreflightReport + after: CashWalletCutoverPreflightReport + eligible: number + provisioned: ProvisionedCashWalletUsdtWallet[] + failed: FailedCashWalletUsdtWalletProvision[] + dryRun: boolean +} + +type CashWalletCutoverProvisioningRepository = { + getConfig: () => Promise +} + +type AddWalletIfNonexistent = ({ + accountId, + type, + currency, +}: { + accountId: AccountId + type: WalletType + currency: WalletCurrency +}) => Promise + +const defaultSleep = (delayMs: number) => + new Promise((resolve) => setTimeout(resolve, delayMs)) + +const errorMessage = (error: unknown): string => { + if (error instanceof Error && error.message) return error.message + return String(error) +} + +const isRateLimitError = (error: unknown): boolean => + errorMessage(error).toLowerCase().includes("too many requests") + +export type { ProvisionPrimaryCashWalletUsdtWalletsResult } + +export const provisionPrimaryCashWalletUsdtWallets = async ({ + cutoverVersion, + runId, + accountsRepo, + walletsRepo, + migrationsRepo, + addWalletIfNonexistent, + provisionLimit, + provisionDelayMs = 0, + provisionRetryDelayMs = 60_000, + maxProvisionAttempts = 5, + dryRun = false, + sleep = defaultSleep, +}: { + cutoverVersion: number + runId: string + accountsRepo: Pick + walletsRepo: Pick + migrationsRepo: CashWalletCutoverProvisioningRepository + addWalletIfNonexistent: AddWalletIfNonexistent + provisionLimit?: number + provisionDelayMs?: number + provisionRetryDelayMs?: number + maxProvisionAttempts?: number + dryRun?: boolean + sleep?: (delayMs: number) => Promise +}): Promise => { + const config = await migrationsRepo.getConfig() + if (config instanceof Error) return config + + if (config.state !== "pre") { + return new InvalidCashWalletCutoverStateTransitionError( + "Cash wallet USDT provisioning can only run before cutover start", + ) + } + + const discoveries = await discoverCashWalletCutoverAccounts({ + accountsRepo, + walletsRepo, + }) + if (discoveries instanceof Error) return discoveries + + const before = buildCashWalletCutoverPreflightReport({ + cutoverVersion, + runId, + discoveries, + }) + + const eligibleDiscoveries = discoveries + .filter(({ status }) => status === "missing_destination_usdt") + .slice(0, provisionLimit) + const provisioned: ProvisionedCashWalletUsdtWallet[] = [] + const failed: FailedCashWalletUsdtWalletProvision[] = [] + + if (!dryRun) { + for (const [index, discovery] of eligibleDiscoveries.entries()) { + let wallet: Wallet | ApplicationError = new Error("Provisioning was not attempted") + const attempts = Math.max(1, maxProvisionAttempts) + + for (let attempt = 1; attempt <= attempts; attempt += 1) { + wallet = await addWalletIfNonexistent({ + accountId: discovery.accountId, + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }) + + if (!(wallet instanceof Error)) break + if (!isRateLimitError(wallet) || attempt === attempts) break + + await sleep(provisionRetryDelayMs) + } + + if (wallet instanceof Error) { + failed.push({ accountId: discovery.accountId, error: errorMessage(wallet) }) + } else { + provisioned.push({ accountId: discovery.accountId, walletId: wallet.id }) + } + + if (provisionDelayMs > 0 && index < eligibleDiscoveries.length - 1) { + await sleep(provisionDelayMs) + } + } + } + + const afterDiscoveries = dryRun + ? discoveries + : await discoverCashWalletCutoverAccounts({ accountsRepo, walletsRepo }) + if (afterDiscoveries instanceof Error) return afterDiscoveries + + const after = buildCashWalletCutoverPreflightReport({ + cutoverVersion, + runId, + discoveries: afterDiscoveries, + }) + + return { + before, + after, + eligible: eligibleDiscoveries.length, + provisioned, + failed, + dryRun, + } +} diff --git a/src/app/cash-wallet-cutover/runner.ts b/src/app/cash-wallet-cutover/runner.ts new file mode 100644 index 000000000..70789c8c9 --- /dev/null +++ b/src/app/cash-wallet-cutover/runner.ts @@ -0,0 +1,142 @@ +type CashWalletMigrationBatchRepository = { + listRunnableMigrations(args: { + cutoverVersion: number + runId: string + limit?: number + }): Promise + acquireMigrationLock(args: { + id: string + workerId: string + staleBefore: Date + cutoverVersion: number + runId: string + }): Promise + releaseMigrationLock(args: { + id: string + workerId: string + cutoverVersion: number + runId: string + }): Promise + markMigrationFailed(args: { + id: string + workerId: string + cutoverVersion: number + runId: string + error: Error + status: "failed" | "requires_operator_review" + }): Promise +} + +type CashWalletMigrationBatchExecutor = ( + migration: CashWalletMigration, +) => Promise + +type CashWalletMigrationBatchResult = { + attempted: number + advanced: number + failed: number + skipped: number +} + +type SleepFn = (delayMs: number) => Promise + +const sleep = (delayMs: number) => + new Promise((resolve) => setTimeout(resolve, delayMs)) + +const AMBIGUOUS_SIDE_EFFECT_STATUSES: CashWalletMigrationStatus[] = [ + "invoice_created", + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "fee_reimbursed", + "pointer_flipped", +] + +const failureStatusForMigration = ( + status: CashWalletMigrationStatus, +): "failed" | "requires_operator_review" => + AMBIGUOUS_SIDE_EFFECT_STATUSES.includes(status) ? "requires_operator_review" : "failed" + +export const runCashWalletMigrationBatch = async ({ + cutoverVersion, + runId, + workerId, + limit, + lockStaleBefore, + migrationsRepo, + executor, + stepDelayMs = 0, + sleep: sleepFn = sleep, +}: { + cutoverVersion: number + runId: string + workerId: string + limit?: number + lockStaleBefore: Date + migrationsRepo: CashWalletMigrationBatchRepository + executor: CashWalletMigrationBatchExecutor + stepDelayMs?: number + sleep?: SleepFn +}): Promise => { + const migrations = await migrationsRepo.listRunnableMigrations({ + cutoverVersion, + runId, + limit, + }) + if (migrations instanceof Error) return migrations + + const result: CashWalletMigrationBatchResult = { + attempted: 0, + advanced: 0, + failed: 0, + skipped: 0, + } + + for (const [index, migration] of migrations.entries()) { + result.attempted += 1 + + const locked = await migrationsRepo.acquireMigrationLock({ + id: migration.id, + workerId, + staleBefore: lockStaleBefore, + cutoverVersion, + runId, + }) + if (locked instanceof Error) { + result.skipped += 1 + continue + } + + const step = await executor(locked) + if (step instanceof Error) { + result.failed += 1 + const marked = await migrationsRepo.markMigrationFailed({ + id: locked.id, + workerId, + cutoverVersion, + runId, + error: step, + status: failureStatusForMigration(locked.status), + }) + if (marked instanceof Error) return marked + continue + } else { + result.advanced += 1 + } + + await migrationsRepo.releaseMigrationLock({ + id: locked.id, + workerId, + cutoverVersion, + runId, + }) + + if (stepDelayMs > 0 && index < migrations.length - 1) { + await sleepFn(stepDelayMs) + } + } + + return result +} diff --git a/src/app/cash-wallet-cutover/runtime-services.ts b/src/app/cash-wallet-cutover/runtime-services.ts new file mode 100644 index 000000000..4f2766689 --- /dev/null +++ b/src/app/cash-wallet-cutover/runtime-services.ts @@ -0,0 +1,331 @@ +import { addWalletIfNonexistent, updateDefaultWalletId } from "@app/accounts" +import { getBalanceForWallet } from "@app/wallets" +import { decodeInvoice } from "@domain/bitcoin/lightning" +import { InvalidWalletId } from "@domain/errors" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" +import { AccountsRepository } from "@services/mongoose" +import Ibex from "@services/ibex/client" +import { UnexpectedIbexResponse } from "@services/ibex/errors" +import { getFunderWalletId } from "@services/ledger/caching" + +import { + CashWalletMigrationFailedError, + InvalidCashWalletCutoverAmountError, + InvalidCashWalletMigrationTransitionError, +} from "./errors" +import { destinationShortfallUsdtMicros } from "./amount-conversion" + +const CUTOVER_IBEX_INVOICE_EXPIRATION_SECONDS = 15 * 60 +const CUTOVER_IBEX_RATE_LIMIT_RETRY_DELAY_MS = 60_000 +const CUTOVER_IBEX_RATE_LIMIT_MAX_ATTEMPTS = 5 + +type SleepFn = (delayMs: number) => Promise + +type RuntimeServiceDependencies = { + now?: () => Date + addWalletIfNonexistent?: typeof addWalletIfNonexistent + updateDefaultWalletId?: typeof updateDefaultWalletId + getBalanceForWallet?: typeof getBalanceForWallet + createInvoice?: typeof Ibex.addInvoice + createNoAmountInvoice?: typeof Ibex.addInvoice + payInvoice?: typeof Ibex.payInvoice + accountsRepo?: Pick, "findById"> + getTreasuryWalletId?: () => Promise + maxRateLimitAttempts?: number + rateLimitRetryDelayMs?: number + sleep?: SleepFn +} + +const isUsdAmount = (amount: unknown): amount is USDAmount => amount instanceof USDAmount +const isUsdtAmount = (amount: unknown): amount is USDTAmount => + amount instanceof USDTAmount + +const ibexInvoiceToDomainInvoice = (response: Awaited>) => { + if (response instanceof Error) return response + + const invoiceString = response.invoice?.bolt11 + if (!invoiceString) return new UnexpectedIbexResponse("Could not find invoice.") + + const decodedInvoice = decodeInvoice(invoiceString) + if (decodedInvoice instanceof Error) return decodedInvoice + + return decodedInvoice +} + +const sleep: SleepFn = (delayMs: number) => + new Promise((resolve) => setTimeout(resolve, delayMs)) + +const errorMessage = (error: Error): string => error.message || String(error) + +const isIbexRateLimitError = (error: Error): boolean => + errorMessage(error).toLowerCase().includes("too many requests") + +const withIbexRateLimitRetry = async ({ + operation, + maxAttempts, + retryDelayMs, + sleepFn, +}: { + operation: () => Promise + maxAttempts: number + retryDelayMs: number + sleepFn: SleepFn +}): Promise => { + for (let attempt = 1; ; attempt += 1) { + const result = await operation() + + if ( + !(result instanceof Error) || + !isIbexRateLimitError(result) || + attempt >= maxAttempts + ) { + return result + } + + await sleepFn(retryDelayMs) + } +} + +export const createCashWalletMigrationRuntimeServices = ( + deps: RuntimeServiceDependencies = {}, +) => { + const addWallet = deps.addWalletIfNonexistent ?? addWalletIfNonexistent + const updateDefaultWallet = deps.updateDefaultWalletId ?? updateDefaultWalletId + const balanceForWallet = deps.getBalanceForWallet ?? getBalanceForWallet + const invoiceForRecipient = deps.createInvoice ?? Ibex.addInvoice + const noAmountInvoiceForRecipient = deps.createNoAmountInvoice ?? Ibex.addInvoice + const payInvoice = deps.payInvoice ?? Ibex.payInvoice + const accountsRepo = deps.accountsRepo ?? AccountsRepository() + const rateLimitRetry = { + maxAttempts: Math.max( + 1, + deps.maxRateLimitAttempts ?? CUTOVER_IBEX_RATE_LIMIT_MAX_ATTEMPTS, + ), + retryDelayMs: + deps.rateLimitRetryDelayMs ?? CUTOVER_IBEX_RATE_LIMIT_RETRY_DELAY_MS, + sleepFn: deps.sleep ?? sleep, + } + + return { + now: deps.now ?? (() => new Date()), + provisioningService: { + ensureDestinationWallet: async ({ + accountId, + destinationUsdtWalletId, + }: { + accountId: AccountId + destinationUsdtWalletId: WalletId + }): Promise => { + const wallet = await addWallet({ + accountId, + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }) + if (wallet instanceof Error) return wallet + if (wallet.id !== destinationUsdtWalletId) return new InvalidWalletId() + return true + }, + }, + balanceReader: { + readSourceBalanceUsdCents: async ( + migration: CashWalletMigration, + ): Promise => { + const balance = await balanceForWallet({ + walletId: migration.legacyUsdWalletId, + currency: WalletCurrency.Usd, + }) + if (balance instanceof Error) return balance + if (!isUsdAmount(balance)) { + return new InvalidCashWalletCutoverAmountError("Expected USD balance") + } + return balance.asCents() + }, + readDestinationBalanceUsdtMicros: async ( + migration: CashWalletMigration, + ): Promise => { + const balance = await balanceForWallet({ + walletId: migration.destinationUsdtWalletId, + currency: WalletCurrency.Usdt, + }) + if (balance instanceof Error) return balance + if (!isUsdtAmount(balance)) { + return new InvalidCashWalletCutoverAmountError("Expected USDT balance") + } + return balance.asSmallestUnits() + }, + }, + invoiceService: { + createInvoice: ({ + recipientWalletId, + amount, + memo, + }: { + recipientWalletId: WalletId + amount: string + memo: string + }) => { + const usdtAmount = USDTAmount.smallestUnits(amount) + if (usdtAmount instanceof Error) return Promise.resolve(usdtAmount) + + return withIbexRateLimitRetry({ + ...rateLimitRetry, + operation: () => + invoiceForRecipient({ + accountId: recipientWalletId as IbexAccountId, + amount: usdtAmount, + memo, + expiration: CUTOVER_IBEX_INVOICE_EXPIRATION_SECONDS as Seconds, + }), + }).then(ibexInvoiceToDomainInvoice) + }, + createNoAmountInvoice: ({ + recipientWalletId, + memo, + }: { + recipientWalletId: WalletId + memo: string + }) => + withIbexRateLimitRetry({ + ...rateLimitRetry, + operation: () => + noAmountInvoiceForRecipient({ + accountId: recipientWalletId, + amount: USDTAmount.ZERO, + memo, + expiration: CUTOVER_IBEX_INVOICE_EXPIRATION_SECONDS as Seconds, + }), + }).then(ibexInvoiceToDomainInvoice), + }, + paymentService: { + payInvoice: async ({ + senderWalletId, + paymentRequest, + senderAmountUsdCents, + }: { + senderWalletId: WalletId + paymentRequest: string + senderAmountUsdCents?: string + }): Promise<{ transactionId: IbexTransactionId } | ApplicationError> => { + const send = + senderAmountUsdCents === undefined + ? undefined + : USDAmount.cents(senderAmountUsdCents) + if (send instanceof Error) return send + + const payment = await withIbexRateLimitRetry({ + ...rateLimitRetry, + operation: () => + payInvoice({ + accountId: senderWalletId as IbexAccountId, + invoice: paymentRequest as Bolt11, + send, + }), + }) + if (payment instanceof Error) return payment + + const transactionId = payment.transaction?.id + if (!transactionId) { + return new UnexpectedIbexResponse("Payment transaction id not found") + } + return { transactionId: transactionId as IbexTransactionId } + }, + }, + balanceVerifier: { + verifyBalanceMove: async ({ + legacyUsdWalletId, + }: { + legacyUsdWalletId: WalletId + }): Promise => { + const balance = await balanceForWallet({ + walletId: legacyUsdWalletId, + currency: WalletCurrency.Usd, + }) + if (balance instanceof Error) return balance + if (!isUsdAmount(balance) || !balance.isZero()) { + return new CashWalletMigrationFailedError("Legacy USD wallet is not zero") + } + return true + }, + }, + feeService: { + readFeeAmountUsdtMicros: async ( + migration: CashWalletMigration, + ): Promise => { + if (migration.balanceMovePaymentTransactionId === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMovePaymentTransactionId is required before reading fee amount", + ) + } + + if (migration.destinationAmountUsdtMicros === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "destinationAmountUsdtMicros is required before reading fee amount", + ) + } + + if (migration.destinationStartingBalanceUsdtMicros === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "destinationStartingBalanceUsdtMicros is required before reading fee amount", + ) + } + + const currentBalance = await balanceForWallet({ + walletId: migration.destinationUsdtWalletId, + currency: WalletCurrency.Usdt, + }) + if (currentBalance instanceof Error) return currentBalance + if (!isUsdtAmount(currentBalance)) { + return new InvalidCashWalletCutoverAmountError("Expected USDT balance") + } + + return destinationShortfallUsdtMicros({ + targetUsdtMicros: migration.destinationAmountUsdtMicros, + startingUsdtMicros: migration.destinationStartingBalanceUsdtMicros, + currentUsdtMicros: currentBalance.asSmallestUnits(), + }) + }, + }, + treasuryService: { + getTreasuryWalletId: deps.getTreasuryWalletId ?? getFunderWalletId, + }, + pointerService: { + flipDefaultWallet: async ({ + accountId, + destinationWalletId, + }: { + accountId: AccountId + destinationWalletId: WalletId + }): Promise<{ previousDefaultWalletId: WalletId } | ApplicationError> => { + const account = await accountsRepo.findById(accountId) + if (account instanceof Error) return account + + const previousDefaultWalletId = account.defaultWalletId + const updated = await updateDefaultWallet({ + accountId, + walletId: destinationWalletId, + }) + if (updated instanceof Error) return updated + + return { previousDefaultWalletId } + }, + }, + legacyWalletVerifier: { + verifyLegacyWalletZero: async ({ + legacyUsdWalletId, + }: { + legacyUsdWalletId: WalletId + }): Promise => { + const balance = await balanceForWallet({ + walletId: legacyUsdWalletId, + currency: WalletCurrency.Usd, + }) + if (balance instanceof Error) return balance + if (!isUsdAmount(balance) || !balance.isZero()) { + return new CashWalletMigrationFailedError("Legacy USD wallet is not zero") + } + return true + }, + }, + } +} diff --git a/src/app/cash-wallet-cutover/state-machine.ts b/src/app/cash-wallet-cutover/state-machine.ts new file mode 100644 index 000000000..9a950e065 --- /dev/null +++ b/src/app/cash-wallet-cutover/state-machine.ts @@ -0,0 +1,49 @@ +import { InvalidCashWalletMigrationTransitionError } from "./errors" + +const transitions: Partial< + Record +> = { + not_started: ["started"], + started: ["provisioned", "failed"], + provisioned: ["balance_read", "failed", "skipped_already_migrated"], + balance_read: ["invoice_created", "pointer_flipped", "failed"], + invoice_created: [ + "invoice_created", + "balance_move_sending", + "failed", + "requires_operator_review", + ], + balance_move_sending: ["balance_move_sent", "failed", "requires_operator_review"], + balance_move_sent: ["balance_move_verified", "failed", "requires_operator_review"], + balance_move_verified: [ + "fee_reimbursement_invoice_created", + "fee_reimbursed", + "failed", + "requires_operator_review", + ], + fee_reimbursement_invoice_created: [ + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "failed", + "requires_operator_review", + ], + fee_reimbursement_sending: ["fee_reimbursed", "failed", "requires_operator_review"], + fee_reimbursed: ["pointer_flipped", "failed"], + pointer_flipped: ["legacy_zero_verified", "failed"], + legacy_zero_verified: ["complete", "failed"], + rollback_started: ["rolled_back", "failed"], +} + +export const assertCanTransition = ( + from: CashWalletMigrationStatus, + to: CashWalletMigrationStatus, +): true | InvalidCashWalletMigrationTransitionError => { + if (transitions[from]?.includes(to)) return true + return new InvalidCashWalletMigrationTransitionError( + `Invalid migration transition: ${from} -> ${to}`, + ) +} + +export const nextResumeStatus = ( + status: CashWalletMigrationStatus, +): CashWalletMigrationStatus => status diff --git a/src/app/cash-wallet-cutover/worker.ts b/src/app/cash-wallet-cutover/worker.ts new file mode 100644 index 000000000..f0b860031 --- /dev/null +++ b/src/app/cash-wallet-cutover/worker.ts @@ -0,0 +1,597 @@ +import { decodeInvoice } from "@domain/bitcoin/lightning" + +import { assertCanTransition } from "./state-machine" +import { + feeUsdCentsToUsdtMicros, + usdCentsToUsdtMicros, + usdtMicrosToUsdCentsCeil, +} from "./amount-conversion" +import { + InvalidCashWalletCutoverAmountError, + InvalidCashWalletMigrationTransitionError, +} from "./errors" + +type CashWalletMigrationTransitionRepository = { + transitionMigration(args: { + id: string + from: CashWalletMigrationStatus + to: CashWalletMigrationStatus + cutoverVersion: number + runId: string + patch?: Partial + }): Promise +} + +type CashWalletMigrationInvoiceService = { + createInvoice(args: { + recipientWalletId: WalletId + amount: string + memo: string + }): Promise +} + +type CashWalletMigrationNoAmountInvoiceService = { + createNoAmountInvoice(args: { + recipientWalletId: WalletId + memo: string + }): Promise +} + +type CashWalletMigrationPaymentService = { + payInvoice(args: { + senderWalletId: WalletId + paymentRequest: string + senderAmountUsdCents?: string + }): Promise<{ transactionId: IbexTransactionId } | ApplicationError> +} + +type CashWalletMigrationBalanceVerifier = { + verifyBalanceMove(args: { + legacyUsdWalletId: WalletId + destinationUsdtWalletId: WalletId + sourceBalanceUsdCents?: string + destinationAmountUsdtMicros?: string + transactionId: IbexTransactionId + }): Promise +} + +type CashWalletMigrationPointerService = { + flipDefaultWallet(args: { + accountId: AccountId + destinationWalletId: WalletId + }): Promise<{ previousDefaultWalletId: WalletId } | ApplicationError> +} + +type CashWalletMigrationLegacyWalletVerifier = { + verifyLegacyWalletZero(args: { + legacyUsdWalletId: WalletId + }): Promise +} + +type CashWalletMigrationProvisioningService = { + ensureDestinationWallet(args: { + accountId: AccountId + destinationUsdtWalletId: WalletId + }): Promise +} + +const CUTOVER_INVOICE_PAYMENT_SAFETY_WINDOW_MS = 30 * 1000 + +const isInvoicePaymentRequestStale = ({ + paymentRequest, + now, + safetyWindowMs = CUTOVER_INVOICE_PAYMENT_SAFETY_WINDOW_MS, +}: { + paymentRequest: string + now: Date + safetyWindowMs?: number +}): boolean => { + const invoice = decodeInvoice(paymentRequest) + if (invoice instanceof Error) return true + + return invoice.expiresAt.getTime() <= now.getTime() + safetyWindowMs +} + +export const startCashWalletMigration = async ({ + migration, + migrationsRepo, + startedAt, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository + startedAt: Date +}): Promise => { + const transition = assertCanTransition(migration.status, "started") + if (transition instanceof Error) return transition + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "started", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { startedAt }, + }) +} + +export const provisionCashWalletMigrationDestination = async ({ + migration, + provisioningService, + migrationsRepo, +}: { + migration: CashWalletMigration + provisioningService: CashWalletMigrationProvisioningService + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "provisioned") + if (transition instanceof Error) return transition + + const provisioned = await provisioningService.ensureDestinationWallet({ + accountId: migration.accountId, + destinationUsdtWalletId: migration.destinationUsdtWalletId, + }) + if (provisioned instanceof Error) return provisioned + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "provisioned", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const recordCashWalletMigrationBalance = async ({ + migration, + migrationsRepo, + sourceBalanceUsdCents, + destinationStartingBalanceUsdtMicros, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository + sourceBalanceUsdCents: string + destinationStartingBalanceUsdtMicros: string +}): Promise => { + const destinationAmountUsdtMicros = usdCentsToUsdtMicros(sourceBalanceUsdCents) + if (destinationAmountUsdtMicros instanceof Error) return destinationAmountUsdtMicros + + const transition = assertCanTransition(migration.status, "balance_read") + if (transition instanceof Error) return transition + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "balance_read", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + sourceBalanceUsdCents, + destinationAmountUsdtMicros, + destinationStartingBalanceUsdtMicros, + }, + }) +} + +export const sendCashWalletMigrationBalanceMovePayment = async ({ + migration, + paymentService, + invoiceService, + migrationsRepo, + now = () => new Date(), + invoicePaymentSafetyWindowMs = CUTOVER_INVOICE_PAYMENT_SAFETY_WINDOW_MS, +}: { + migration: CashWalletMigration + paymentService: CashWalletMigrationPaymentService + invoiceService?: CashWalletMigrationNoAmountInvoiceService + migrationsRepo: CashWalletMigrationTransitionRepository + now?: () => Date + invoicePaymentSafetyWindowMs?: number +}): Promise => { + const transition = assertCanTransition(migration.status, "balance_move_sending") + if (transition instanceof Error) return transition + + if (migration.balanceMoveInvoicePaymentRequest === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMoveInvoicePaymentRequest is required before balance move payment sending", + ) + } + + if (migration.sourceBalanceUsdCents === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "sourceBalanceUsdCents is required before balance move payment sending", + ) + } + + let payableMigration = migration + if ( + invoiceService && + isInvoicePaymentRequestStale({ + paymentRequest: migration.balanceMoveInvoicePaymentRequest, + now: now(), + safetyWindowMs: invoicePaymentSafetyWindowMs, + }) + ) { + const refreshedMigration = await createCashWalletMigrationBalanceMoveInvoice({ + migration, + invoiceService, + migrationsRepo, + }) + if (refreshedMigration instanceof Error) return refreshedMigration + payableMigration = refreshedMigration + } + + if (payableMigration.balanceMoveInvoicePaymentRequest === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMoveInvoicePaymentRequest is required before balance move payment sending", + ) + } + + if (payableMigration.sourceBalanceUsdCents === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "sourceBalanceUsdCents is required before balance move payment sending", + ) + } + + const payment = await paymentService.payInvoice({ + senderWalletId: payableMigration.legacyUsdWalletId, + paymentRequest: payableMigration.balanceMoveInvoicePaymentRequest, + senderAmountUsdCents: payableMigration.sourceBalanceUsdCents, + }) + if (payment instanceof Error) return payment + + return migrationsRepo.transitionMigration({ + id: payableMigration.id, + from: payableMigration.status, + to: "balance_move_sending", + cutoverVersion: payableMigration.cutoverVersion, + runId: payableMigration.runId, + patch: { + balanceMovePaymentTransactionId: payment.transactionId, + }, + }) +} + +export const markCashWalletMigrationBalanceMoveSent = async ({ + migration, + migrationsRepo, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "balance_move_sent") + if (transition instanceof Error) return transition + + if (migration.balanceMovePaymentTransactionId === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMovePaymentTransactionId is required before marking balance move sent", + ) + } + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "balance_move_sent", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const verifyCashWalletMigrationBalanceMove = async ({ + migration, + balanceVerifier, + migrationsRepo, +}: { + migration: CashWalletMigration + balanceVerifier: CashWalletMigrationBalanceVerifier + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "balance_move_verified") + if (transition instanceof Error) return transition + + if (migration.balanceMovePaymentTransactionId === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMovePaymentTransactionId is required before verifying balance move", + ) + } + + const verified = await balanceVerifier.verifyBalanceMove({ + legacyUsdWalletId: migration.legacyUsdWalletId, + destinationUsdtWalletId: migration.destinationUsdtWalletId, + sourceBalanceUsdCents: migration.sourceBalanceUsdCents, + destinationAmountUsdtMicros: migration.destinationAmountUsdtMicros, + transactionId: migration.balanceMovePaymentTransactionId as IbexTransactionId, + }) + if (verified instanceof Error) return verified + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "balance_move_verified", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const createCashWalletMigrationBalanceMoveInvoice = async ({ + migration, + invoiceService, + migrationsRepo, +}: { + migration: CashWalletMigration + invoiceService: CashWalletMigrationNoAmountInvoiceService + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "invoice_created") + if (transition instanceof Error) return transition + + if (migration.destinationAmountUsdtMicros === undefined) { + return new InvalidCashWalletCutoverAmountError( + "destinationAmountUsdtMicros is required before balance move invoice creation", + ) + } + + const invoice = await invoiceService.createNoAmountInvoice({ + recipientWalletId: migration.destinationUsdtWalletId, + memo: `cash-wallet-cutover:${migration.runId}:${migration.id}:balance-move`, + }) + if (invoice instanceof Error) return invoice + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "invoice_created", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + balanceMoveInvoicePaymentRequest: invoice.paymentRequest, + balanceMoveInvoicePaymentHash: invoice.paymentHash, + }, + }) +} + +export const createCashWalletMigrationFeeReimbursementInvoice = async ({ + migration, + invoiceService, + migrationsRepo, + feeAmountUsdtMicros, +}: { + migration: CashWalletMigration + invoiceService: CashWalletMigrationInvoiceService + migrationsRepo: CashWalletMigrationTransitionRepository + feeAmountUsdtMicros: string +}): Promise => { + const feeAmountUsdCents = usdtMicrosToUsdCentsCeil(feeAmountUsdtMicros) + if (feeAmountUsdCents instanceof Error) return feeAmountUsdCents + + const reimbursableFeeAmountUsdtMicros = feeUsdCentsToUsdtMicros(feeAmountUsdCents) + if (reimbursableFeeAmountUsdtMicros instanceof Error) + return reimbursableFeeAmountUsdtMicros + + const transition = assertCanTransition( + migration.status, + "fee_reimbursement_invoice_created", + ) + if (transition instanceof Error) return transition + + const invoice = await invoiceService.createInvoice({ + recipientWalletId: migration.destinationUsdtWalletId, + amount: reimbursableFeeAmountUsdtMicros, + memo: `cash-wallet-cutover:${migration.runId}:${migration.id}:fee-reimbursement`, + }) + if (invoice instanceof Error) return invoice + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "fee_reimbursement_invoice_created", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + feeAmountUsdCents, + feeAmountUsdtMicros, + feeReimbursementInvoicePaymentRequest: invoice.paymentRequest, + feeReimbursementInvoicePaymentHash: invoice.paymentHash, + }, + }) +} + +export const skipCashWalletMigrationFeeReimbursement = async ({ + migration, + migrationsRepo, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "fee_reimbursed") + if (transition instanceof Error) return transition + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "fee_reimbursed", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + feeAmountUsdCents: "0", + feeAmountUsdtMicros: "0", + }, + }) +} + +export const sendCashWalletMigrationFeeReimbursementPayment = async ({ + migration, + treasuryWalletId, + paymentService, + invoiceService, + migrationsRepo, + now = () => new Date(), + invoicePaymentSafetyWindowMs = CUTOVER_INVOICE_PAYMENT_SAFETY_WINDOW_MS, +}: { + migration: CashWalletMigration + treasuryWalletId: WalletId + paymentService: CashWalletMigrationPaymentService + invoiceService?: CashWalletMigrationInvoiceService + migrationsRepo: CashWalletMigrationTransitionRepository + now?: () => Date + invoicePaymentSafetyWindowMs?: number +}): Promise => { + const transition = assertCanTransition(migration.status, "fee_reimbursement_sending") + if (transition instanceof Error) return transition + + if (migration.feeReimbursementInvoicePaymentRequest === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "feeReimbursementInvoicePaymentRequest is required before fee reimbursement sending", + ) + } + + let payableMigration = migration + if ( + invoiceService && + isInvoicePaymentRequestStale({ + paymentRequest: migration.feeReimbursementInvoicePaymentRequest, + now: now(), + safetyWindowMs: invoicePaymentSafetyWindowMs, + }) + ) { + if (migration.feeAmountUsdtMicros === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "feeAmountUsdtMicros is required before fee reimbursement invoice refresh", + ) + } + + const refreshedMigration = await createCashWalletMigrationFeeReimbursementInvoice({ + migration, + invoiceService, + migrationsRepo, + feeAmountUsdtMicros: migration.feeAmountUsdtMicros, + }) + if (refreshedMigration instanceof Error) return refreshedMigration + payableMigration = refreshedMigration + } + + if (payableMigration.feeReimbursementInvoicePaymentRequest === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "feeReimbursementInvoicePaymentRequest is required before fee reimbursement sending", + ) + } + + const payment = await paymentService.payInvoice({ + senderWalletId: treasuryWalletId, + paymentRequest: payableMigration.feeReimbursementInvoicePaymentRequest, + }) + if (payment instanceof Error) return payment + + return migrationsRepo.transitionMigration({ + id: payableMigration.id, + from: payableMigration.status, + to: "fee_reimbursement_sending", + cutoverVersion: payableMigration.cutoverVersion, + runId: payableMigration.runId, + patch: { + feeReimbursementPaymentTransactionId: payment.transactionId, + }, + }) +} + +export const markCashWalletMigrationFeeReimbursed = async ({ + migration, + migrationsRepo, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "fee_reimbursed") + if (transition instanceof Error) return transition + + if (migration.feeReimbursementPaymentTransactionId === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "feeReimbursementPaymentTransactionId is required before marking fee reimbursed", + ) + } + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "fee_reimbursed", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const flipCashWalletMigrationDefaultPointer = async ({ + migration, + pointerService, + migrationsRepo, +}: { + migration: CashWalletMigration + pointerService: CashWalletMigrationPointerService + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "pointer_flipped") + if (transition instanceof Error) return transition + + const pointer = await pointerService.flipDefaultWallet({ + accountId: migration.accountId, + destinationWalletId: migration.destinationUsdtWalletId, + }) + if (pointer instanceof Error) return pointer + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "pointer_flipped", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + previousDefaultWalletId: pointer.previousDefaultWalletId, + }, + }) +} + +export const verifyCashWalletMigrationLegacyZero = async ({ + migration, + legacyWalletVerifier, + migrationsRepo, +}: { + migration: CashWalletMigration + legacyWalletVerifier: CashWalletMigrationLegacyWalletVerifier + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "legacy_zero_verified") + if (transition instanceof Error) return transition + + const verified = await legacyWalletVerifier.verifyLegacyWalletZero({ + legacyUsdWalletId: migration.legacyUsdWalletId, + }) + if (verified instanceof Error) return verified + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "legacy_zero_verified", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const completeCashWalletMigration = async ({ + migration, + migrationsRepo, + completedAt, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository + completedAt: Date +}): Promise => { + const transition = assertCanTransition(migration.status, "complete") + if (transition instanceof Error) return transition + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "complete", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { completedAt }, + }) +} diff --git a/src/app/errors.ts b/src/app/errors.ts index 6e747daf6..4740230f4 100644 --- a/src/app/errors.ts +++ b/src/app/errors.ts @@ -19,6 +19,7 @@ import * as PubSubErrors from "@domain/pubsub/errors" import * as CaptchaErrors from "@domain/captcha/errors" import * as AuthenticationErrors from "@domain/authentication/errors" import * as UserErrors from "@domain/users/errors" +import * as CashWalletCutoverErrors from "@app/cash-wallet-cutover/errors" import * as LedgerFacadeErrors from "@services/ledger/domain/errors" import * as KratosErrors from "@services/kratos/errors" @@ -51,6 +52,7 @@ export const ApplicationErrors = { ...CaptchaErrors, ...AuthenticationErrors, ...UserErrors, + ...CashWalletCutoverErrors, ...KratosErrors, ...LedgerFacadeErrors, diff --git a/src/app/index.ts b/src/app/index.ts index b006e8446..46afb0463 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -14,6 +14,7 @@ import * as WalletsMod from "./wallets" import * as PaymentsMod from "./payments" import * as MerchantsMod from "./merchants" import * as SwapMod from "./swap" +import * as CashWalletCutoverMod from "./cash-wallet-cutover" const allFunctions = { Accounts: { ...AccountsMod }, @@ -30,6 +31,7 @@ const allFunctions = { Payments: { ...PaymentsMod }, Merchants: { ...MerchantsMod }, Swap: { ...SwapMod }, + CashWalletCutover: { ...CashWalletCutoverMod }, } as const let subModule: keyof typeof allFunctions @@ -60,4 +62,5 @@ export const { Payments, Merchants, Swap, + CashWalletCutover, } = allFunctions diff --git a/src/graphql/admin/mutations.ts b/src/graphql/admin/mutations.ts index 070a0d7c7..fcf3f6ce1 100644 --- a/src/graphql/admin/mutations.ts +++ b/src/graphql/admin/mutations.ts @@ -3,6 +3,7 @@ import { GT } from "@graphql/index" import AccountUpdateLevelMutation from "@graphql/admin/root/mutation/account-update-level" import AccountUpdateStatusMutation from "@graphql/admin/root/mutation/account-update-status" import BusinessUpdateMapInfoMutation from "@graphql/admin/root/mutation/business-update-map-info" +import CashWalletCutoverUpdateMutation from "@graphql/admin/root/mutation/cash-wallet-cutover-update" import UserUpdatePhoneMutation from "./root/mutation/user-update-phone" import BusinessDeleteMapInfoMutation from "./root/mutation/delete-business-map" @@ -13,8 +14,7 @@ import MerchantMapDeleteMutation from "./root/mutation/merchant-map-delete" import MerchantMapValidateMutation from "./root/mutation/merchant-map-validate" export const mutationFields = { - unauthed: { - }, + unauthed: {}, authed: { userUpdatePhone: UserUpdatePhoneMutation, accountUpdateLevel: AccountUpdateLevelMutation, @@ -25,6 +25,7 @@ export const mutationFields = { businessDeleteMapInfo: BusinessDeleteMapInfoMutation, sendNotification: SendNotificationMutation, cashoutNotificationSend: sendCashoutSettledNotification, + cashWalletCutoverUpdate: CashWalletCutoverUpdateMutation, }, } diff --git a/src/graphql/admin/queries.ts b/src/graphql/admin/queries.ts index a069657df..deafdfd9f 100644 --- a/src/graphql/admin/queries.ts +++ b/src/graphql/admin/queries.ts @@ -1,4 +1,5 @@ import { GT } from "@graphql/index" +import CashWalletCutoverQuery from "@graphql/shared/root/query/cash-wallet-cutover" import AllLevelsQuery from "./root/query/all-levels" import LightningInvoiceQuery from "./root/query/lightning-invoice" @@ -36,6 +37,7 @@ export const queryFields = { idDocumentReadUrl: IdDocumentReadUrlQuery, notificationTopics: NotificationTopicsQuery, bridgeReconciliationOrphans: BridgeReconciliationOrphansQuery, + cashWalletCutover: CashWalletCutoverQuery, }, } diff --git a/src/graphql/admin/root/mutation/cash-wallet-cutover-update.ts b/src/graphql/admin/root/mutation/cash-wallet-cutover-update.ts new file mode 100644 index 000000000..ff2df55e8 --- /dev/null +++ b/src/graphql/admin/root/mutation/cash-wallet-cutover-update.ts @@ -0,0 +1,61 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import CashWalletCutoverPayload from "@graphql/admin/types/payload/cash-wallet-cutover" +import CashWalletCutoverState from "@graphql/shared/types/scalar/cash-wallet-cutover-state" +import Timestamp from "@graphql/shared/types/scalar/timestamp" +import { CashWalletCutoverRepository } from "@services/mongoose/cash-wallet-cutover" + +const CashWalletCutoverUpdateInput = GT.Input({ + name: "CashWalletCutoverUpdateInput", + fields: () => ({ + state: { type: GT.NonNull(CashWalletCutoverState) }, + scheduledAt: { type: Timestamp }, + cutoverVersion: { type: GT.Int }, + runId: { type: GT.String }, + pauseReason: { type: GT.String }, + }), +}) + +const CashWalletCutoverUpdateMutation = GT.Field< + null, + GraphQLAdminContext, + { + input: { + state: CashWalletCutoverState | Error + scheduledAt?: Date | Error + cutoverVersion?: number + runId?: string + pauseReason?: string + } + } +>({ + type: GT.NonNull(CashWalletCutoverPayload), + args: { + input: { type: GT.NonNull(CashWalletCutoverUpdateInput) }, + }, + resolve: async (_, { input }, ctx) => { + if (input.state instanceof Error) { + return { errors: [{ message: input.state.message }] } + } + if (input.scheduledAt instanceof Error) { + return { errors: [{ message: input.scheduledAt.message }] } + } + + const patch: Partial = { + state: input.state, + scheduledAt: input.scheduledAt, + cutoverVersion: input.cutoverVersion, + runId: input.runId, + pauseReason: input.pauseReason, + } + + const result = await CashWalletCutoverRepository().updateConfig(patch, ctx.user.id) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { errors: [], cashWalletCutover: result } + }, +}) + +export default CashWalletCutoverUpdateMutation diff --git a/src/graphql/admin/schema.graphql b/src/graphql/admin/schema.graphql index 1eb75c263..46da132ac 100644 --- a/src/graphql/admin/schema.graphql +++ b/src/graphql/admin/schema.graphql @@ -136,6 +136,38 @@ input BusinessUpdateMapInfoInput { username: Username! } +type CashWalletCutover { + completedAt: Timestamp + cutoverVersion: Int! + pauseReason: String + pausedAt: Timestamp + runId: String + scheduledAt: Timestamp + startedAt: Timestamp + state: CashWalletCutoverState! + updatedAt: Timestamp! + updatedBy: String +} + +type CashWalletCutoverPayload { + cashWalletCutover: CashWalletCutover + errors: [Error!]! +} + +enum CashWalletCutoverState { + COMPLETE + IN_PROGRESS + PRE +} + +input CashWalletCutoverUpdateInput { + cutoverVersion: Int + pauseReason: String + runId: String + scheduledAt: Timestamp + state: CashWalletCutoverState! +} + input CashoutNotificationSendInput { accountId: String! amount: Int! @@ -278,6 +310,7 @@ type Mutation { accountUpdateStatus(input: AccountUpdateStatusInput!): AccountDetailPayload! businessDeleteMapInfo(input: BusinessDeleteMapInfoInput!): AccountDetailPayload! businessUpdateMapInfo(input: BusinessUpdateMapInfoInput!): AccountDetailPayload! + cashWalletCutoverUpdate(input: CashWalletCutoverUpdateInput!): CashWalletCutoverPayload! cashoutNotificationSend(input: CashoutNotificationSendInput!): SuccessPayload! merchantMapDelete(input: MerchantMapDeleteInput!): MerchantPayload! merchantMapValidate(input: MerchantMapValidateInput!): MerchantPayload! @@ -335,6 +368,7 @@ type Query { accountDetailsByUsername(username: Username!): AuditedAccount! allLevels: [AccountLevel!]! bridgeReconciliationOrphans(limit: Int = 50, orphanType: String = null, status: String = null): [BridgeReconciliationOrphan!]! + cashWalletCutover: CashWalletCutover! idDocumentReadUrl( """Storage key of the ID document file""" fileKey: String! @@ -572,7 +606,7 @@ A wallet belonging to an account which contains a USDT balance and a list of tra """ type UsdtWallet implements Wallet { accountId: ID! - balance: FractionalCentAmount! + balance: FractionalCentAmount id: ID! isExternal: Boolean! lnurlp: Lnurl diff --git a/src/graphql/admin/types/payload/cash-wallet-cutover.ts b/src/graphql/admin/types/payload/cash-wallet-cutover.ts new file mode 100644 index 000000000..5f209898c --- /dev/null +++ b/src/graphql/admin/types/payload/cash-wallet-cutover.ts @@ -0,0 +1,13 @@ +import { GT } from "@graphql/index" +import IError from "@graphql/shared/types/abstract/error" +import CashWalletCutoverObject from "@graphql/shared/types/object/cash-wallet-cutover" + +const CashWalletCutoverPayload = GT.Object({ + name: "CashWalletCutoverPayload", + fields: () => ({ + errors: { type: GT.NonNullList(IError) }, + cashWalletCutover: { type: CashWalletCutoverObject }, + }), +}) + +export default CashWalletCutoverPayload diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 9f93e8875..480711d87 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -792,6 +792,42 @@ export const mapError = (error: ApplicationError): CustomApolloError => { case "InvalidLnurlError": return new InvalidLnurlError({ message: error.message, logger: baseLogger }) + case "CashWalletCutoverInProgressError": + message = "Cash Wallet cutover is in progress. Please try again shortly." + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletMigrationFailedError": + message = "Cash Wallet migration needs support review." + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletCutoverPreflightError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletCutoverTreasuryInsufficientBalanceError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletMissingLegacyUsdWalletError": + message = "Legacy USD Cash Wallet is missing for this account." + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletMissingUsdtWalletError": + message = "USDT Cash Wallet is missing for this account." + return new ValidationInternalError({ message, logger: baseLogger }) + + case "InvalidCashWalletCutoverAmountError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + + case "InvalidCashWalletMigrationTransitionError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + + case "InvalidCashWalletCutoverStateTransitionError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + case "UnknownCaptchaError": message = `Unknown error occurred (code: ${error.name}${ error.message ? ": " + error.message : "" diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index 8f8e7f46f..7a401d97e 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -1,4 +1,5 @@ import { GT } from "@graphql/index" +import CashWalletCutoverQuery from "@graphql/shared/root/query/cash-wallet-cutover" import MeQuery from "@graphql/public/root/query/me" import GlobalsQuery from "@graphql/public/root/query/globals" @@ -44,6 +45,7 @@ export const queryFields = { npubByUsername: NpubByUserNameQuery, isFlashNpub: IsFlashNpubQuery, supportedBanks: SupportedBanksQuery, + cashWalletCutover: CashWalletCutoverQuery, }, authed: { atAccountLevel: { diff --git a/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts b/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts index 7c5b4dd2a..cf663acc1 100644 --- a/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts +++ b/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts @@ -1,4 +1,5 @@ import { Payments } from "@app" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" import { checkedToWalletId } from "@domain/wallets" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import { GT } from "@graphql/index" @@ -9,7 +10,6 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import dedent from "dedent" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" // import { RequestInit, Response } from 'node-fetch' -import { EmailService } from "@services/email" const IntraLedgerUsdPaymentSendInput = GT.Input({ name: "IntraLedgerUsdPaymentSendInput", @@ -34,7 +34,11 @@ const IntraLedgerUsdPaymentSendMutation = GT.Field { + resolve: async ( + _, + args, + { domainAccount, cashWalletClientCapabilities }: GraphQLPublicContextAuth, + ) => { const { walletId, recipientWalletId, amount, memo } = args.input for (const input of [walletId, recipientWalletId, amount, memo]) { if (input instanceof Error) { @@ -52,11 +56,23 @@ const IntraLedgerUsdPaymentSendMutation = GT.Field({ extensions: { complexity: 120, }, @@ -30,7 +31,7 @@ const LnNoAmountUsdInvoiceFeeProbeMutation = GT.Field({ args: { input: { type: GT.NonNull(LnNoAmountUsdInvoiceFeeProbeInput) }, }, - resolve: async (_, args) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, paymentRequest, amount } = args.input for (const input of [walletId, paymentRequest, amount]) { @@ -39,6 +40,15 @@ const LnNoAmountUsdInvoiceFeeProbeMutation = GT.Field({ } } + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, + walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(routedWalletId)] } + } + // FLASH FORK: create IBEX fee estimation instead of Galoy fee estimation // const { result: feeSatAmount, error } = // await Payments.getNoAmountLightningFeeEstimationForUsdWallet({ @@ -49,17 +59,19 @@ const LnNoAmountUsdInvoiceFeeProbeMutation = GT.Field({ // TODO: Move Ibex call to Payments interface const checkedAmount = await usdWalletAmountFromWalletId({ - walletId, + walletId: routedWalletId, amount: amount.toString(), }) if (checkedAmount instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(checkedAmount)] } } - const resp: IbexFeeEstimation | IbexError = await Ibex.getLnFeeEstimation({ - invoice: paymentRequest as Bolt11, - send: checkedAmount, - }) - if (resp instanceof IbexError) return { errors: [mapAndParseErrorForGqlResponse(resp)] } + const resp: IbexFeeEstimation | IbexError = + await Ibex.getLnFeeEstimation({ + invoice: paymentRequest as Bolt11, + send: checkedAmount, + }) + if (resp instanceof IbexError) + return { errors: [mapAndParseErrorForGqlResponse(resp)] } // if (resp.amount === undefined) return new UnexpectedIbexResponse("Unable to parse fee.") // const feeSatAmount: PaymentAmount = { diff --git a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts index e8d8d6227..12298ec34 100644 --- a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts +++ b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts @@ -14,6 +14,7 @@ import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fract import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { usdWalletAmountFromWalletId } from "@app/wallets" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" import Ibex from "@services/ibex/client" import { IbexError } from "@services/ibex/errors" @@ -63,7 +64,7 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< args: { input: { type: GT.NonNull(LnNoAmountUsdInvoicePaymentInput) }, }, - resolve: async (_, args, { domainAccount }) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, paymentRequest, amount, memo } = args.input if (walletId instanceof InputValidationError) { @@ -90,8 +91,20 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< if (!domainAccount) throw new Error("Authentication required") // eslint-disable-next-line @typescript-eslint/no-explicit-any - const usCents = await usdWalletAmountFromWalletId({ + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(routedWalletId)], + } + } + + const usCents = await usdWalletAmountFromWalletId({ + walletId: routedWalletId, amount: amount.toString(), }) if (usCents instanceof Error) { @@ -102,7 +115,7 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< } const PayLightningInvoice = await Ibex.payInvoice({ invoice: paymentRequest as Bolt11, - accountId: walletId, + accountId: routedWalletId, send: usCents, }) diff --git a/src/graphql/public/root/mutation/ln-usd-invoice-create.ts b/src/graphql/public/root/mutation/ln-usd-invoice-create.ts index 2cbedab73..064a5d4d3 100644 --- a/src/graphql/public/root/mutation/ln-usd-invoice-create.ts +++ b/src/graphql/public/root/mutation/ln-usd-invoice-create.ts @@ -9,6 +9,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import LnInvoicePayload from "@graphql/public/types/payload/ln-invoice" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import { Wallets } from "@app/index" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" const LnUsdInvoiceCreateInput = GT.Input({ @@ -18,7 +19,10 @@ const LnUsdInvoiceCreateInput = GT.Input({ type: GT.NonNull(WalletId), description: "Wallet ID for a USD wallet belonging to the current user.", }, - amount: { type: GT.NonNull(FractionalCentAmount), description: "Amount in USD cents." }, + amount: { + type: GT.NonNull(FractionalCentAmount), + description: "Amount in USD cents.", + }, memo: { type: Memo, description: "Optional memo for the lightning invoice." }, expiresIn: { type: Minutes, @@ -27,7 +31,7 @@ const LnUsdInvoiceCreateInput = GT.Input({ }), }) -const LnUsdInvoiceCreateMutation = GT.Field({ +const LnUsdInvoiceCreateMutation = GT.Field({ extensions: { complexity: 120, }, @@ -39,7 +43,7 @@ const LnUsdInvoiceCreateMutation = GT.Field({ args: { input: { type: GT.NonNull(LnUsdInvoiceCreateInput) }, }, - resolve: async (_, args) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, amount, memo, expiresIn } = args.input for (const input of [walletId, amount, memo, expiresIn]) { @@ -48,8 +52,17 @@ const LnUsdInvoiceCreateMutation = GT.Field({ } } - const invoice = await Wallets.addInvoiceForSelfForUsdWallet({ + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(routedWalletId)] } + } + + const invoice = await Wallets.addInvoiceForSelfForUsdWallet({ + walletId: routedWalletId, amount, memo, expiresIn, diff --git a/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts b/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts index ccfb2c7bb..c1a06e346 100644 --- a/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts +++ b/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts @@ -3,6 +3,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import CentAmountPayload from "@graphql/public/types/payload/cent-amount" import LnPaymentRequest from "@graphql/shared/types/scalar/ln-payment-request" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" import { checkedToWalletId } from "@domain/wallets" @@ -53,7 +54,7 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< args: { input: { type: GT.NonNull(LnUsdInvoiceFeeProbeInput) }, }, - resolve: async (_, args) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, paymentRequest } = args.input if (walletId instanceof Error) { @@ -68,6 +69,15 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< if (walletIdChecked instanceof Error) return { errors: [mapAndParseErrorForGqlResponse(walletIdChecked)] } + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, + walletId: walletIdChecked, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(routedWalletId)] } + } + // FLASH FORK: create IBEX fee estimation instead of Galoy fee estimation // const { result: feeSatAmount, error } = // await Payments.getLightningFeeEstimationForUsdWallet({ @@ -75,7 +85,7 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< // uncheckedPaymentRequest: paymentRequest, // }) - const wallet = await WalletsRepository().findById(walletIdChecked) + const wallet = await WalletsRepository().findById(routedWalletId) if (wallet instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(wallet)] } } @@ -84,8 +94,9 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< invoice: paymentRequest as Bolt11, currency: wallet.currency, }) - if (resp instanceof IbexError) return { errors: [mapAndParseErrorForGqlResponse(resp)] } - + if (resp instanceof IbexError) + return { errors: [mapAndParseErrorForGqlResponse(resp)] } + return { errors: [], invoiceAmount: resp.invoice, diff --git a/src/graphql/public/root/mutation/onchain-usd-payment-send.ts b/src/graphql/public/root/mutation/onchain-usd-payment-send.ts index b45224a6c..f85b02c55 100644 --- a/src/graphql/public/root/mutation/onchain-usd-payment-send.ts +++ b/src/graphql/public/root/mutation/onchain-usd-payment-send.ts @@ -12,6 +12,7 @@ import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fract import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { Wallets } from "@app/index" import { usdWalletAmountFromWalletId } from "@app/wallets" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" const OnChainUsdPaymentSendInput = GT.Input({ name: "OnChainUsdPaymentSendInput", @@ -47,7 +48,7 @@ const OnChainUsdPaymentSendMutation = GT.Field< args: { input: { type: GT.NonNull(OnChainUsdPaymentSendInput) }, }, - resolve: async (_, args, { domainAccount }) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, address, amount, memo, speed } = args.input if (walletId instanceof Error) { @@ -67,8 +68,20 @@ const OnChainUsdPaymentSendMutation = GT.Field< } if (!domainAccount) throw new Error("Authentication required") - const usdAmount = await usdWalletAmountFromWalletId({ + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { + status: PaymentSendStatus.Failure.value, + errors: [mapAndParseErrorForGqlResponse(routedWalletId)], + } + } + + const usdAmount = await usdWalletAmountFromWalletId({ + walletId: routedWalletId, amount: amount.toString(), }) if (usdAmount instanceof Error) { @@ -77,24 +90,24 @@ const OnChainUsdPaymentSendMutation = GT.Field< errors: [mapAndParseErrorForGqlResponse(usdAmount)], } } - + const result = await Wallets.payOnChainByWalletId({ senderAccount: domainAccount, - senderWalletId: walletId, + senderWalletId: routedWalletId, amount: usdAmount, address, speed, memo, }) if (result instanceof Error) { - return { - status: PaymentSendStatus.Failure.value, - errors: [mapAndParseErrorForGqlResponse(result)] + return { + status: PaymentSendStatus.Failure.value, + errors: [mapAndParseErrorForGqlResponse(result)], } } return { - status: result.status.value, - errors: [] + status: result.status.value, + errors: [], } }, }) diff --git a/src/graphql/public/root/query/account-default-wallet-id.ts b/src/graphql/public/root/query/account-default-wallet-id.ts index 23763edda..f91ebc4a1 100644 --- a/src/graphql/public/root/query/account-default-wallet-id.ts +++ b/src/graphql/public/root/query/account-default-wallet-id.ts @@ -1,10 +1,11 @@ +import { resolveCashWalletPresentationForAccount } from "@app/cash-wallet-cutover" import { mapError } from "@graphql/error-map" import { GT } from "@graphql/index" import Username from "@graphql/shared/types/scalar/username" import WalletId from "@graphql/shared/types/scalar/wallet-id" import { AccountsRepository } from "@services/mongoose" -const AccountDefaultWalletIdQuery = GT.Field({ +const AccountDefaultWalletIdQuery = GT.Field({ deprecationReason: "will be migrated to AccountDefaultWalletId", type: GT.NonNull(WalletId), args: { @@ -12,7 +13,7 @@ const AccountDefaultWalletIdQuery = GT.Field({ type: GT.NonNull(Username), }, }, - resolve: async (_, args) => { + resolve: async (_, args, { cashWalletClientCapabilities }) => { const { username } = args if (username instanceof Error) { @@ -24,8 +25,13 @@ const AccountDefaultWalletIdQuery = GT.Field({ throw mapError(account) } - const walletId = account.defaultWalletId - return walletId + const presentation = await resolveCashWalletPresentationForAccount({ + account, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.defaultWalletId }, }) diff --git a/src/graphql/public/root/query/account-default-wallet.ts b/src/graphql/public/root/query/account-default-wallet.ts index 1387c0169..8d5b12f47 100644 --- a/src/graphql/public/root/query/account-default-wallet.ts +++ b/src/graphql/public/root/query/account-default-wallet.ts @@ -1,4 +1,4 @@ -import { Wallets } from "@app" +import { resolveCashWalletPresentationForAccount } from "@app/cash-wallet-cutover" import { CouldNotFindWalletFromUsernameAndCurrencyError } from "@domain/errors" import { mapError } from "@graphql/error-map" import { GT } from "@graphql/index" @@ -7,7 +7,7 @@ import WalletCurrency from "@graphql/shared/types/scalar/wallet-currency" import PublicWallet from "@graphql/public/types/abstract/public-wallet" import { AccountsRepository } from "@services/mongoose" -const AccountDefaultWalletQuery = GT.Field({ +const AccountDefaultWalletQuery = GT.Field({ type: GT.NonNull(PublicWallet), args: { username: { @@ -15,7 +15,7 @@ const AccountDefaultWalletQuery = GT.Field({ }, walletCurrency: { type: WalletCurrency }, }, - resolve: async (_, args) => { + resolve: async (_, args, { cashWalletClientCapabilities }) => { const { username, walletCurrency } = args if (username instanceof Error) { @@ -27,16 +27,21 @@ const AccountDefaultWalletQuery = GT.Field({ throw mapError(account) } - const wallets = await Wallets.listWalletsByAccountId(account.id) - if (wallets instanceof Error) { - throw mapError(wallets) - } + const presentation = await resolveCashWalletPresentationForAccount({ + account, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) if (!walletCurrency) { - return wallets.find((wallet) => wallet.id === account.defaultWalletId) + return presentation.wallets.find( + (wallet) => wallet.id === presentation.defaultWalletId, + ) } - const wallet = wallets.find((wallet) => wallet.currency === walletCurrency) + const wallet = presentation.wallets.find( + (wallet) => wallet.currency === walletCurrency, + ) if (!wallet) { throw mapError(new CouldNotFindWalletFromUsernameAndCurrencyError(username)) } diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index e2f607717..30c15eada 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -358,6 +358,25 @@ input CaptchaRequestAuthCodeInput { validationCode: String! } +type CashWalletCutover { + completedAt: Timestamp + cutoverVersion: Int! + pauseReason: String + pausedAt: Timestamp + runId: String + scheduledAt: Timestamp + startedAt: Timestamp + state: CashWalletCutoverState! + updatedAt: Timestamp! + updatedBy: String +} + +enum CashWalletCutoverState { + COMPLETE + IN_PROGRESS + PRE +} + type CashoutOffer { """The rate used when withdrawing to a JMD bank account""" exchangeRate: JMDCents @@ -1296,6 +1315,7 @@ type Query { btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] businessMapMarkers: [MapMarker!]! + cashWalletCutover: CashWalletCutover! currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload @@ -1654,7 +1674,7 @@ A wallet belonging to an account which contains a USDT balance and a list of tra """ type UsdtWallet implements Wallet { accountId: ID! - balance: FractionalCentAmount! + balance: FractionalCentAmount id: ID! isExternal: Boolean! lnurlp: Lnurl diff --git a/src/graphql/public/types/object/business-account.ts b/src/graphql/public/types/object/business-account.ts index 97daf3670..a9980a89e 100644 --- a/src/graphql/public/types/object/business-account.ts +++ b/src/graphql/public/types/object/business-account.ts @@ -1,4 +1,8 @@ -import { Accounts, Prices, Wallets } from "@app" +import { Accounts, Prices } from "@app" +import { + cashWalletTransactionWalletIdsForPresentation, + resolveCashWalletPresentationForAccount, +} from "@app/cash-wallet-cutover" import { majorToMinorUnit, @@ -15,8 +19,6 @@ import { checkedConnectionArgs, } from "@graphql/connections" -import { WalletsRepository } from "@services/mongoose" - import IAccount from "../abstract/account" import Wallet from "../../../shared/types/abstract/wallet" @@ -30,7 +32,7 @@ import { TransactionConnection } from "../../../shared/types/object/transaction" import RealtimePrice from "./realtime-price" import { NotificationSettings } from "./notification-settings" -const BusinessAccount = GT.Object({ +const BusinessAccount = GT.Object({ name: "BusinessAccount", interfaces: () => [IAccount], isTypeOf: () => false, @@ -42,15 +44,36 @@ const BusinessAccount = GT.Object({ wallets: { type: GT.NonNullList(Wallet), - resolve: async (source: Account) => { - return Wallets.listWalletsByAccountId(source.id) + resolve: async ( + source: Account, + args, + { cashWalletClientCapabilities }: GraphQLPublicContextAuth, + ) => { + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.wallets }, }, defaultWalletId: { type: GT.NonNull(WalletId), - resolve: (source, args, { domainAccount }: { domainAccount: Account }) => - domainAccount.defaultWalletId, + resolve: async ( + source: Account, + args, + { cashWalletClientCapabilities }: GraphQLPublicContextAuth, + ) => { + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.defaultWalletId + }, }, level: { @@ -60,8 +83,7 @@ const BusinessAccount = GT.Object({ displayCurrency: { type: GT.NonNull(DisplayCurrency), - resolve: (source, args, { domainAccount }: { domainAccount: Account }) => - domainAccount.displayCurrency, + resolve: (source, args, { domainAccount }) => domainAccount.displayCurrency, }, realtimePrice: { @@ -123,21 +145,28 @@ const BusinessAccount = GT.Object({ type: GT.List(WalletId), }, }, - resolve: async (source, args) => { + resolve: async ( + source: Account, + args, + { cashWalletClientCapabilities }: GraphQLPublicContextAuth, + ) => { const paginationArgs = checkedConnectionArgs(args) if (paginationArgs instanceof Error) { throw paginationArgs } + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + let { walletIds } = args - if (!walletIds) { - const wallets = await WalletsRepository().listByAccountId(source.id) - if (wallets instanceof Error) { - throw mapError(wallets) - } - walletIds = wallets.map((wallet) => wallet.id) - } + walletIds = cashWalletTransactionWalletIdsForPresentation({ + walletIds, + presentation, + }) const { result, error } = await Accounts.getTransactionsForAccountByWalletIds({ account: source, diff --git a/src/graphql/public/types/object/consumer-account.ts b/src/graphql/public/types/object/consumer-account.ts index 53e3e5a17..660b3a999 100644 --- a/src/graphql/public/types/object/consumer-account.ts +++ b/src/graphql/public/types/object/consumer-account.ts @@ -1,4 +1,8 @@ -import { Accounts, Prices, Wallets } from "@app" +import { Accounts, Prices } from "@app" +import { + cashWalletTransactionWalletIdsForPresentation, + resolveCashWalletPresentationForAccount, +} from "@app/cash-wallet-cutover" import { majorToMinorUnit, @@ -21,8 +25,6 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import RealtimePrice from "@graphql/public/types/object/realtime-price" import DisplayCurrency from "@graphql/shared/types/scalar/display-currency" -import { WalletsRepository } from "@services/mongoose" - import { listEndpoints } from "@app/callback" import AccountLevel from "../../../shared/types/scalar/account-level" @@ -54,14 +56,28 @@ const ConsumerAccount = GT.Object({ wallets: { type: GT.NonNullList(Wallet), - resolve: async (source) => { - return Wallets.listWalletsByAccountId(source.id) + resolve: async (source, args, { cashWalletClientCapabilities }) => { + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.wallets }, }, defaultWalletId: { type: GT.NonNull(WalletId), - resolve: (source) => source.defaultWalletId, + resolve: async (source, args, { cashWalletClientCapabilities }) => { + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.defaultWalletId + }, }, displayCurrency: { @@ -145,21 +161,24 @@ const ConsumerAccount = GT.Object({ type: GT.List(WalletId), }, }, - resolve: async (source, args) => { + resolve: async (source, args, { cashWalletClientCapabilities }) => { const paginationArgs = checkedConnectionArgs(args) if (paginationArgs instanceof Error) { throw paginationArgs } + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + let { walletIds } = args - if (!walletIds) { - const wallets = await WalletsRepository().listByAccountId(source.id) - if (wallets instanceof Error) { - throw mapError(wallets) - } - walletIds = wallets.map((wallet) => wallet.id) - } + walletIds = cashWalletTransactionWalletIdsForPresentation({ + walletIds, + presentation, + }) const { result, error } = await Accounts.getTransactionsForAccountByWalletIds({ account: source, diff --git a/src/graphql/shared/root/query/cash-wallet-cutover.ts b/src/graphql/shared/root/query/cash-wallet-cutover.ts new file mode 100644 index 000000000..71ca79e82 --- /dev/null +++ b/src/graphql/shared/root/query/cash-wallet-cutover.ts @@ -0,0 +1,14 @@ +import { GT } from "@graphql/index" +import CashWalletCutoverObject from "@graphql/shared/types/object/cash-wallet-cutover" +import { CashWalletCutoverRepository } from "@services/mongoose/cash-wallet-cutover" + +const CashWalletCutoverQuery = GT.Field({ + type: GT.NonNull(CashWalletCutoverObject), + resolve: async () => { + const config = await CashWalletCutoverRepository().getConfig() + if (config instanceof Error) throw config + return config + }, +}) + +export default CashWalletCutoverQuery diff --git a/src/graphql/shared/types/object/cash-wallet-cutover.ts b/src/graphql/shared/types/object/cash-wallet-cutover.ts new file mode 100644 index 000000000..6570e1b69 --- /dev/null +++ b/src/graphql/shared/types/object/cash-wallet-cutover.ts @@ -0,0 +1,21 @@ +import { GT } from "@graphql/index" +import Timestamp from "@graphql/shared/types/scalar/timestamp" +import CashWalletCutoverState from "@graphql/shared/types/scalar/cash-wallet-cutover-state" + +const CashWalletCutoverObject = GT.Object({ + name: "CashWalletCutover", + fields: () => ({ + state: { type: GT.NonNull(CashWalletCutoverState) }, + scheduledAt: { type: Timestamp }, + startedAt: { type: Timestamp }, + completedAt: { type: Timestamp }, + pausedAt: { type: Timestamp }, + pauseReason: { type: GT.String }, + cutoverVersion: { type: GT.NonNull(GT.Int) }, + runId: { type: GT.String }, + updatedBy: { type: GT.String }, + updatedAt: { type: GT.NonNull(Timestamp) }, + }), +}) + +export default CashWalletCutoverObject diff --git a/src/graphql/shared/types/object/usd-wallet.ts b/src/graphql/shared/types/object/usd-wallet.ts index 305a855bb..510351520 100644 --- a/src/graphql/shared/types/object/usd-wallet.ts +++ b/src/graphql/shared/types/object/usd-wallet.ts @@ -9,6 +9,7 @@ import { mapError } from "@graphql/error-map" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import { Wallets } from "@app" +import { resolveCashWalletPresentationForAccount } from "@app/cash-wallet-cutover" import { WalletCurrency as WalletCurrencyDomain, USDTAmount } from "@domain/shared" import { WalletType } from "@domain/wallets" @@ -22,6 +23,18 @@ import Lnurl from "../scalar/lnurl" import { TransactionConnection } from "./transaction" +export const usdtMicrosToUsdCents = (usdtMicros: bigint | number | string): number => { + const [wholeMicros, fractionalMicros] = usdtMicros.toString().split(".") + if (fractionalMicros && !/^0+$/.test(fractionalMicros)) { + throw new Error(`Cannot convert fractional USDT micros ${usdtMicros} to USD cents`) + } + + const amount = USDTAmount.smallestUnits(wholeMicros) + if (amount instanceof Error) throw amount + + return Number(amount.asUsdCents()) +} + const UsdWallet = GT.Object({ name: "UsdWallet", description: @@ -49,17 +62,34 @@ const UsdWallet = GT.Object({ }, balance: { type: FractionalCentAmount, - resolve: async (source) => { + resolve: async (source, args, ctx) => { if (source.type === WalletType.External) return null + let balanceWallet = source + + if ( + "cashWalletClientCapabilities" in ctx && + ctx.domainAccount?.id === source.accountId + ) { + const presentation = await resolveCashWalletPresentationForAccount({ + account: ctx.domainAccount, + client: ctx.cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + if (source.id === presentation.legacyUsdWallet?.id) { + balanceWallet = presentation.activeSettlementWallet + } + } + const balance = await Wallets.getBalanceForWallet({ - walletId: source.id, - currency: source.currency, + walletId: balanceWallet.id, + currency: balanceWallet.currency, }) if (balance instanceof Error) { throw mapError(balance) } if (balance instanceof USDTAmount) { - return Number(balance.asUsdCents()) + return usdtMicrosToUsdCents(balance.asSmallestUnits()) } return Number(balance.asCents(8)) }, diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index 3dd36759a..40f064cc0 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -56,7 +56,7 @@ const UsdtWallet = GT.Object({ }, balance: { - type: GT.NonNull(FractionalCentAmount), + type: FractionalCentAmount, resolve: async (source) => { const balance = await Wallets.getBalanceForWallet({ walletId: source.id, diff --git a/src/graphql/shared/types/scalar/cash-wallet-cutover-state.ts b/src/graphql/shared/types/scalar/cash-wallet-cutover-state.ts new file mode 100644 index 000000000..831a377f8 --- /dev/null +++ b/src/graphql/shared/types/scalar/cash-wallet-cutover-state.ts @@ -0,0 +1,12 @@ +import { GT } from "@graphql/index" + +const CashWalletCutoverState = GT.Enum({ + name: "CashWalletCutoverState", + values: { + PRE: { value: "pre" }, + IN_PROGRESS: { value: "in_progress" }, + COMPLETE: { value: "complete" }, + }, +}) + +export default CashWalletCutoverState diff --git a/src/scripts/cash-wallet-cutover-dashboard.ts b/src/scripts/cash-wallet-cutover-dashboard.ts new file mode 100644 index 000000000..cbd034b9f --- /dev/null +++ b/src/scripts/cash-wallet-cutover-dashboard.ts @@ -0,0 +1,983 @@ +#!/usr/bin/env node + +import fs from "fs" +import http from "http" + +import express from "express" +import yargs from "yargs" +import { hideBin } from "yargs/helpers" + +import { + buildCashWalletCutoverOperatorSnapshot, + CashWalletCutoverOperatorManifestAccount, + CashWalletCutoverOperatorSnapshot, + formatCashWalletCutoverOperatorSnapshotCsv, + formatOperatorBalance, + OperatorBalance, + parseCashWalletCutoverOperatorManifest, + refreshOperatorAccountCutoverBalanceAudit, +} from "@app/cash-wallet-cutover/operator-dashboard" +import { discoverCashWalletCutoverAccounts } from "@app/cash-wallet-cutover/discovery" +import { buildCashWalletCutoverPreflightReport } from "@app/cash-wallet-cutover/preflight" +import { getBalanceForWallet } from "@app/wallets" +import { WalletCurrency } from "@domain/shared" +import { setupMongoConnection } from "@services/mongodb" +import { + AccountsRepository, + CashWalletCutoverRepository, + WalletsRepository, +} from "@services/mongoose" +import { baseLogger } from "@services/logger" +import { getFunderWalletId } from "@services/ledger/caching" + +const BALANCE_TIMEOUT_MS = 7_500 +const BALANCE_READ_ATTEMPTS = 3 +const BALANCE_READ_SPACING_MS = 1_000 + +const args = yargs(hideBin(process.argv)) + .option("port", { type: "number", default: 3450 }) + .option("manifest", { type: "array", string: true, demandOption: true }) + .option("expected-accounts", { type: "number", default: 60 }) + .option("snapshot-ttl-ms", { type: "number", default: 5_000 }) + .option("run-id", { type: "string" }) + .option("cutover-version", { type: "number" }) + .option("configPath", { type: "string", demandOption: true }) + .parseSync() + +const readManifestAccounts = (): CashWalletCutoverOperatorManifestAccount[] => { + const accounts = args.manifest.flatMap((manifestPath) => + parseCashWalletCutoverOperatorManifest( + JSON.parse(fs.readFileSync(manifestPath, "utf8")), + ), + ) + + const seen = new Set() + for (const account of accounts) { + if (seen.has(account.accountId)) { + throw new Error(`Duplicate operator dashboard accountId: ${account.accountId}`) + } + seen.add(account.accountId) + } + + if (args["expected-accounts"] && accounts.length !== args["expected-accounts"]) { + throw new Error( + `Expected ${args["expected-accounts"]} operator accounts, loaded ${accounts.length}`, + ) + } + + return accounts +} + +const withBalanceTimeout = (balance: ReturnType) => + Promise.race([ + balance, + new Promise((resolve) => { + setTimeout( + () => resolve(new Error("Balance read timed out") as ApplicationError), + BALANCE_TIMEOUT_MS, + ) + }), + ]) + +let nextBalanceReadAt = 0 + +const readBalanceThrottled = async ( + request: Parameters[0], +) => { + const now = Date.now() + const scheduledAt = Math.max(now, nextBalanceReadAt) + nextBalanceReadAt = scheduledAt + BALANCE_READ_SPACING_MS + + const waitMs = scheduledAt - now + if (waitMs > 0) { + await new Promise((resolve) => setTimeout(resolve, waitMs)) + } + + return withBalanceTimeout(getBalanceForWallet(request)) +} + +const shortId = (value?: string) => (value ? value.slice(0, 8) : "-") + +type CachedBalance = OperatorBalance & { + walletId: WalletId + updatedAt?: string +} + +const html = ` + + + + + Cash Wallet Cutover Dashboard + + + +
+
+

Cash Wallet Cutover Dashboard

+
Raw Mongo wallets plus lazy IBEX balances. Presentation filtering is bypassed.
+
+
+ canStart: - + Loading... + + +
+
+
+
+
+ + + + + + +
+
+ + + + + + + + + + + + + + + + + +
#PhoneAccountDefaultUSDUSD BalanceUSDTUSDT BalanceAuditMigrationAnomalies
+
+
+ + +` + +const start = async () => { + const manifestAccounts = readManifestAccounts() + const accountsRepo = AccountsRepository() + const walletsRepo = WalletsRepository() + const migrationsRepo = CashWalletCutoverRepository() + const migrationLookup = + args["run-id"] && args["cutover-version"] + ? { runId: args["run-id"], cutoverVersion: args["cutover-version"] } + : undefined + + await setupMongoConnection() + + const loadTreasuryAccountIds = async (): Promise => { + const funderWalletId = await getFunderWalletId() + + const funderWallet = await walletsRepo.findById(funderWalletId) + if (funderWallet instanceof Error) throw funderWallet + + return [funderWallet.accountId] + } + + const treasuryAccountIds = await loadTreasuryAccountIds() + + let cache: + | { + snapshot: CashWalletCutoverOperatorSnapshot + cachedAt: number + } + | undefined + let pending: Promise | undefined + const walletCurrencies = new Map() + const balanceCache = new Map() + const balanceQueue: Array<{ walletId: WalletId; currency: WalletCurrency }> = [] + const queuedBalanceIds = new Set() + let activeBalanceId: WalletId | undefined + let balanceWorker: Promise | undefined + + const registerSnapshotWallets = (snapshot: CashWalletCutoverOperatorSnapshot) => { + for (const account of [...snapshot.accounts, ...snapshot.treasury.accounts]) { + for (const wallet of [...account.usdWallets, ...account.usdtWallets]) { + walletCurrencies.set(wallet.id, wallet.currency) + if (!balanceCache.has(wallet.id)) { + balanceCache.set(wallet.id, { + walletId: wallet.id, + currency: wallet.currency, + display: "loading", + minorUnits: "0", + minorUnitsNumber: 0, + status: "loading", + }) + } + } + } + } + + const runBalanceWorker = () => { + if (balanceWorker) return balanceWorker + + balanceWorker = (async () => { + while (balanceQueue.length > 0) { + const request = balanceQueue.shift() + if (!request) continue + + queuedBalanceIds.delete(request.walletId) + activeBalanceId = request.walletId + balanceCache.set(request.walletId, { + walletId: request.walletId, + currency: request.currency, + display: "loading", + minorUnits: "0", + minorUnitsNumber: 0, + status: "loading", + }) + + const balance = await readBalanceThrottled({ + walletId: request.walletId, + currency: request.currency, + }) + balanceCache.set(request.walletId, { + walletId: request.walletId, + ...formatOperatorBalance( + { id: request.walletId, currency: request.currency } as Wallet, + balance, + ), + updatedAt: new Date().toISOString(), + }) + } + })().finally(() => { + activeBalanceId = undefined + balanceWorker = undefined + if (balanceQueue.length > 0) runBalanceWorker() + }) + + return balanceWorker + } + + const enqueueBalance = ({ + walletId, + currency, + force, + }: { + walletId: WalletId + currency: WalletCurrency + force: boolean + }) => { + const cached = balanceCache.get(walletId) + if (!force && cached && cached.status !== "loading") return + if (queuedBalanceIds.has(walletId) || activeBalanceId === walletId) return + + queuedBalanceIds.add(walletId) + balanceQueue.push({ walletId, currency }) + runBalanceWorker() + } + + const snapshotWithCachedBalances = ( + currentSnapshot: CashWalletCutoverOperatorSnapshot, + ): CashWalletCutoverOperatorSnapshot => ({ + ...currentSnapshot, + accounts: currentSnapshot.accounts.map((account) => + refreshOperatorAccountCutoverBalanceAudit({ + ...account, + usdWallets: account.usdWallets.map((wallet) => ({ + ...wallet, + balance: balanceCache.get(wallet.id) ?? wallet.balance, + })), + usdtWallets: account.usdtWallets.map((wallet) => ({ + ...wallet, + balance: balanceCache.get(wallet.id) ?? wallet.balance, + })), + }), + ), + treasury: { + ...currentSnapshot.treasury, + accounts: currentSnapshot.treasury.accounts.map((account) => ({ + ...account, + usdWallets: account.usdWallets.map((wallet) => ({ + ...wallet, + balance: balanceCache.get(wallet.id) ?? wallet.balance, + })), + usdtWallets: account.usdtWallets.map((wallet) => ({ + ...wallet, + balance: balanceCache.get(wallet.id) ?? wallet.balance, + })), + })), + }, + }) + + const buildSnapshot = async () => { + const config = await migrationsRepo.getConfig() + if (config instanceof Error) throw config + const lookup = + migrationLookup ?? + (config.runId + ? { + cutoverVersion: config.cutoverVersion, + runId: config.runId, + } + : undefined) + const discoveries = lookup + ? await discoverCashWalletCutoverAccounts({ + accountsRepo, + walletsRepo, + }) + : undefined + if (discoveries instanceof Error) throw discoveries + const preflightReport = + lookup && discoveries + ? buildCashWalletCutoverPreflightReport({ + cutoverVersion: lookup.cutoverVersion, + runId: lookup.runId, + discoveries, + }) + : undefined + + const result = await buildCashWalletCutoverOperatorSnapshot({ + manifestAccounts, + accountsRepo, + walletsRepo, + migrationsRepo, + migrationLookup: lookup, + preflightReport, + discoveredAccounts: discoveries, + treasuryAccountIds, + balanceReadAttempts: BALANCE_READ_ATTEMPTS, + balanceMode: "structural", + getBalanceForWallet: (request) => + readBalanceThrottled({ + walletId: request.walletId, + currency: request.currency ?? WalletCurrency.Usd, + }), + }) + registerSnapshotWallets(result) + return result + } + + const snapshot = async (force: boolean) => { + const now = Date.now() + if (!force && cache && now - cache.cachedAt < args["snapshot-ttl-ms"]) { + return cache.snapshot + } + if (pending) return pending + + pending = buildSnapshot() + .then((result) => { + cache = { snapshot: result, cachedAt: Date.now() } + return result + }) + .finally(() => { + pending = undefined + }) + return pending + } + + const app = express() + app.get("/", (_req, res) => res.type("html").send(html)) + app.get("/api/snapshot", async (req, res) => { + try { + res.json(await snapshot(req.query.refresh === "1")) + } catch (error) { + baseLogger.error({ error }, "Cash wallet cutover dashboard snapshot failed") + res.status(500).json({ + error: error instanceof Error ? error.message : String(error), + }) + } + }) + app.get("/api/balances", async (req, res) => { + try { + await snapshot(false) + const rawWalletIds = + typeof req.query.walletIds === "string" ? req.query.walletIds : "" + const requestedWalletIds = rawWalletIds + ? rawWalletIds + .split(",") + .map((walletId) => walletId.trim()) + .filter(Boolean) + : Array.from(walletCurrencies.keys()) + const force = req.query.refresh === "1" + + for (const rawWalletId of requestedWalletIds) { + const walletId = rawWalletId as WalletId + const currency = walletCurrencies.get(walletId) + if (!currency) continue + enqueueBalance({ walletId, currency, force }) + } + + const balances: Record = {} + for (const rawWalletId of requestedWalletIds) { + const walletId = rawWalletId as WalletId + const cached = balanceCache.get(walletId) + if (cached) balances[walletId] = cached + } + + res.json({ + balances, + queue: { + pending: balanceQueue.length, + active: activeBalanceId, + }, + }) + } catch (error) { + baseLogger.error({ error }, "Cash wallet cutover dashboard balance refresh failed") + res.status(500).json({ + error: error instanceof Error ? error.message : String(error), + }) + } + }) + app.get("/api/balance-status", async (_req, res) => { + const balances = Array.from(balanceCache.values()) + res.json({ + known: walletCurrencies.size, + cached: balances.length, + fresh: balances.filter((balance) => balance.status === "fresh").length, + errors: balances.filter((balance) => balance.status === "error").length, + loading: balances.filter((balance) => balance.status === "loading").length, + queue: { + pending: balanceQueue.length, + active: activeBalanceId, + }, + }) + }) + app.get("/api/export.csv", async (_req, res) => { + try { + const currentSnapshot = await snapshot(false) + const csv = formatCashWalletCutoverOperatorSnapshotCsv( + snapshotWithCachedBalances(currentSnapshot), + ) + const runId = currentSnapshot.cutover.runId ?? "unknown-run" + res + .type("text/csv") + .attachment(`cash-wallet-cutover-${runId}-${Date.now()}.csv`) + .send(csv) + } catch (error) { + baseLogger.error({ error }, "Cash wallet cutover dashboard CSV export failed") + res.status(500).json({ + error: error instanceof Error ? error.message : String(error), + }) + } + }) + + const server = http.createServer(app) + server.listen(args.port, "127.0.0.1", () => { + baseLogger.info( + { port: args.port, accounts: manifestAccounts.length }, + "Cash wallet cutover dashboard listening", + ) + console.log(`Cash wallet cutover dashboard: http://localhost:${args.port}`) + }) +} + +start().catch((error) => { + baseLogger.error({ error }, "Cash wallet cutover dashboard failed") + process.exit(1) +}) diff --git a/src/scripts/cash-wallet-cutover.ts b/src/scripts/cash-wallet-cutover.ts new file mode 100644 index 000000000..5e481a074 --- /dev/null +++ b/src/scripts/cash-wallet-cutover.ts @@ -0,0 +1,166 @@ +#!/usr/bin/env node + +import yargs from "yargs" +import { hideBin } from "yargs/helpers" + +import { CashWalletCutover } from "@app" +import { addWalletIfNonexistent } from "@app/accounts" +import { setupMongoConnection } from "@services/mongodb" +import { + AccountsRepository, + CashWalletCutoverRepository, + WalletsRepository, +} from "@services/mongoose" +import { baseLogger } from "@services/logger" + +const args = yargs(hideBin(process.argv)) + .command("preview", "discover accounts and print the migration plan without writes") + .command( + "provision-usdt-wallets", + "create missing destination USDT wallets before preparing migrations", + ) + .command("prepare", "discover accounts and upsert migration records") + .command("start", "mark a prepared cutover run in progress") + .command("run-batch", "run one locked migration worker batch") + .command("status", "print cutover config and migration counts") + .command("complete", "mark cutover complete after all migrations finish") + .demandCommand(1) + .option("cutover-version", { type: "number", demandOption: true }) + .option("run-id", { type: "string", demandOption: true }) + .option("operator", { type: "string", default: "unknown" }) + .option("worker-id", { type: "string", default: `worker-${process.pid}` }) + .option("limit", { type: "number", default: 25 }) + .option("step-delay-ms", { type: "number", default: 0 }) + .option("provision-limit", { type: "number" }) + .option("provision-delay-ms", { type: "number", default: 12_500 }) + .option("provision-retry-delay-ms", { type: "number", default: 60_000 }) + .option("max-provision-attempts", { type: "number", default: 5 }) + .option("dry-run", { type: "boolean", default: false }) + .option("lock-stale-seconds", { type: "number", default: 300 }) + .option("configPath", { type: "string", demandOption: true }) + .parseSync() + +const repository = CashWalletCutoverRepository() + +const toJson = (result: unknown) => { + console.log(JSON.stringify(result, null, 2)) +} + +const run = async () => { + const command = args._[0] + const cutoverVersion = args["cutover-version"] + const runId = args["run-id"] + + switch (command) { + case "preview": { + const result = await CashWalletCutover.previewPrimaryCashWalletCutover({ + cutoverVersion, + runId, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "provision-usdt-wallets": { + const result = await CashWalletCutover.provisionPrimaryCashWalletUsdtWallets({ + cutoverVersion, + runId, + accountsRepo: AccountsRepository(), + walletsRepo: WalletsRepository(), + migrationsRepo: repository, + addWalletIfNonexistent, + provisionLimit: args["provision-limit"], + provisionDelayMs: args["provision-delay-ms"], + provisionRetryDelayMs: args["provision-retry-delay-ms"], + maxProvisionAttempts: args["max-provision-attempts"], + dryRun: args["dry-run"], + }) + if (result instanceof Error) throw result + toJson(result) + if (result.failed.length > 0) { + throw new Error( + `Failed to provision ${result.failed.length} destination USDT wallet(s)`, + ) + } + return + } + + case "prepare": { + const result = await CashWalletCutover.preparePrimaryCashWalletCutover({ + cutoverVersion, + runId, + accountsRepo: AccountsRepository(), + walletsRepo: WalletsRepository(), + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "start": { + const result = await CashWalletCutover.startPrimaryCashWalletCutover({ + cutoverVersion, + runId, + actor: args.operator, + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "run-batch": { + const result = await CashWalletCutover.runPrimaryCashWalletCutoverBatch({ + cutoverVersion, + runId, + workerId: args["worker-id"], + limit: args.limit, + stepDelayMs: args["step-delay-ms"], + lockStaleBefore: new Date(Date.now() - args["lock-stale-seconds"] * 1000), + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "status": { + const result = await CashWalletCutover.getPrimaryCashWalletCutoverStatus({ + cutoverVersion, + runId, + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "complete": { + const result = await CashWalletCutover.completePrimaryCashWalletCutover({ + cutoverVersion, + runId, + actor: args.operator, + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + default: + throw new Error(`Unsupported cash wallet cutover command: ${command}`) + } +} + +setupMongoConnection() + .then(async (mongoose) => { + await run() + await mongoose?.connection.close() + process.exit(0) + }) + .catch((error) => { + baseLogger.error({ error }, "Cash wallet cutover operator command failed") + process.exit(1) + }) diff --git a/src/servers/graphql-main-server.ts b/src/servers/graphql-main-server.ts index a0c3b1598..dfbeb53e6 100644 --- a/src/servers/graphql-main-server.ts +++ b/src/servers/graphql-main-server.ts @@ -6,7 +6,6 @@ import { AuthorizationError } from "@graphql/error" import { gqlMainSchema, mutationFields, queryFields } from "@graphql/public" import { bootstrap } from "@app/bootstrap" -import { activateLndHealthCheck } from "@services/lnd/health" import { baseLogger } from "@services/logger" import { setupMongoConnection } from "@services/mongodb" import { shield } from "graphql-shield" @@ -20,6 +19,7 @@ import { import { NextFunction, Request, Response } from "express" import { parseIps } from "@domain/accounts-ips" +import { parseCashWalletClientCapabilities } from "@app/cash-wallet-cutover/client-capability" import { startApolloServerForAdminSchema } from "./graphql-admin-server" import { isAuthenticated, startApolloServer } from "./graphql-server" @@ -44,8 +44,12 @@ const setGqlContext = async ( tokenPayload, ip, }) + const cashWalletClientCapabilities = parseCashWalletClientCapabilities(req.headers) - req.gqlContext = gqlContext + req.gqlContext = { + ...gqlContext, + cashWalletClientCapabilities, + } return addAttributesToCurrentSpanAndPropagate( { @@ -56,6 +60,11 @@ const setGqlContext = async ( [SemanticAttributes.HTTP_USER_AGENT]: req.headers["user-agent"], [ACCOUNT_USERNAME]: gqlContext?.domainAccount?.username, [SemanticAttributes.ENDUSER_ID]: tokenPayload?.sub, + "cash_wallet.client_presentation": + cashWalletClientCapabilities.cashWalletPresentation, + "cash_wallet.client_usdt_supported": String( + cashWalletClientCapabilities.hasUsdtCashWalletSupport, + ), }, next, ) @@ -101,9 +110,9 @@ export async function startApolloServerForCoreSchema() { if (require.main === module) { setupMongoConnection(true) .then(async () => { - // activateLndHealthCheck() // + // activateLndHealthCheck() - const res = await bootstrap() + await bootstrap() // if (res instanceof Error) throw res await Promise.race([ diff --git a/src/servers/index.files.d.ts b/src/servers/index.files.d.ts index 29513578f..c918274f8 100644 --- a/src/servers/index.files.d.ts +++ b/src/servers/index.files.d.ts @@ -13,6 +13,7 @@ type GraphQLPublicContext = { domainAccount: Account | undefined ip: IpAddress | undefined sessionId: SessionId | undefined + cashWalletClientCapabilities: import("@app/cash-wallet-cutover/client-capability").CashWalletClientCapabilities } type GraphQLPublicContextAuth = Omit & { diff --git a/src/servers/middlewares/session.ts b/src/servers/middlewares/session.ts index 03201a40a..2631ce286 100644 --- a/src/servers/middlewares/session.ts +++ b/src/servers/middlewares/session.ts @@ -1,6 +1,7 @@ import DataLoader from "dataloader" import { Accounts, Transactions } from "@app" +import { DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES } from "@app/cash-wallet-cutover" import { recordExceptionInCurrentSpan } from "@services/tracing" import jsonwebtoken from "jsonwebtoken" @@ -67,8 +68,7 @@ export const sessionPublicContext = async ({ error: txnMetadata, }) return keys.map(() => undefined) - } - else if (txnMetadata instanceof Error) { + } else if (txnMetadata instanceof Error) { recordExceptionInCurrentSpan({ error: txnMetadata, level: txnMetadata.level, @@ -88,5 +88,6 @@ export const sessionPublicContext = async ({ domainAccount, ip, sessionId, + cashWalletClientCapabilities: DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES, } } diff --git a/src/servers/ws-server.ts b/src/servers/ws-server.ts index 9255a7f46..c98cbb28a 100644 --- a/src/servers/ws-server.ts +++ b/src/servers/ws-server.ts @@ -10,6 +10,7 @@ import jsonwebtoken from "jsonwebtoken" import { parseIps } from "@domain/accounts-ips" import { ErrorLevel } from "@domain/shared" +import { parseCashWalletClientCapabilities } from "@app/cash-wallet-cutover" import jwksRsa from "jwks-rsa" @@ -60,12 +61,16 @@ const getContext = async ( fnName: "getContext", fn: async () => { const connectionParams = ctx.connectionParams + const cashWalletClientCapabilities = parseCashWalletClientCapabilities({ + ...ctx.extra?.request?.headers, + ...connectionParams, + }) // TODO: check if nginx pass the ip to the header // TODO: ip not been used currently for subscription. // implement some rate limiting. const ipString = UNSECURE_IP_FROM_REQUEST_OBJECT - ? connectionParams?.ip ?? ctx.extra?.request?.socket?.remoteAddress + ? (connectionParams?.ip ?? ctx.extra?.request?.socket?.remoteAddress) : connectionParams?.["x-real-ip"] || connectionParams?.["x-forwarded-for"] const ip = parseIps(ipString) @@ -82,10 +87,14 @@ const getContext = async ( sub: kratosCookieRes.kratosUserId, } - return sessionPublicContext({ + const context = await sessionPublicContext({ tokenPayload, ip, }) + return { + ...context, + cashWalletClientCapabilities, + } } const kratosToken = authz?.slice(7) as AuthToken @@ -106,10 +115,14 @@ const getContext = async ( return false } - return sessionPublicContext({ + const context = await sessionPublicContext({ tokenPayload, ip, }) + return { + ...context, + cashWalletClientCapabilities, + } }, })() } diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index 8464520e1..cf448778e 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -17,6 +17,7 @@ import IbexClient, { PayToALnurlPayResponse201, SendToAddressCopyBodyParam, SendToAddressCopyResponse200, + IbexUrls, } from "ibex-client" import { IbexConfig } from "@config" @@ -55,6 +56,8 @@ const Ibex = new IbexClient( Redis, ) +const IbexUrlConfig = IbexUrls[IbexConfig.environment] + const createAccount = async ( name: string, currencyId: IbexCurrencyId, @@ -262,42 +265,39 @@ const getIbexToken = async (): Promise => { const cached = await Ibex.authentication.storage.getAccessToken() if (typeof cached === "string") return `${cached}` - // The SDK uses a single base URL for all calls, but the sandbox auth domain is separate - const resp = await fetch(`${IbexConfig.url}/auth/signin`, { + const body = new URLSearchParams({ + grant_type: "client_credentials", + client_id: IbexConfig.clientId, + client_secret: IbexConfig.clientSecret, + audience: IbexUrlConfig.audience, + }) + + const resp = await fetch(`${IbexUrlConfig.authDomain}/oauth/token`, { method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: IbexConfig.email, password: IbexConfig.password }), + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: body.toString(), }).catch( (err: unknown) => new IbexError(err instanceof Error ? err : new Error(String(err))), ) if (resp instanceof IbexError) return resp if (!resp.ok) { - const body = await resp.text().catch(() => "") - return new IbexError(new Error(`IBEX sign-in failed: ${resp.status} — ${body}`)) + const responseBody = await resp.text().catch(() => "") + return new IbexError( + new Error(`IBEX token request failed: ${resp.status} — ${responseBody}`), + ) } const data = (await resp.json()) as { - accessToken?: string - accessTokenExpiresAt?: number - refreshToken?: string - refreshTokenExpiresAt?: number + access_token?: string + expires_in?: number } - if (!data.accessToken) - return new IbexError(new Error("IBEX sign-in: no access token in response")) + if (!data.access_token) + return new IbexError(new Error("IBEX token request: no access_token in response")) - await Ibex.authentication.storage.setAccessToken( - data.accessToken, - data.accessTokenExpiresAt, - ) - if (data.refreshToken) { - await Ibex.authentication.storage.setRefreshToken( - data.refreshToken, - data.refreshTokenExpiresAt, - ) - } + await Ibex.authentication.storage.setAccessToken(data.access_token, data.expires_in) - return data.accessToken as string + return data.access_token } const ibexFetch = async ( @@ -305,7 +305,7 @@ const ibexFetch = async ( path: string, init: RequestInit = {}, ): Promise => { - const url = `${IbexConfig.url}${path}` + const url = `${IbexUrlConfig.hubUrl}${path}` const resp = await fetch(url, { ...init, headers: { @@ -439,7 +439,10 @@ const getEthereumUsdtOption = async (): Promise const getIbexCurrencyId = async ( currency: WalletCurrency, ): Promise => { - const data = await ibexGet<{ currencies: IbexCurrency[] }>("", "/currency/all") + const token = await getIbexToken() + if (token instanceof IbexError) return token + + const data = await ibexGet<{ currencies: IbexCurrency[] }>(token, "/currency/all") if (data instanceof IbexError) return data const currencyId = data.currencies.find((c) => c.name === currency)?.id if (!currencyId) return new IbexError(new Error(`Currency ${currency} not found`)) diff --git a/src/services/mongoose/cash-wallet-cutover.ts b/src/services/mongoose/cash-wallet-cutover.ts new file mode 100644 index 000000000..4ce2f19d8 --- /dev/null +++ b/src/services/mongoose/cash-wallet-cutover.ts @@ -0,0 +1,348 @@ +import { randomUUID } from "crypto" + +import { CouldNotUpdateError } from "@domain/errors" + +import { parseRepositoryError } from "./utils" +import { CashWalletCutoverConfig, CashWalletMigration } from "./schema" + +const CONFIG_ID = "cash_wallet_cutover" + +const TERMINAL_STATUSES: CashWalletMigrationStatus[] = [ + "complete", + "failed", + "requires_operator_review", + "skipped_already_migrated", + "rollback_started", + "rolled_back", +] + +type UpsertMigrationArgs = { + accountId: AccountId + accountUuid?: AccountUuid + legacyUsdWalletId: WalletId + destinationUsdtWalletId: WalletId + previousDefaultWalletId?: WalletId + cutoverVersion: number + runId: string + idempotencyKey: string +} + +type TransitionMigrationArgs = { + id: string + from: CashWalletMigrationStatus + to: CashWalletMigrationStatus + cutoverVersion: number + runId: string + patch?: Partial> +} + +type LockMigrationArgs = { + id: string + workerId: string + staleBefore: Date + cutoverVersion: number + runId: string +} + +type MarkMigrationFailedArgs = Omit & { + error: Error + status: "failed" | "requires_operator_review" +} + +const defaultConfig = (): CashWalletCutoverConfig => ({ + state: "pre", + cutoverVersion: 1, + updatedAt: new Date(0), +}) + +const resultToConfig = ( + record: CashWalletCutoverConfigRecord, +): CashWalletCutoverConfig => ({ + state: record.state, + scheduledAt: record.scheduledAt, + startedAt: record.startedAt, + completedAt: record.completedAt, + pausedAt: record.pausedAt, + pauseReason: record.pauseReason, + updatedBy: record.updatedBy, + cutoverVersion: record.cutoverVersion, + runId: record.runId, + updatedAt: record.updatedAt, +}) + +const resultToMigration = (record: CashWalletMigrationRecord): CashWalletMigration => ({ + id: record._id, + accountId: record.accountId as AccountId, + accountUuid: record.accountUuid as AccountUuid | undefined, + legacyUsdWalletId: record.legacyUsdWalletId as WalletId, + destinationUsdtWalletId: record.destinationUsdtWalletId as WalletId, + previousDefaultWalletId: record.previousDefaultWalletId as WalletId | undefined, + cutoverVersion: record.cutoverVersion, + runId: record.runId, + status: record.status, + sourceBalanceUsdCents: record.sourceBalanceUsdCents, + destinationAmountUsdtMicros: record.destinationAmountUsdtMicros, + destinationStartingBalanceUsdtMicros: record.destinationStartingBalanceUsdtMicros, + feeAmountUsdCents: record.feeAmountUsdCents, + feeAmountUsdtMicros: record.feeAmountUsdtMicros, + balanceMoveInvoicePaymentRequest: record.balanceMoveInvoicePaymentRequest, + balanceMoveInvoicePaymentHash: record.balanceMoveInvoicePaymentHash, + balanceMovePaymentTransactionId: record.balanceMovePaymentTransactionId, + feeReimbursementInvoicePaymentRequest: record.feeReimbursementInvoicePaymentRequest, + feeReimbursementInvoicePaymentHash: record.feeReimbursementInvoicePaymentHash, + feeReimbursementPaymentTransactionId: record.feeReimbursementPaymentTransactionId, + estimatedFee: record.estimatedFee, + idempotencyKey: record.idempotencyKey, + attempts: record.attempts, + lastError: record.lastError, + lockedAt: record.lockedAt, + lockedBy: record.lockedBy, + startedAt: record.startedAt, + completedAt: record.completedAt, + updatedAt: record.updatedAt, +}) + +export const CashWalletCutoverRepository = () => { + const getConfig = async (): Promise => { + try { + const result = await CashWalletCutoverConfig.findById(CONFIG_ID) + if (!result) return defaultConfig() + return resultToConfig(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const updateConfig = async ( + patch: Partial, + actor?: string, + ): Promise => { + try { + const $set: Record = { updatedBy: actor, updatedAt: new Date() } + const $unset: Record = {} + + for (const [key, value] of Object.entries(patch)) { + if (value === undefined) { + $unset[key] = 1 + } else { + $set[key] = value + } + } + + const update = Object.keys($unset).length > 0 ? { $set, $unset } : { $set } + + const result = await CashWalletCutoverConfig.findOneAndUpdate( + { _id: CONFIG_ID }, + update, + { upsert: true, new: true }, + ) + return resultToConfig(result) + } catch (err) { + return parseRepositoryError(err) + } + } + return resultToConfig(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const upsertMigration = async ( + args: UpsertMigrationArgs, + ): Promise => { + try { + const now = new Date() + const result = await CashWalletMigration.findOneAndUpdate( + { accountId: args.accountId, runId: args.runId }, + { + $setOnInsert: { + _id: randomUUID(), + ...args, + status: "not_started", + attempts: 0, + updatedAt: now, + }, + }, + { upsert: true, new: true }, + ) + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const findMigrationByAccountId = async ({ + accountId, + cutoverVersion, + runId, + }: { + accountId: AccountId + cutoverVersion: number + runId: string + }): Promise => { + try { + const result = await CashWalletMigration.findOne({ + accountId, + cutoverVersion, + runId, + }) + if (!result) return null + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const transitionMigration = async ({ + id, + from, + to, + cutoverVersion, + runId, + patch = {}, + }: TransitionMigrationArgs): Promise => { + try { + const result = await CashWalletMigration.findOneAndUpdate( + { _id: id, status: from, cutoverVersion, runId }, + { $set: { ...patch, status: to, updatedAt: new Date() } }, + { new: true }, + ) + if (!result) + return new CouldNotUpdateError("Could not transition cash wallet migration") + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const acquireMigrationLock = async ({ + id, + workerId, + staleBefore, + cutoverVersion, + runId, + }: LockMigrationArgs): Promise => { + try { + const result = await CashWalletMigration.findOneAndUpdate( + { + _id: id, + cutoverVersion, + runId, + $or: [{ lockedAt: null }, { lockedAt: { $lt: staleBefore } }], + }, + { $set: { lockedAt: new Date(), lockedBy: workerId, updatedAt: new Date() } }, + { new: true }, + ) + if (!result) + return new CouldNotUpdateError("Could not acquire cash wallet migration lock") + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const releaseMigrationLock = async ({ + id, + workerId, + cutoverVersion, + runId, + }: Omit): Promise< + CashWalletMigration | RepositoryError + > => { + try { + const result = await CashWalletMigration.findOneAndUpdate( + { _id: id, lockedBy: workerId, cutoverVersion, runId }, + { $set: { lockedAt: null, lockedBy: null, updatedAt: new Date() } }, + { new: true }, + ) + if (!result) + return new CouldNotUpdateError("Could not release cash wallet migration lock") + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const markMigrationFailed = async ({ + id, + workerId, + cutoverVersion, + runId, + error, + status, + }: MarkMigrationFailedArgs): Promise => { + try { + const result = await CashWalletMigration.findOneAndUpdate( + { _id: id, lockedBy: workerId, cutoverVersion, runId }, + { + $set: { + status, + lastError: error.message, + lockedAt: null, + lockedBy: null, + updatedAt: new Date(), + }, + $inc: { attempts: 1 }, + }, + { new: true }, + ) + if (!result) + return new CouldNotUpdateError("Could not mark cash wallet migration failed") + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const listRunnableMigrations = async ({ + cutoverVersion, + runId, + limit, + }: { + cutoverVersion: number + runId: string + limit?: number + }): Promise => { + try { + const results = await CashWalletMigration.find({ + cutoverVersion, + runId, + status: { $nin: TERMINAL_STATUSES }, + }) + .sort({ updatedAt: 1 }) + .limit(limit ?? 0) + return results.map(resultToMigration) + } catch (err) { + return parseRepositoryError(err) + } + } + + const countByStatus = async ({ + cutoverVersion, + runId, + status, + }: { + cutoverVersion: number + runId: string + status: CashWalletMigrationStatus + }): Promise => { + try { + return CashWalletMigration.countDocuments({ cutoverVersion, runId, status }) + } catch (err) { + return parseRepositoryError(err) + } + } + + return { + getConfig, + updateConfig, + upsertMigration, + findMigrationByAccountId, + transitionMigration, + acquireMigrationLock, + releaseMigrationLock, + markMigrationFailed, + listRunnableMigrations, + countByStatus, + } +} diff --git a/src/services/mongoose/index.ts b/src/services/mongoose/index.ts index 8e5be4f68..e0cb83509 100644 --- a/src/services/mongoose/index.ts +++ b/src/services/mongoose/index.ts @@ -8,3 +8,4 @@ export * from "./wallet-invoices" export * from "./wallet-on-chain-addresses" export * from "./wallet-onchain-pending-receive" export * from "./merchants" +export * from "./cash-wallet-cutover" diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index ef7b0ebf9..a15023362 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -620,6 +620,68 @@ export const WalletOnChainPendingReceive = WalletOnChainPendingReceiveSchema, ) +const CashWalletCutoverConfigSchema = new Schema({ + _id: { type: String, default: "cash_wallet_cutover" }, + state: { type: String, enum: ["pre", "in_progress", "complete"], required: true }, + scheduledAt: Date, + startedAt: Date, + completedAt: Date, + pausedAt: Date, + pauseReason: String, + updatedBy: String, + cutoverVersion: { type: Number, required: true, default: 1 }, + runId: String, + updatedAt: { type: Date, default: Date.now }, +}) + +export const CashWalletCutoverConfig = mongoose.model( + "CashWalletCutoverConfig", + CashWalletCutoverConfigSchema, +) + +const CashWalletMigrationSchema = new Schema({ + _id: { type: String, required: true }, + accountId: { type: String, required: true, index: true }, + accountUuid: String, + legacyUsdWalletId: { type: String, required: true }, + destinationUsdtWalletId: { type: String, required: true }, + previousDefaultWalletId: String, + cutoverVersion: { type: Number, required: true, index: true }, + runId: { type: String, required: true, index: true }, + status: { type: String, required: true, index: true }, + sourceBalanceUsdCents: String, + destinationAmountUsdtMicros: String, + destinationStartingBalanceUsdtMicros: String, + feeAmountUsdCents: String, + feeAmountUsdtMicros: String, + balanceMoveInvoicePaymentRequest: String, + balanceMoveInvoicePaymentHash: String, + balanceMovePaymentTransactionId: String, + feeReimbursementInvoicePaymentRequest: String, + feeReimbursementInvoicePaymentHash: String, + feeReimbursementPaymentTransactionId: String, + estimatedFee: Boolean, + idempotencyKey: { type: String, required: true }, + attempts: { type: Number, default: 0 }, + lastError: String, + lockedAt: Date, + lockedBy: String, + startedAt: Date, + completedAt: Date, + updatedAt: { type: Date, default: Date.now, index: true }, +}) + +CashWalletMigrationSchema.index({ accountId: 1, runId: 1 }, { unique: true }) +CashWalletMigrationSchema.index({ idempotencyKey: 1 }, { unique: true }) +CashWalletMigrationSchema.index({ cutoverVersion: 1, status: 1, updatedAt: 1 }) +CashWalletMigrationSchema.index({ runId: 1, status: 1 }) +CashWalletMigrationSchema.index({ lockedAt: 1 }) + +export const CashWalletMigration = mongoose.model( + "CashWalletMigration", + CashWalletMigrationSchema, +) + const BridgeVirtualAccountSchema = new Schema({ // unique: true enforces one VA per account at the DB layer — idempotency guard accountId: { type: String, required: true, unique: true }, diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 39d5a3da9..332c918f5 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -103,6 +103,52 @@ interface AccountRecord { save: () => Promise } +interface CashWalletCutoverConfigRecord { + _id: string + state: CashWalletCutoverState + scheduledAt?: Date + startedAt?: Date + completedAt?: Date + pausedAt?: Date + pauseReason?: string + updatedBy?: string + cutoverVersion: number + runId?: string + updatedAt: Date +} + +interface CashWalletMigrationRecord { + _id: string + accountId: string + accountUuid?: string + legacyUsdWalletId: string + destinationUsdtWalletId: string + previousDefaultWalletId?: string + cutoverVersion: number + runId: string + status: CashWalletMigrationStatus + sourceBalanceUsdCents?: string + destinationAmountUsdtMicros?: string + destinationStartingBalanceUsdtMicros?: string + feeAmountUsdCents?: string + feeAmountUsdtMicros?: string + balanceMoveInvoicePaymentRequest?: string + balanceMoveInvoicePaymentHash?: string + balanceMovePaymentTransactionId?: string + feeReimbursementInvoicePaymentRequest?: string + feeReimbursementInvoicePaymentHash?: string + feeReimbursementPaymentTransactionId?: string + estimatedFee?: boolean + idempotencyKey: string + attempts: number + lastError?: string + lockedAt?: Date + lockedBy?: string + startedAt?: Date + completedAt?: Date + updatedAt: Date +} + interface LocationRecord { type: "Point" coordinates: CoordinateRecord From f405524e65f2a32f6687116cb7c8955f98b43548 Mon Sep 17 00:00:00 2001 From: Vandana Date: Mon, 1 Jun 2026 20:03:18 -0700 Subject: [PATCH 17/43] fix(bridge): restore cutover repository build --- src/services/mongoose/cash-wallet-cutover.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/services/mongoose/cash-wallet-cutover.ts b/src/services/mongoose/cash-wallet-cutover.ts index 4ce2f19d8..f5c9e8da0 100644 --- a/src/services/mongoose/cash-wallet-cutover.ts +++ b/src/services/mongoose/cash-wallet-cutover.ts @@ -141,11 +141,6 @@ export const CashWalletCutoverRepository = () => { return parseRepositoryError(err) } } - return resultToConfig(result) - } catch (err) { - return parseRepositoryError(err) - } - } const upsertMigration = async ( args: UpsertMigrationArgs, From 79d764f0462332570946607a7464f87260fcc542 Mon Sep 17 00:00:00 2001 From: Forge0x Date: Tue, 2 Jun 2026 16:20:56 -0400 Subject: [PATCH 18/43] fix(cutover): preserve cash wallet history after cutover (#380) --- src/app/cash-wallet-cutover/index.ts | 1 + src/app/cash-wallet-cutover/presentation.ts | 59 ++++- .../cash-wallet-cutover/recipient-routing.ts | 43 ++++ ...d-invoice-create-on-behalf-of-recipient.ts | 18 +- .../public/types/object/business-account.ts | 4 +- .../public/types/object/consumer-account.ts | 4 +- .../types/object/cash-wallet-history.ts | 37 +++ src/graphql/shared/types/object/usd-wallet.ts | 19 +- .../shared/types/object/usdt-wallet.ts | 19 +- .../cash-wallet-cutover/presentation.spec.ts | 107 ++++++++ .../recipient-routing.spec.ts | 58 +++++ .../get-transactions-for-wallet.spec.ts | 94 ++++++- ...oice-create-on-behalf-of-recipient.spec.ts | 69 +++++ .../cash-wallet-history-resolvers.spec.ts | 235 ++++++++++++++++++ 14 files changed, 742 insertions(+), 25 deletions(-) create mode 100644 src/app/cash-wallet-cutover/recipient-routing.ts create mode 100644 src/graphql/shared/types/object/cash-wallet-history.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/presentation.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/recipient-routing.spec.ts create mode 100644 test/flash/unit/graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient.spec.ts create mode 100644 test/flash/unit/graphql/public/types/object/cash-wallet-history-resolvers.spec.ts diff --git a/src/app/cash-wallet-cutover/index.ts b/src/app/cash-wallet-cutover/index.ts index 67d632d84..89ad096c5 100644 --- a/src/app/cash-wallet-cutover/index.ts +++ b/src/app/cash-wallet-cutover/index.ts @@ -22,3 +22,4 @@ export * from "./orchestrator" export * from "./lifecycle" export * from "./preview" export * from "./provision-usdt-wallets" +export * from "./recipient-routing" diff --git a/src/app/cash-wallet-cutover/presentation.ts b/src/app/cash-wallet-cutover/presentation.ts index 7c41478a8..873c0e415 100644 --- a/src/app/cash-wallet-cutover/presentation.ts +++ b/src/app/cash-wallet-cutover/presentation.ts @@ -59,24 +59,65 @@ export const resolveCashWalletPresentation = ({ } } -export const cashWalletTransactionWalletIdsForPresentation = ({ - walletIds, +const cashWalletHistoryWalletIds = ({ + selectedWalletIds, presentation, }: { - walletIds?: WalletId[] + selectedWalletIds: WalletId[] presentation: CashWalletPresentationResult }): WalletId[] => { - const selectedWalletIds = walletIds ?? presentation.wallets.map((wallet) => wallet.id) + const { legacyUsdWallet, activeSettlementWallet } = presentation + if (!legacyUsdWallet) return Array.from(new Set(selectedWalletIds)) - if (!presentation.legacyUsdWallet) return selectedWalletIds + const cashWalletIds = new Set([legacyUsdWallet.id, activeSettlementWallet.id]) return Array.from( new Set( - selectedWalletIds.map((walletId) => - walletId === presentation.legacyUsdWallet?.id - ? presentation.activeSettlementWallet.id - : walletId, + selectedWalletIds.flatMap((walletId) => + cashWalletIds.has(walletId) + ? [activeSettlementWallet.id, legacyUsdWallet.id] + : [walletId], ), ), ) } + +export const cashWalletHistoryWalletIdsForPresentation = ({ + walletIds, + presentation, +}: { + walletIds?: WalletId[] + presentation: CashWalletPresentationResult +}): WalletId[] => { + const selectedWalletIds = walletIds ?? presentation.wallets.map((wallet) => wallet.id) + + return cashWalletHistoryWalletIds({ selectedWalletIds, presentation }) +} + +export const cashWalletHistoryWalletsForPresentation = ({ + wallets, + presentation, +}: { + wallets: Wallet[] + presentation: CashWalletPresentationResult +}): Wallet[] => { + const historyWalletIds = cashWalletHistoryWalletIds({ + selectedWalletIds: wallets.map((wallet) => wallet.id), + presentation, + }) + + const walletById = new Map( + [ + ...presentation.wallets, + presentation.legacyUsdWallet, + presentation.activeSettlementWallet, + ...wallets, + ] + .filter((wallet): wallet is Wallet => Boolean(wallet)) + .map((wallet) => [wallet.id, wallet]), + ) + + return historyWalletIds + .map((walletId) => walletById.get(walletId)) + .filter((wallet): wallet is Wallet => Boolean(wallet)) +} diff --git a/src/app/cash-wallet-cutover/recipient-routing.ts b/src/app/cash-wallet-cutover/recipient-routing.ts new file mode 100644 index 000000000..84a96eabb --- /dev/null +++ b/src/app/cash-wallet-cutover/recipient-routing.ts @@ -0,0 +1,43 @@ +import { AccountsRepository, WalletsRepository } from "@services/mongoose" + +import { CashWalletClientCapabilities } from "./client-capability" +import { resolveCashWalletMutationWalletIdForAccount } from "./presentation-for-account" + +type RecipientRoutingAccountsRepository = { + findById(accountId: AccountId): Promise +} + +type RecipientRoutingWalletsRepository = { + findById(walletId: WalletId): Promise + listByAccountId(accountId: AccountId): Promise +} + +type ResolveMutationWalletIdForAccount = + typeof resolveCashWalletMutationWalletIdForAccount + +export const resolveCashWalletRecipientMutationWalletId = async ({ + recipientWalletId, + client, + accountsRepo = AccountsRepository(), + walletsRepo = WalletsRepository(), + resolveMutationWalletIdForAccount = resolveCashWalletMutationWalletIdForAccount, +}: { + recipientWalletId: WalletId + client: CashWalletClientCapabilities + accountsRepo?: RecipientRoutingAccountsRepository + walletsRepo?: RecipientRoutingWalletsRepository + resolveMutationWalletIdForAccount?: ResolveMutationWalletIdForAccount +}): Promise => { + const recipientWallet = await walletsRepo.findById(recipientWalletId) + if (recipientWallet instanceof Error) return recipientWallet + + const recipientAccount = await accountsRepo.findById(recipientWallet.accountId) + if (recipientAccount instanceof Error) return recipientAccount + + return resolveMutationWalletIdForAccount({ + account: recipientAccount, + walletId: recipientWalletId, + client, + walletsRepo, + }) +} diff --git a/src/graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient.ts b/src/graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient.ts index 65fd9cc7d..6a0a32787 100644 --- a/src/graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient.ts +++ b/src/graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient.ts @@ -1,6 +1,7 @@ import dedent from "dedent" import { Wallets } from "@app" +import { resolveCashWalletRecipientMutationWalletId } from "@app/cash-wallet-cutover" import { GT } from "@graphql/index" import Memo from "@graphql/shared/types/scalar/memo" @@ -36,7 +37,10 @@ const LnUsdInvoiceCreateOnBehalfOfRecipientInput = GT.Input({ }), }) -const LnUsdInvoiceCreateOnBehalfOfRecipientMutation = GT.Field({ +const LnUsdInvoiceCreateOnBehalfOfRecipientMutation = GT.Field< + null, + GraphQLPublicContext +>({ extensions: { complexity: 120, }, @@ -48,7 +52,7 @@ const LnUsdInvoiceCreateOnBehalfOfRecipientMutation = GT.Field({ args: { input: { type: GT.NonNull(LnUsdInvoiceCreateOnBehalfOfRecipientInput) }, }, - resolve: async (_, args) => { + resolve: async (_, args, { cashWalletClientCapabilities }) => { const { recipientWalletId, amount, memo, descriptionHash, expiresIn } = args.input for (const input of [recipientWalletId, amount, memo, descriptionHash, expiresIn]) { if (input instanceof Error) { @@ -56,8 +60,16 @@ const LnUsdInvoiceCreateOnBehalfOfRecipientMutation = GT.Field({ } } - const invoice = await Wallets.addInvoiceForRecipientForUsdWallet({ + const routedRecipientWalletId = await resolveCashWalletRecipientMutationWalletId({ recipientWalletId, + client: cashWalletClientCapabilities, + }) + if (routedRecipientWalletId instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(routedRecipientWalletId)] } + } + + const invoice = await Wallets.addInvoiceForRecipientForUsdWallet({ + recipientWalletId: routedRecipientWalletId, amount, memo, descriptionHash, diff --git a/src/graphql/public/types/object/business-account.ts b/src/graphql/public/types/object/business-account.ts index a9980a89e..1fd023820 100644 --- a/src/graphql/public/types/object/business-account.ts +++ b/src/graphql/public/types/object/business-account.ts @@ -1,6 +1,6 @@ import { Accounts, Prices } from "@app" import { - cashWalletTransactionWalletIdsForPresentation, + cashWalletHistoryWalletIdsForPresentation, resolveCashWalletPresentationForAccount, } from "@app/cash-wallet-cutover" @@ -163,7 +163,7 @@ const BusinessAccount = GT.Object({ let { walletIds } = args - walletIds = cashWalletTransactionWalletIdsForPresentation({ + walletIds = cashWalletHistoryWalletIdsForPresentation({ walletIds, presentation, }) diff --git a/src/graphql/public/types/object/consumer-account.ts b/src/graphql/public/types/object/consumer-account.ts index 660b3a999..483b00518 100644 --- a/src/graphql/public/types/object/consumer-account.ts +++ b/src/graphql/public/types/object/consumer-account.ts @@ -1,6 +1,6 @@ import { Accounts, Prices } from "@app" import { - cashWalletTransactionWalletIdsForPresentation, + cashWalletHistoryWalletIdsForPresentation, resolveCashWalletPresentationForAccount, } from "@app/cash-wallet-cutover" @@ -175,7 +175,7 @@ const ConsumerAccount = GT.Object({ let { walletIds } = args - walletIds = cashWalletTransactionWalletIdsForPresentation({ + walletIds = cashWalletHistoryWalletIdsForPresentation({ walletIds, presentation, }) diff --git a/src/graphql/shared/types/object/cash-wallet-history.ts b/src/graphql/shared/types/object/cash-wallet-history.ts new file mode 100644 index 000000000..1d8e74228 --- /dev/null +++ b/src/graphql/shared/types/object/cash-wallet-history.ts @@ -0,0 +1,37 @@ +import { + CashWalletClientCapabilities, + cashWalletHistoryWalletsForPresentation, + resolveCashWalletPresentationForAccount, +} from "@app/cash-wallet-cutover" +import { WalletType } from "@domain/wallets" +import { mapError } from "@graphql/error-map" + +type CashWalletHistoryContext = { + domainAccount?: Account + cashWalletClientCapabilities?: CashWalletClientCapabilities +} + +export const resolveCashWalletHistoryWalletsForWalletObject = async ({ + source, + ctx, +}: { + source: Wallet + ctx: CashWalletHistoryContext +}): Promise => { + if (source.type === WalletType.External) return [source] + + const { domainAccount, cashWalletClientCapabilities } = ctx + if (!domainAccount || !cashWalletClientCapabilities) return [source] + if (domainAccount.id !== source.accountId) return [source] + + const presentation = await resolveCashWalletPresentationForAccount({ + account: domainAccount, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return cashWalletHistoryWalletsForPresentation({ + wallets: [source], + presentation, + }) +} diff --git a/src/graphql/shared/types/object/usd-wallet.ts b/src/graphql/shared/types/object/usd-wallet.ts index 510351520..266c5a8ce 100644 --- a/src/graphql/shared/types/object/usd-wallet.ts +++ b/src/graphql/shared/types/object/usd-wallet.ts @@ -21,6 +21,7 @@ import SignedAmount from "../scalar/signed-amount" import OnChainAddress from "../scalar/on-chain-address" import Lnurl from "../scalar/lnurl" +import { resolveCashWalletHistoryWalletsForWalletObject } from "./cash-wallet-history" import { TransactionConnection } from "./transaction" export const usdtMicrosToUsdCents = (usdtMicros: bigint | number | string): number => { @@ -108,14 +109,19 @@ const UsdWallet = GT.Object({ transactions: { type: TransactionConnection, args: connectionArgs, - resolve: async (source, args) => { + resolve: async (source, args, ctx) => { const paginationArgs = checkedConnectionArgs(args) if (paginationArgs instanceof Error) { throw paginationArgs } + const wallets = await resolveCashWalletHistoryWalletsForWalletObject({ + source, + ctx, + }) + const { result, error } = await Wallets.getTransactionsForWallets({ - wallets: [source], + wallets, paginationArgs, }) if (error instanceof Error) { @@ -141,7 +147,7 @@ const UsdWallet = GT.Object({ description: "Returns the items that include this address.", }, }, - resolve: async (source, args) => { + resolve: async (source, args, ctx) => { const paginationArgs = checkedConnectionArgs(args) if (paginationArgs instanceof Error) { throw paginationArgs @@ -150,8 +156,13 @@ const UsdWallet = GT.Object({ const { address } = args if (address instanceof Error) throw address + const wallets = await resolveCashWalletHistoryWalletsForWalletObject({ + source, + ctx, + }) + const { result, error } = await Wallets.getTransactionsForWalletsByAddresses({ - wallets: [source], + wallets, addresses: [address], paginationArgs, }) diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index 40f064cc0..64831b1d1 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -26,6 +26,7 @@ import OnChainAddress from "../scalar/on-chain-address" import Lnurl from "../scalar/lnurl" +import { resolveCashWalletHistoryWalletsForWalletObject } from "./cash-wallet-history" import { TransactionConnection } from "./transaction" const UsdtWallet = GT.Object({ @@ -88,14 +89,19 @@ const UsdtWallet = GT.Object({ transactions: { type: TransactionConnection, args: connectionArgs, - resolve: async (source, args) => { + resolve: async (source, args, ctx) => { const paginationArgs = checkedConnectionArgs(args) if (paginationArgs instanceof Error) { throw paginationArgs } + const wallets = await resolveCashWalletHistoryWalletsForWalletObject({ + source, + ctx, + }) + const { result, error } = await Wallets.getTransactionsForWallets({ - wallets: [source], + wallets, paginationArgs, }) if (error instanceof Error) { @@ -120,7 +126,7 @@ const UsdtWallet = GT.Object({ description: "Returns the items that include this address.", }, }, - resolve: async (source, args) => { + resolve: async (source, args, ctx) => { const paginationArgs = checkedConnectionArgs(args) if (paginationArgs instanceof Error) { throw paginationArgs @@ -129,8 +135,13 @@ const UsdtWallet = GT.Object({ const { address } = args if (address instanceof Error) throw address + const wallets = await resolveCashWalletHistoryWalletsForWalletObject({ + source, + ctx, + }) + const { result, error } = await Wallets.getTransactionsForWalletsByAddresses({ - wallets: [source], + wallets, addresses: [address], paginationArgs, }) diff --git a/test/flash/unit/app/cash-wallet-cutover/presentation.spec.ts b/test/flash/unit/app/cash-wallet-cutover/presentation.spec.ts new file mode 100644 index 000000000..992183907 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/presentation.spec.ts @@ -0,0 +1,107 @@ +import { + cashWalletHistoryWalletIdsForPresentation, + cashWalletHistoryWalletsForPresentation, +} from "@app/cash-wallet-cutover/presentation" +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const accountId = "cash-account-id" as AccountId + +const wallet = ({ id, currency }: { id: string; currency: WalletCurrency }): Wallet => + ({ + id: id as WalletId, + accountId, + currency, + type: WalletType.Checking, + onChainAddressIdentifiers: [], + onChainAddresses: () => [], + lnurlp: `lnurlp-${id}` as Lnurl, + }) as Wallet + +const btcWallet = wallet({ + id: "11111111-1111-4111-8111-111111111111", + currency: WalletCurrency.Btc, +}) +const legacyUsdWallet = wallet({ + id: "22222222-2222-4222-8222-222222222222", + currency: WalletCurrency.Usd, +}) +const usdtWallet = wallet({ + id: "33333333-3333-4333-8333-333333333333", + currency: WalletCurrency.Usdt, +}) + +describe("cash wallet history expansion for presentation", () => { + const legacyCompatPresentation = { + wallets: [btcWallet, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + } + + const usdtPresentation = { + wallets: [btcWallet, usdtWallet], + defaultWalletId: usdtWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + } + + const preCutoverPresentation = { + wallets: [btcWallet, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: legacyUsdWallet, + } + + it("keeps pre-cutover Cash Wallet history on legacy USD only", () => { + expect( + cashWalletHistoryWalletIdsForPresentation({ + presentation: preCutoverPresentation, + }), + ).toEqual([btcWallet.id, legacyUsdWallet.id]) + }) + + it("appends the legacy-compatible USD archive after active USDT history", () => { + expect( + cashWalletHistoryWalletIdsForPresentation({ + presentation: legacyCompatPresentation, + }), + ).toEqual([btcWallet.id, usdtWallet.id, legacyUsdWallet.id]) + }) + + it("expands explicit legacy USD history to active USDT then legacy archive", () => { + expect( + cashWalletHistoryWalletIdsForPresentation({ + walletIds: [legacyUsdWallet.id], + presentation: legacyCompatPresentation, + }), + ).toEqual([usdtWallet.id, legacyUsdWallet.id]) + }) + + it("expands explicit USDT history to active USDT then legacy archive", () => { + expect( + cashWalletHistoryWalletIdsForPresentation({ + walletIds: [usdtWallet.id], + presentation: usdtPresentation, + }), + ).toEqual([usdtWallet.id, legacyUsdWallet.id]) + }) + + it("leaves non-cash wallet filters unchanged", () => { + expect( + cashWalletHistoryWalletIdsForPresentation({ + walletIds: [btcWallet.id], + presentation: legacyCompatPresentation, + }), + ).toEqual([btcWallet.id]) + }) + + it("returns wallet objects for expanded wallet object history calls", () => { + expect( + cashWalletHistoryWalletsForPresentation({ + wallets: [legacyUsdWallet], + presentation: legacyCompatPresentation, + }), + ).toEqual([usdtWallet, legacyUsdWallet]) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/recipient-routing.spec.ts b/test/flash/unit/app/cash-wallet-cutover/recipient-routing.spec.ts new file mode 100644 index 000000000..c2338da0c --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/recipient-routing.spec.ts @@ -0,0 +1,58 @@ +import { resolveCashWalletRecipientMutationWalletId } from "@app/cash-wallet-cutover/recipient-routing" +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const recipientAccountId = "recipient-account-id" as AccountId +const recipientWalletId = "11111111-1111-4111-8111-111111111111" as WalletId +const routedWalletId = "22222222-2222-4222-8222-222222222222" as WalletId + +const recipientWallet = { + id: recipientWalletId, + accountId: recipientAccountId, + currency: WalletCurrency.Usd, + type: WalletType.Checking, + onChainAddressIdentifiers: [], + onChainAddresses: () => [], + lnurlp: "lnurlp-recipient" as Lnurl, +} as Wallet + +const recipientAccount = { + id: recipientAccountId, + uuid: "recipient-account-uuid" as AccountUuid, +} as Account + +const client = { + cashWalletPresentation: "usdt", + hasUsdtCashWalletSupport: true, +} as const + +describe("resolveCashWalletRecipientMutationWalletId", () => { + it("routes recipient legacy USD wallet ids through the recipient account presentation", async () => { + const walletsRepo = { + findById: jest.fn().mockResolvedValue(recipientWallet), + listByAccountId: jest.fn(), + } + const accountsRepo = { + findById: jest.fn().mockResolvedValue(recipientAccount), + } + const resolveMutationWalletIdForAccount = jest.fn().mockResolvedValue(routedWalletId) + + const result = await resolveCashWalletRecipientMutationWalletId({ + recipientWalletId, + client, + walletsRepo, + accountsRepo, + resolveMutationWalletIdForAccount, + }) + + expect(result).toBe(routedWalletId) + expect(walletsRepo.findById).toHaveBeenCalledWith(recipientWalletId) + expect(accountsRepo.findById).toHaveBeenCalledWith(recipientAccountId) + expect(resolveMutationWalletIdForAccount).toHaveBeenCalledWith({ + account: recipientAccount, + walletId: recipientWalletId, + client, + walletsRepo, + }) + }) +}) diff --git a/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts b/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts index d47e1775f..28790116e 100644 --- a/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts +++ b/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts @@ -1,7 +1,32 @@ -import { toWalletTransactions } from "@app/wallets/get-transactions-for-wallet" +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + getAccountTransactions: jest.fn(), + }, +})) + +import { + getTransactionsForWallets, + toWalletTransactions, +} from "@app/wallets/get-transactions-for-wallet" import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" +import Ibex from "@services/ibex/client" import { GResponse200 } from "ibex-client" +const accountId = "account-id" as AccountId + +const wallet = ({ id, currency }: { id: string; currency: WalletCurrency }): Wallet => + ({ + id: id as WalletId, + accountId, + currency, + type: WalletType.Checking, + onChainAddressIdentifiers: [], + onChainAddresses: () => [], + lnurlp: `lnurlp-${id}` as Lnurl, + }) as Wallet + describe("toWalletTransactions", () => { it("maps IBEX USDT currency id to USDT wallet currency", () => { const [transaction] = toWalletTransactions([ @@ -36,3 +61,70 @@ describe("toWalletTransactions", () => { expect(transaction.settlementVia.type).toBe("unknown") }) }) + +describe("getTransactionsForWallets", () => { + beforeEach(() => { + jest.mocked(Ibex.getAccountTransactions).mockReset() + }) + + it("concatenates active wallet history before the legacy archive wallet history", async () => { + const activeSettlementWallet = wallet({ + id: "33333333-3333-4333-8333-333333333333", + currency: WalletCurrency.Usdt, + }) + const legacyUsdWallet = wallet({ + id: "22222222-2222-4222-8222-222222222222", + currency: WalletCurrency.Usd, + }) + + jest + .mocked(Ibex.getAccountTransactions) + .mockImplementation(async ({ account_id }) => { + if (account_id === activeSettlementWallet.id) { + return [ + { + id: "active-newer", + accountId: activeSettlementWallet.id, + amount: 100, + currencyId: 29, + transactionTypeId: 1, + createdAt: "2026-06-01T00:00:00.000Z", + }, + ] as GResponse200 + } + + return [ + { + id: "legacy-archive-older", + accountId: legacyUsdWallet.id, + amount: 200, + currencyId: 3, + transactionTypeId: 1, + createdAt: "2026-05-01T00:00:00.000Z", + }, + ] as GResponse200 + }) + + const result = await getTransactionsForWallets({ + wallets: [activeSettlementWallet, legacyUsdWallet], + paginationArgs: { first: 20 }, + }) + + expect(Ibex.getAccountTransactions).toHaveBeenNthCalledWith(1, { + account_id: activeSettlementWallet.id, + limit: 20, + page: 0, + sort: "settledAt", + }) + expect(Ibex.getAccountTransactions).toHaveBeenNthCalledWith(2, { + account_id: legacyUsdWallet.id, + limit: 20, + page: 0, + sort: "settledAt", + }) + expect(result.result?.slice.map((transaction) => transaction.id)).toEqual([ + "active-newer", + "legacy-archive-older", + ]) + }) +}) diff --git a/test/flash/unit/graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient.spec.ts b/test/flash/unit/graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient.spec.ts new file mode 100644 index 000000000..f7b2834d8 --- /dev/null +++ b/test/flash/unit/graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient.spec.ts @@ -0,0 +1,69 @@ +const mockAddInvoiceForRecipientForUsdWallet = jest.fn() +const mockResolveCashWalletRecipientMutationWalletId = jest.fn() + +jest.mock("@app", () => ({ + Wallets: { + addInvoiceForRecipientForUsdWallet: ( + ...args: Parameters + ) => mockAddInvoiceForRecipientForUsdWallet(...args), + }, +})) + +jest.mock("@app/cash-wallet-cutover", () => ({ + resolveCashWalletRecipientMutationWalletId: ( + ...args: Parameters + ) => mockResolveCashWalletRecipientMutationWalletId(...args), +})) + +import LnUsdInvoiceCreateOnBehalfOfRecipientMutation from "@graphql/public/root/mutation/ln-usd-invoice-create-on-behalf-of-recipient" + +const recipientWalletId = "11111111-1111-4111-8111-111111111111" as WalletId +const routedWalletId = "22222222-2222-4222-8222-222222222222" as WalletId +const amount = 1234 as UsdCents +const invoice = { paymentRequest: "lnbc1-routed" } as LnInvoice + +const client = { + cashWalletPresentation: "usdt", + hasUsdtCashWalletSupport: true, +} as const + +describe("LnUsdInvoiceCreateOnBehalfOfRecipientMutation", () => { + beforeEach(() => { + jest.clearAllMocks() + mockResolveCashWalletRecipientMutationWalletId.mockResolvedValue(routedWalletId) + mockAddInvoiceForRecipientForUsdWallet.mockResolvedValue(invoice) + }) + + it("routes fixed-amount recipient invoices through the recipient account active settlement wallet", async () => { + const result = await LnUsdInvoiceCreateOnBehalfOfRecipientMutation.resolve?.( + null, + { + input: { + recipientWalletId, + amount, + memo: "recipient memo" as Memo, + descriptionHash: undefined, + expiresIn: 5 as Minutes, + }, + }, + { cashWalletClientCapabilities: client } as GraphQLPublicContext, + {} as never, + ) + + expect(mockResolveCashWalletRecipientMutationWalletId).toHaveBeenCalledWith({ + recipientWalletId, + client, + }) + expect(mockAddInvoiceForRecipientForUsdWallet).toHaveBeenCalledWith({ + recipientWalletId: routedWalletId, + amount, + memo: "recipient memo", + descriptionHash: undefined, + expiresIn: 5, + }) + expect(result).toEqual({ + errors: [], + invoice, + }) + }) +}) diff --git a/test/flash/unit/graphql/public/types/object/cash-wallet-history-resolvers.spec.ts b/test/flash/unit/graphql/public/types/object/cash-wallet-history-resolvers.spec.ts new file mode 100644 index 000000000..7a6af825c --- /dev/null +++ b/test/flash/unit/graphql/public/types/object/cash-wallet-history-resolvers.spec.ts @@ -0,0 +1,235 @@ +const mockGetTransactionsForAccountByWalletIds = jest.fn() +const mockGetTransactionsForWallets = jest.fn() +const mockGetTransactionsForWalletsByAddresses = jest.fn() +const mockResolveCashWalletPresentationForAccount = jest.fn() + +jest.mock("@app", () => ({ + Accounts: { + getTransactionsForAccountByWalletIds: ( + ...args: Parameters + ) => mockGetTransactionsForAccountByWalletIds(...args), + }, + Prices: {}, + Wallets: { + getTransactionsForWallets: ( + ...args: Parameters + ) => mockGetTransactionsForWallets(...args), + getTransactionsForWalletsByAddresses: ( + ...args: Parameters + ) => mockGetTransactionsForWalletsByAddresses(...args), + }, +})) + +jest.mock("@app/cash-wallet-cutover", () => { + const presentation = jest.requireActual("@app/cash-wallet-cutover/presentation") + + return { + cashWalletHistoryWalletIdsForPresentation: + presentation.cashWalletHistoryWalletIdsForPresentation, + cashWalletHistoryWalletsForPresentation: + presentation.cashWalletHistoryWalletsForPresentation, + resolveCashWalletPresentationForAccount: ( + ...args: Parameters + ) => mockResolveCashWalletPresentationForAccount(...args), + } +}) + +import ConsumerAccount from "@graphql/public/types/object/consumer-account" +import BusinessAccount from "@graphql/public/types/object/business-account" +import UsdWallet from "@graphql/shared/types/object/usd-wallet" +import UsdtWallet from "@graphql/shared/types/object/usdt-wallet" + +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const accountId = "cash-account-id" as AccountId +const account = { + id: accountId, + uuid: "cash-account-uuid" as AccountUuid, + displayCurrency: "USD" as DisplayCurrency, +} as Account + +const client = { + cashWalletPresentation: "usdt", + hasUsdtCashWalletSupport: true, +} as const + +const context = { + domainAccount: account, + cashWalletClientCapabilities: client, +} as GraphQLPublicContextAuth + +const wallet = ({ + id, + currency, + type = WalletType.Checking, +}: { + id: string + currency: WalletCurrency + type?: WalletType +}): Wallet => + ({ + id: id as WalletId, + accountId, + currency, + type, + onChainAddressIdentifiers: [], + onChainAddresses: () => [], + lnurlp: `lnurlp-${id}` as Lnurl, + }) as Wallet + +const btcWallet = wallet({ + id: "11111111-1111-4111-8111-111111111111", + currency: WalletCurrency.Btc, +}) +const legacyUsdWallet = wallet({ + id: "22222222-2222-4222-8222-222222222222", + currency: WalletCurrency.Usd, +}) +const usdtWallet = wallet({ + id: "33333333-3333-4333-8333-333333333333", + currency: WalletCurrency.Usdt, +}) + +const presentation = { + wallets: [btcWallet, usdtWallet], + defaultWalletId: usdtWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, +} + +const emptyConnectionResult = { + result: { slice: [], total: 0 }, + partialResult: true, +} + +beforeEach(() => { + jest.clearAllMocks() + mockResolveCashWalletPresentationForAccount.mockResolvedValue(presentation) + mockGetTransactionsForAccountByWalletIds.mockResolvedValue(emptyConnectionResult) + mockGetTransactionsForWallets.mockResolvedValue(emptyConnectionResult) + mockGetTransactionsForWalletsByAddresses.mockResolvedValue(emptyConnectionResult) +}) + +const resolveField = async ({ + objectType, + field, + source, + args, +}: { + objectType: typeof ConsumerAccount + field: string + source: unknown + args: Record +}) => { + const resolver = objectType.getFields()[field].resolve + if (!resolver) throw new Error(`Missing resolver for ${field}`) + + return resolver(source, args, context, {} as never, {} as never) +} + +describe("account Cash Wallet transaction resolvers", () => { + it("expands ConsumerAccount legacy USD filters to active USDT history then legacy USD archive", async () => { + await resolveField({ + objectType: ConsumerAccount, + field: "transactions", + source: account, + args: { first: 20, walletIds: [legacyUsdWallet.id] }, + }) + + expect(mockGetTransactionsForAccountByWalletIds).toHaveBeenCalledWith({ + account, + walletIds: [usdtWallet.id, legacyUsdWallet.id], + paginationArgs: { first: 20, walletIds: [legacyUsdWallet.id] }, + }) + }) + + it("expands BusinessAccount default Cash Wallet history to active USDT then legacy USD archive", async () => { + await resolveField({ + objectType: BusinessAccount, + field: "transactions", + source: account, + args: { first: 20 }, + }) + + expect(mockGetTransactionsForAccountByWalletIds).toHaveBeenCalledWith({ + account, + walletIds: [btcWallet.id, usdtWallet.id, legacyUsdWallet.id], + paginationArgs: { first: 20 }, + }) + }) +}) + +describe("wallet object Cash Wallet transaction resolvers", () => { + it("expands legacy USD wallet object transactions to active USDT history then legacy USD archive", async () => { + await resolveField({ + objectType: UsdWallet, + field: "transactions", + source: legacyUsdWallet, + args: { first: 20 }, + }) + + expect(mockGetTransactionsForWallets).toHaveBeenCalledWith({ + wallets: [usdtWallet, legacyUsdWallet], + paginationArgs: { first: 20 }, + }) + }) + + it("expands USDT wallet object transactions to active USDT history then legacy USD archive", async () => { + await resolveField({ + objectType: UsdtWallet, + field: "transactions", + source: usdtWallet, + args: { first: 20 }, + }) + + expect(mockGetTransactionsForWallets).toHaveBeenCalledWith({ + wallets: [usdtWallet, legacyUsdWallet], + paginationArgs: { first: 20 }, + }) + }) + + it("expands wallet object transactionsByAddress across active USDT and legacy USD backing wallets", async () => { + const address = "bc1-cash-wallet-address" as OnChainAddress + + await resolveField({ + objectType: UsdWallet, + field: "transactionsByAddress", + source: legacyUsdWallet, + args: { first: 20, address }, + }) + + expect(mockGetTransactionsForWalletsByAddresses).toHaveBeenCalledWith({ + wallets: [usdtWallet, legacyUsdWallet], + addresses: [address], + paginationArgs: { first: 20, address }, + }) + }) + + it("keeps wrong-account wallet object history scoped to the source wallet only", async () => { + const otherAccountUsdWallet = wallet({ + id: "44444444-4444-4444-8444-444444444444", + currency: WalletCurrency.Usd, + }) + + await resolveField({ + objectType: UsdWallet, + field: "transactions", + source: { + ...otherAccountUsdWallet, + accountId: "other-account-id" as AccountId, + }, + args: { first: 20 }, + }) + + expect(mockGetTransactionsForWallets).toHaveBeenCalledWith({ + wallets: [ + { + ...otherAccountUsdWallet, + accountId: "other-account-id" as AccountId, + }, + ], + paginationArgs: { first: 20 }, + }) + }) +}) From 5c1b954904180eb56631daf43bd2ed160e29c95f Mon Sep 17 00:00:00 2001 From: Forge0x Date: Wed, 3 Jun 2026 11:51:35 -0400 Subject: [PATCH 19/43] fix(wallets): preserve USDT transaction history precision (#382) * fix(wallets): preserve USDT transaction history precision * chore(wallets): restore base relay import * fix(wallets): handle missing IBEX USDT history amounts --- .../wallets/get-transactions-for-wallet.ts | 37 ++++++-- src/domain/shared/index.types.d.ts | 1 + src/domain/wallets/index.types.d.ts | 4 +- .../get-transactions-for-wallet.spec.ts | 95 ++++++++++++++++++- 4 files changed, 126 insertions(+), 11 deletions(-) diff --git a/src/app/wallets/get-transactions-for-wallet.ts b/src/app/wallets/get-transactions-for-wallet.ts index 4d3bec459..9b1d6fd47 100644 --- a/src/app/wallets/get-transactions-for-wallet.ts +++ b/src/app/wallets/get-transactions-for-wallet.ts @@ -77,7 +77,7 @@ export const toWalletTransactions = (ibexResp: GResponse200): IbexTransaction[] const baseTrx: BaseWalletTransaction = { walletId: (trx.accountId || "") as WalletId, settlementAmount: toSettlementAmount(trx.amount, trx.transactionTypeId, currency), - settlementFee: asCurrency(trx.networkFee, currency), + settlementFee: toSettlementMinorUnit(trx.networkFee, currency), settlementCurrency: currency, settlementDisplayAmount: `${trx.amount}`, settlementDisplayFee: `${trx.networkFee}`, @@ -118,24 +118,49 @@ export const toWalletTransactions = (ibexResp: GResponse200): IbexTransaction[] }) } -const asCurrency = (amount: number | undefined, currency: WalletCurrency): Satoshis | UsdCents => { - return currency === "USD" ? amount as UsdCents : amount as Satoshis +type SettlementMinorUnitAmount = Satoshis | UsdCents | UsdtMicros + +const toUsdtMicros = (amount: number): UsdtMicros => { + const usdtAmount = USDTAmount.fromNumber(amount.toString()) + if (usdtAmount instanceof Error) { + baseLogger.error({ err: usdtAmount, amount }, "Failed to parse IBEX USDT amount") + return 0 as UsdtMicros + } + return Number(usdtAmount.asSmallestUnits()) as UsdtMicros +} + +const zeroSettlementMinorUnit = ( + currency: WalletCurrency, +): SettlementMinorUnitAmount => { + if (currency === WalletCurrency.Usd) return 0 as UsdCents + if (currency === WalletCurrency.Usdt) return 0 as UsdtMicros + return 0 as Satoshis +} + +const toSettlementMinorUnit = ( + amount: number | undefined, + currency: WalletCurrency, +): SettlementMinorUnitAmount => { + if (amount === undefined) return zeroSettlementMinorUnit(currency) + if (currency === WalletCurrency.Usd) return amount as UsdCents + if (currency === WalletCurrency.Usdt) return toUsdtMicros(amount) + return amount as Satoshis } const toSettlementAmount = ( ibexAmount: number | undefined, transactionTypeId: number | undefined, currency: WalletCurrency -): Satoshis | UsdCents => { +): SettlementMinorUnitAmount => { if (ibexAmount === undefined) { baseLogger.warn("Ibex did not return transaction amount") - return asCurrency(ibexAmount, currency) + return toSettlementMinorUnit(ibexAmount, currency) } // When sending, make negative const amt = (transactionTypeId === 2 || transactionTypeId === 4) ? -1 * ibexAmount : ibexAmount - return asCurrency(amt, currency) + return toSettlementMinorUnit(amt, currency) } enum SortOrder { diff --git a/src/domain/shared/index.types.d.ts b/src/domain/shared/index.types.d.ts index 4e2015b95..a8e6d7ec1 100644 --- a/src/domain/shared/index.types.d.ts +++ b/src/domain/shared/index.types.d.ts @@ -49,6 +49,7 @@ type WalletDescriptor = PartialWalletDescriptor & { type BtcPaymentAmount = PaymentAmount<"BTC"> type UsdPaymentAmount = PaymentAmount<"USD"> +type UsdtMicros = number & { readonly brand: unique symbol } type RequireField = T & Required> diff --git a/src/domain/wallets/index.types.d.ts b/src/domain/wallets/index.types.d.ts index d86dcbc21..ea93eac15 100644 --- a/src/domain/wallets/index.types.d.ts +++ b/src/domain/wallets/index.types.d.ts @@ -71,8 +71,8 @@ type PartialBaseWalletTransaction = { type BaseWalletTransaction = { readonly walletId: WalletId | undefined - readonly settlementAmount: Satoshis | UsdCents - readonly settlementFee: Satoshis | UsdCents + readonly settlementAmount: Satoshis | UsdCents | UsdtMicros + readonly settlementFee: Satoshis | UsdCents | UsdtMicros readonly settlementCurrency: WalletCurrency readonly settlementDisplayAmount: DisplayCurrencyMajorAmount diff --git a/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts b/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts index 28790116e..1ac0611fe 100644 --- a/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts +++ b/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts @@ -12,6 +12,7 @@ import { import { WalletCurrency } from "@domain/shared" import { WalletType } from "@domain/wallets" import Ibex from "@services/ibex/client" +import { baseLogger } from "@services/logger" import { GResponse200 } from "ibex-client" const accountId = "account-id" as AccountId @@ -28,12 +29,58 @@ const wallet = ({ id, currency }: { id: string; currency: WalletCurrency }): Wal }) as Wallet describe("toWalletTransactions", () => { - it("maps IBEX USDT currency id to USDT wallet currency", () => { + it("maps IBEX USDT currency id to USDT wallet currency with integer micros", () => { + const transactions = toWalletTransactions([ + { + id: "trx-id-1", + accountId: "wallet-id", + amount: 0.17531, + currencyId: 29, + transactionTypeId: 1, + createdAt: "2026-05-13T00:00:00.000Z", + }, + { + id: "trx-id-2", + accountId: "wallet-id", + amount: 9.824690376349, + currencyId: 29, + transactionTypeId: 1, + createdAt: "2026-05-13T00:00:00.000Z", + }, + ] as GResponse200) + + expect(transactions).toHaveLength(2) + expect(transactions[0].settlementCurrency).toBe(WalletCurrency.Usdt) + expect(transactions[0].settlementAmount).toBe(175_310) + expect(transactions[1].settlementAmount).toBe(9_824_690) + expect( + transactions.reduce((sum, transaction) => sum + transaction.settlementAmount, 0), + ).toBe(10_000_000) + }) + + it("maps IBEX USDT send amounts to negative integer micros", () => { + const [transaction] = toWalletTransactions([ + { + id: "trx-id", + accountId: "wallet-id", + amount: 0.5, + networkFee: 0.000001, + currencyId: 29, + transactionTypeId: 2, + createdAt: "2026-05-13T00:00:00.000Z", + }, + ] as GResponse200) + + expect(transaction.settlementCurrency).toBe(WalletCurrency.Usdt) + expect(transaction.settlementAmount).toBe(-500_000) + expect(transaction.settlementFee).toBe(1) + }) + + it("defaults omitted IBEX USDT amount and network fee to zero micros", () => { const [transaction] = toWalletTransactions([ { id: "trx-id", accountId: "wallet-id", - amount: 19446, currencyId: 29, transactionTypeId: 1, createdAt: "2026-05-13T00:00:00.000Z", @@ -41,7 +88,49 @@ describe("toWalletTransactions", () => { ] as GResponse200) expect(transaction.settlementCurrency).toBe(WalletCurrency.Usdt) - expect(transaction.settlementAmount).toBe(19446) + expect(transaction.settlementAmount).toBe(0) + expect(transaction.settlementFee).toBe(0) + }) + + it("logs USDT conversion errors with error details", () => { + const errorSpy = jest.spyOn(baseLogger, "error").mockImplementation() + + const [transaction] = toWalletTransactions([ + { + id: "trx-id", + accountId: "wallet-id", + amount: Number.NaN, + currencyId: 29, + transactionTypeId: 1, + createdAt: "2026-05-13T00:00:00.000Z", + }, + ] as GResponse200) + + expect(transaction.settlementAmount).toBe(0) + expect(errorSpy).toHaveBeenCalledWith( + expect.objectContaining({ err: expect.any(Error), amount: expect.any(Number) }), + "Failed to parse IBEX USDT amount", + ) + + errorSpy.mockRestore() + }) + + it("keeps IBEX USD amounts in integer cents", () => { + const [transaction] = toWalletTransactions([ + { + id: "trx-id", + accountId: "wallet-id", + amount: 500, + networkFee: 12, + currencyId: 3, + transactionTypeId: 1, + createdAt: "2026-05-13T00:00:00.000Z", + }, + ] as GResponse200) + + expect(transaction.settlementCurrency).toBe(WalletCurrency.Usd) + expect(transaction.settlementAmount).toBe(500) + expect(transaction.settlementFee).toBe(12) }) it("does not silently classify unknown IBEX currency ids as BTC", () => { From 886b207f4ccb678a66b4b2521da590447e2a9fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olaniran=20=E2=9A=A1?= <93789719+heyolaniran@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:24:26 +0200 Subject: [PATCH 20/43] feat(bridge): external account webhook + ENG-350 transfer failure reset (#379) * feat(externalAccounts): Webhook endpoint firing for external accounts feat: external accounts event triggering this commit initialize the webhook endpoint to register on Bridge to trigger external account link after plaid completion * fix(ENG-350) : reset pending withdraw state on transfer failure. this commit mark the transfer as failed in our bridge withdrawals collection when something went wrong during the trasfer flow, the user is notified with a popup and on return state, as well for transfer successfully completed * fix(bridge): normalize currency display in withdrawal notification Map "usdt" to "USD" and uppercase other currency codes so the push notification shows a human-readable label instead of a raw enum value. --- .../bridge/send-withdrawal-notification.ts | 2 +- src/config/schema.ts | 3 +- src/config/schema.types.d.ts | 1 + .../mutation/bridge-add-external-account.ts | 4 +- .../object/bridge-external-account-link.ts | 11 +++ .../types/object/bridge-external-account.ts | 2 +- src/services/bridge/webhook-server/index.ts | 2 + .../middleware/verify-signature.ts | 2 +- .../webhook-server/routes/external-account.ts | 80 +++++++++++++++++++ .../bridge/webhook-server/routes/replay.ts | 6 +- .../bridge/webhook-server/routes/transfer.ts | 8 +- src/services/mongoose/bridge-accounts.ts | 7 +- 12 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 src/graphql/public/types/object/bridge-external-account-link.ts create mode 100644 src/services/bridge/webhook-server/routes/external-account.ts diff --git a/src/app/bridge/send-withdrawal-notification.ts b/src/app/bridge/send-withdrawal-notification.ts index 8adf90f30..cc5c7ae29 100644 --- a/src/app/bridge/send-withdrawal-notification.ts +++ b/src/app/bridge/send-withdrawal-notification.ts @@ -70,7 +70,7 @@ export const sendBridgeWithdrawalNotification = async ({ data: { type: `bridge_withdrawal_${outcome}`, amount, - currency, + currency: currency == "usdt" ? "USD" : currency.toUpperCase(), ...(failureReason ? { failureReason } : {}), }, }) diff --git a/src/config/schema.ts b/src/config/schema.ts index 72f08a6f8..9a62f7163 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -660,8 +660,9 @@ export const configSchema = { kyc: { type: "string" }, deposit: { type: "string" }, transfer: { type: "string" }, + external_account: { type: "string" }, }, - required: ["kyc", "deposit", "transfer"], + required: ["kyc", "deposit", "transfer", "external_account"], }, timestampSkewMs: { type: "integer" }, replaySecret: { type: "string" }, diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index dad0fb31c..bc1819d2d 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -33,6 +33,7 @@ type BridgeWebhookPublicKeys = { kyc: string deposit: string transfer: string + external_account: string } type BridgeWebhook = { diff --git a/src/graphql/public/root/mutation/bridge-add-external-account.ts b/src/graphql/public/root/mutation/bridge-add-external-account.ts index 0c4a2fd19..1c3ccfbe5 100644 --- a/src/graphql/public/root/mutation/bridge-add-external-account.ts +++ b/src/graphql/public/root/mutation/bridge-add-external-account.ts @@ -1,7 +1,7 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import IError from "@graphql/shared/types/abstract/error" -import BridgeExternalAccount from "@graphql/public/types/object/bridge-external-account" +import BridgeExternalAccountLink from "@graphql/public/types/object/bridge-external-account-link" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" @@ -10,7 +10,7 @@ const BridgeAddExternalAccountPayload = GT.Object({ name: "BridgeAddExternalAccountPayload", fields: () => ({ errors: { type: GT.NonNullList(IError) }, - externalAccount: { type: BridgeExternalAccount }, + externalAccount: { type: BridgeExternalAccountLink }, }), }) diff --git a/src/graphql/public/types/object/bridge-external-account-link.ts b/src/graphql/public/types/object/bridge-external-account-link.ts new file mode 100644 index 000000000..5d51c664d --- /dev/null +++ b/src/graphql/public/types/object/bridge-external-account-link.ts @@ -0,0 +1,11 @@ +import { GT } from "@graphql/index" + +const BridgeExternalAccountLink = GT.Object({ + name: "BridgeExternalAccountLink", + fields: () => ({ + linkUrl: { type: GT.NonNull(GT.String) }, + expiresAt: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeExternalAccountLink diff --git a/src/graphql/public/types/object/bridge-external-account.ts b/src/graphql/public/types/object/bridge-external-account.ts index 890733978..92cae18ad 100644 --- a/src/graphql/public/types/object/bridge-external-account.ts +++ b/src/graphql/public/types/object/bridge-external-account.ts @@ -3,7 +3,7 @@ import { GT } from "@graphql/index" const BridgeExternalAccount = GT.Object({ name: "BridgeExternalAccount", fields: () => ({ - id: { type: GT.NonNullID }, + id: { type: GT.NonNullID, resolve: (obj) => obj.bridgeExternalAccountId }, bankName: { type: GT.NonNull(GT.String) }, accountNumberLast4: { type: GT.NonNull(GT.String) }, status: { type: GT.NonNull(GT.String) }, diff --git a/src/services/bridge/webhook-server/index.ts b/src/services/bridge/webhook-server/index.ts index 2cf97522a..e89060179 100644 --- a/src/services/bridge/webhook-server/index.ts +++ b/src/services/bridge/webhook-server/index.ts @@ -14,6 +14,7 @@ import { verifyBridgeSignature } from "./middleware/verify-signature" import { kycHandler } from "./routes/kyc" import { depositHandler } from "./routes/deposit" import { transferHandler } from "./routes/transfer" +import { externalAccountHandler } from "./routes/external-account" import { replayAuthMiddleware, replayHandler } from "./routes/replay" type RawBodyRequest = express.Request & { rawBody?: string } @@ -43,6 +44,7 @@ export const startBridgeWebhookServer = () => { app.post("/kyc", verifyBridgeSignature("kyc"), kycHandler) app.post("/deposit", verifyBridgeSignature("deposit"), depositHandler) app.post("/transfer", verifyBridgeSignature("transfer"), transferHandler) + app.post("/external-account", verifyBridgeSignature("external_account"), externalAccountHandler) app.post("/internal/replay", replayAuthMiddleware, replayHandler) if (!BridgeConfig.webhook.replaySecret && !process.env.BRIDGE_WEBHOOK_REPLAY_SECRET) { diff --git a/src/services/bridge/webhook-server/middleware/verify-signature.ts b/src/services/bridge/webhook-server/middleware/verify-signature.ts index 03b75494f..447ff9aaf 100644 --- a/src/services/bridge/webhook-server/middleware/verify-signature.ts +++ b/src/services/bridge/webhook-server/middleware/verify-signature.ts @@ -14,7 +14,7 @@ import { baseLogger } from "@services/logger" type RawBodyRequest = Request & { rawBody?: string } -export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transfer") => { +export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transfer" | "external_account") => { return (req: Request, res: Response, next: NextFunction) => { const signature = req.headers["x-webhook-signature"] as string diff --git a/src/services/bridge/webhook-server/routes/external-account.ts b/src/services/bridge/webhook-server/routes/external-account.ts new file mode 100644 index 000000000..6618f5a31 --- /dev/null +++ b/src/services/bridge/webhook-server/routes/external-account.ts @@ -0,0 +1,80 @@ +/** + * Bridge External Account Webhook Handler + * Handles external_account.created and external_account.updated events from Bridge.xyz + * + * Fires after a user completes the Plaid bank-linking flow. + * Persists the linked account to MongoDB so it appears in bridgeExternalAccounts queries. + * + * Status mapping: + * active: true → "verified" + * active: false → "failed" + */ + +import { Request, Response } from "express" +import { AccountsRepository } from "@services/mongoose/accounts" +import { LockService } from "@services/lock" +import { baseLogger } from "@services/logger" +import { toBridgeCustomerId } from "@domain/primitives/bridge" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" + +const toStatus = (active: boolean | undefined): "pending" | "verified" | "failed" => { + if (active === true) return "verified" + if (active === false) return "failed" + return "pending" +} + +export const externalAccountHandler = async (req: Request, res: Response) => { + const { event_id, event_object } = req.body + const { id, customer_id, bank_name, last_4, active } = event_object ?? {} + + if (!id || !customer_id || !event_id) { + return res.status(400).json({ error: "Invalid payload" }) + } + + try { + const bridgeCustomerId = toBridgeCustomerId(customer_id) + const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) + if (account instanceof Error) { + baseLogger.warn( + { customer_id, event_id }, + "Account not found for Bridge customer — may be a timing issue, Bridge will retry", + ) + return res.status(503).json({ error: "Account not ready" }) + } + + const lockKey = `bridge-external-account:${event_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) + if (lockResult instanceof Error) { + baseLogger.info({ customer_id, event_id, id }, "Duplicate Bridge external account webhook") + return res.status(200).json({ status: "already_processed" }) + } + + const status = toStatus(active) + + const result = await BridgeAccountsRepo.createExternalAccount({ + accountId: String(account.id), + bridgeExternalAccountId: id, + bankName: bank_name ?? "Unknown", + accountNumberLast4: last_4 ?? "0000", + status, + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, event_id, id, error: result }, + "Failed to persist Bridge external account", + ) + return res.status(500).json({ error: "Failed to persist external account" }) + } + + baseLogger.info( + { accountId: account.id, bridgeExternalAccountId: id, status }, + "Bridge external account persisted", + ) + + return res.status(200).json({ status: "success" }) + } catch (error) { + baseLogger.error({ error, customer_id, event_id }, "Error processing Bridge external account webhook") + return res.status(500).json({ error: "Internal server error" }) + } +} diff --git a/src/services/bridge/webhook-server/routes/replay.ts b/src/services/bridge/webhook-server/routes/replay.ts index 33b499bd9..009414c28 100644 --- a/src/services/bridge/webhook-server/routes/replay.ts +++ b/src/services/bridge/webhook-server/routes/replay.ts @@ -14,14 +14,16 @@ import { } from "../transfer-direction" import { depositHandler } from "./deposit" +import { externalAccountHandler } from "./external-account" import { kycHandler } from "./kyc" import { transferHandler } from "./transfer" -type RouteKey = "kyc" | "deposit" | "transfer" +type RouteKey = "kyc" | "deposit" | "transfer" | "external_account" const HANDLERS: Record Promise> = { kyc: kycHandler, deposit: depositHandler, transfer: transferHandler, + external_account: externalAccountHandler, } const DEPOSIT_EVENT_TYPES = new Set([ @@ -39,6 +41,7 @@ const DEPOSIT_EVENT_TYPES = new Set([ const toRouteKey = (bridgeEventType: string): RouteKey | null => { if (bridgeEventType.startsWith("kyc")) return "kyc" if (bridgeEventType.startsWith("transfer")) return "transfer" + if (bridgeEventType.startsWith("external_account")) return "external_account" if (DEPOSIT_EVENT_TYPES.has(bridgeEventType)) return "deposit" return null } @@ -114,6 +117,7 @@ const toHandlerBody = ({ event_id: eventId, event_object: eventObject, } + } export const replayAuthMiddleware = (req: Request, res: Response, next: () => void) => { diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts index a24a32fdc..6330ad94a 100644 --- a/src/services/bridge/webhook-server/routes/transfer.ts +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -1,6 +1,6 @@ /** * Bridge Transfer Webhook Handler - * Handles transfer.completed and transfer.failed events from Bridge.xyz + * Handles transfer webhook events from Bridge.xyz (transfer.completed, transfer.updated.status_transitioned) */ import { Request, Response } from "express" @@ -65,7 +65,7 @@ export const transferHandler = async (req: Request, res: Response) => { event === "transfer.payment_processed" || state === "payment_processed" - const isFailure = event === "transfer.failed" || TERMINAL_FAILURE_STATES.has(state) + const isFailure = TERMINAL_FAILURE_STATES.has(state) if (!isCompletion && !isFailure) { baseLogger.info({ transfer_id, state, event }, "Bridge transfer event not handled") @@ -118,9 +118,7 @@ export const transferHandler = async (req: Request, res: Response) => { const failureReason = state === "refund_failed" ? (return_reason as string | undefined) - : event === "transfer.failed" - ? (reason as string | undefined) - : ((reason as string | undefined) ?? (return_reason as string | undefined)) + : ((reason as string | undefined) ?? (return_reason as string | undefined)) const result = await BridgeAccountsRepo.updateWithdrawalStatus( bridgeTransferId, diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index c045295be..4afd2c974 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -61,7 +61,12 @@ export const createExternalAccount = async (data: { status?: "pending" | "verified" | "failed" }) => { try { - const record = await BridgeExternalAccount.create(data) + const { bridgeExternalAccountId, accountId, status, ...immutable } = data + const record = await BridgeExternalAccount.findOneAndUpdate( + { bridgeExternalAccountId, accountId }, + { $setOnInsert: { bridgeExternalAccountId, accountId, ...immutable }, $set: { status: status ?? "pending" } }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) return record } catch (error) { return new RepositoryError(String(error)) From f5c9077643f6b2ccd763527375bb6ae4a1cd04aa Mon Sep 17 00:00:00 2001 From: Patoo Date: Wed, 3 Jun 2026 14:33:10 -0400 Subject: [PATCH 21/43] test(bridge): verify webhook signature contract (#383) --- docs/bridge-integration/WEBHOOKS.md | 20 +-- .../webhook-server/verify-signature.spec.ts | 126 ++++++++++++++++++ 2 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 test/flash/unit/services/bridge/webhook-server/verify-signature.spec.ts diff --git a/docs/bridge-integration/WEBHOOKS.md b/docs/bridge-integration/WEBHOOKS.md index bd129d808..404d64c8d 100644 --- a/docs/bridge-integration/WEBHOOKS.md +++ b/docs/bridge-integration/WEBHOOKS.md @@ -4,11 +4,12 @@ Flash receives real-time updates from Bridge.xyz via webhooks. These webhooks ar ## Webhook Endpoint -The webhook server listens on the configured port (default: `3005`) and expects POST requests at the following endpoints: +The webhook server listens on the configured port (default: `4009`) and expects POST requests at the following endpoints: -- `POST /bridge/webhooks/kyc` -- `POST /bridge/webhooks/deposit` -- `POST /bridge/webhooks/transfer` +- `POST /kyc` +- `POST /deposit` +- `POST /transfer` +- `POST /external-account` ## Signature Verification @@ -16,11 +17,14 @@ All incoming webhooks from Bridge.xyz are signed using asymmetric RSA-SHA256. Fl ### Verification Process -1. Retrieve the signature from the `Bridge-Signature` header. -2. Retrieve the timestamp from the `Bridge-Timestamp` header. +1. Retrieve the signature header from `X-Webhook-Signature`. +2. Parse the timestamp and signature from the header format: `t=,v0=`. 3. Verify that the timestamp is within the allowed skew (default: 5 minutes) to prevent replay attacks. -4. Construct the signed payload by concatenating the timestamp and the raw request body: `timestamp + "." + rawBody`. -5. Verify the signature against the signed payload using the appropriate public key (KYC, Deposit, or Transfer). +4. Construct the signed payload by concatenating the timestamp and the exact raw request body: `timestamp + "." + rawBody`. +5. Hash the signed payload with SHA-256. +6. Verify the Base64 `v0` signature against that digest using RSA-SHA256 and the appropriate Bridge public key (KYC, Deposit, Transfer, or External Account). + +Flash must verify against the raw body captured before JSON parsing. Re-serializing the parsed JSON body changes the signed bytes and must fail signature verification. ## Event Types diff --git a/test/flash/unit/services/bridge/webhook-server/verify-signature.spec.ts b/test/flash/unit/services/bridge/webhook-server/verify-signature.spec.ts new file mode 100644 index 000000000..f51c748f4 --- /dev/null +++ b/test/flash/unit/services/bridge/webhook-server/verify-signature.spec.ts @@ -0,0 +1,126 @@ +jest.mock("@config", () => ({ + BridgeConfig: { + webhook: { + publicKeys: { + kyc: "", + deposit: "", + transfer: "", + external_account: "", + }, + timestampSkewMs: 5 * 60 * 1000, + }, + }, +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { + debug: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + }, +})) + +import crypto from "crypto" + +import { NextFunction, Request, Response } from "express" + +import { verifyBridgeSignature } from "@services/bridge/webhook-server/middleware/verify-signature" + +type RawBodyRequest = Request & { rawBody?: string } + +const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", { + modulusLength: 2048, +}) + +const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString() + +const RAW_BODY = JSON.stringify({ + api_version: "v0", + event_id: "wh_signature_fixture", + event_category: "customer", + event_type: "kyc.approved", + event_object_id: "cust_signature_fixture", +}) + +const makeRes = () => { + const res = { status: jest.fn(), json: jest.fn() } as unknown as Response + ;(res.status as jest.Mock).mockReturnValue(res) + ;(res.json as jest.Mock).mockReturnValue(res) + return res +} + +const makeReq = (signature: string, rawBody = RAW_BODY) => + ({ + headers: { "x-webhook-signature": signature }, + rawBody, + }) as RawBodyRequest + +const signBridgeDigestFixture = (timestamp: string, rawBody = RAW_BODY) => { + const signedPayload = `${timestamp}.${rawBody}` + const digest = crypto.createHash("sha256").update(signedPayload).digest() + const signer = crypto.createSign("RSA-SHA256") + signer.update(digest) + return signer.sign(privateKey, "base64") +} + +const signRawPayloadDirectly = (timestamp: string, rawBody = RAW_BODY) => { + const signer = crypto.createSign("RSA-SHA256") + signer.update(`${timestamp}.${rawBody}`) + return signer.sign(privateKey, "base64") +} + +const signatureHeader = (timestamp: string, signature: string) => + `t=${timestamp},v0=${signature}` + +describe("verifyBridgeSignature", () => { + beforeAll(() => { + const { BridgeConfig } = jest.requireMock("@config") + BridgeConfig.webhook.publicKeys.kyc = publicKeyPem + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + it("accepts the Bridge documented digest signature over timestamp and exact raw body", () => { + const timestamp = Date.now().toString() + const signature = signBridgeDigestFixture(timestamp) + const req = makeReq(signatureHeader(timestamp, signature)) + const res = makeRes() + const next = jest.fn() as NextFunction + + verifyBridgeSignature("kyc")(req, res, next) + + expect(next).toHaveBeenCalledTimes(1) + expect(res.status as jest.Mock).not.toHaveBeenCalled() + }) + + it("rejects signatures created over the raw timestamp/body payload directly", () => { + const timestamp = Date.now().toString() + const signature = signRawPayloadDirectly(timestamp) + const req = makeReq(signatureHeader(timestamp, signature)) + const res = makeRes() + const next = jest.fn() as NextFunction + + verifyBridgeSignature("kyc")(req, res, next) + + expect(next).not.toHaveBeenCalled() + expect(res.status as jest.Mock).toHaveBeenCalledWith(401) + expect(res.json as jest.Mock).toHaveBeenCalledWith({ error: "Invalid signature" }) + }) + + it("rejects digest signatures generated from a reserialized body instead of the captured raw body", () => { + const timestamp = Date.now().toString() + const reserializedBody = JSON.stringify(JSON.parse(RAW_BODY), null, 2) + const signature = signBridgeDigestFixture(timestamp, reserializedBody) + const req = makeReq(signatureHeader(timestamp, signature), RAW_BODY) + const res = makeRes() + const next = jest.fn() as NextFunction + + verifyBridgeSignature("kyc")(req, res, next) + + expect(next).not.toHaveBeenCalled() + expect(res.status as jest.Mock).toHaveBeenCalledWith(401) + expect(res.json as jest.Mock).toHaveBeenCalledWith({ error: "Invalid signature" }) + }) +}) From 851c1ca6018d4100ffece9a057c361d8161a40f9 Mon Sep 17 00:00:00 2001 From: Patoo Date: Wed, 3 Jun 2026 16:06:01 -0400 Subject: [PATCH 22/43] fix(cash-wallet): guard cutover start Require a prepared runnable migration row before Cash Wallet cutover start can move config to in_progress. --- src/app/cash-wallet-cutover/lifecycle.ts | 13 ++ .../app/cash-wallet-cutover/lifecycle.spec.ts | 139 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 test/flash/unit/app/cash-wallet-cutover/lifecycle.spec.ts diff --git a/src/app/cash-wallet-cutover/lifecycle.ts b/src/app/cash-wallet-cutover/lifecycle.ts index 80e4fd848..aa624a614 100644 --- a/src/app/cash-wallet-cutover/lifecycle.ts +++ b/src/app/cash-wallet-cutover/lifecycle.ts @@ -2,6 +2,7 @@ import { CashWalletCutoverRepository } from "@services/mongoose" import { CashWalletCutoverInProgressError, + CashWalletCutoverPreflightError, CashWalletMigrationFailedError, InvalidCashWalletCutoverStateTransitionError, } from "./errors" @@ -88,6 +89,18 @@ export const startPrimaryCashWalletCutover = async ({ ) } + const runnable = await migrationsRepo.listRunnableMigrations({ + cutoverVersion, + runId, + limit: 1, + }) + if (runnable instanceof Error) return runnable + if (runnable.length === 0) { + return new CashWalletCutoverPreflightError( + `Cash wallet cutover has no runnable migrations for runId=${runId} cutoverVersion=${cutoverVersion}. Run 'prepare' before starting.`, + ) + } + return migrationsRepo.updateConfig( { state: "in_progress", diff --git a/test/flash/unit/app/cash-wallet-cutover/lifecycle.spec.ts b/test/flash/unit/app/cash-wallet-cutover/lifecycle.spec.ts new file mode 100644 index 000000000..223313e45 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/lifecycle.spec.ts @@ -0,0 +1,139 @@ +jest.mock("@services/mongoose", () => ({ + CashWalletCutoverRepository: jest.fn(), +})) + +import { startPrimaryCashWalletCutover } from "@app/cash-wallet-cutover/lifecycle" +import { CashWalletCutoverPreflightError } from "@app/cash-wallet-cutover/errors" +import { UnknownRepositoryError } from "@domain/errors" + +const cutoverVersion = 7 +const runId = "cash-wallet-cutover-2026-06-03" +const actor = "operator@example.com" +const now = new Date("2026-06-03T18:00:00.000Z") + +const preConfig = { + state: "pre", + cutoverVersion: 6, + updatedAt: new Date("2026-06-03T17:00:00.000Z"), +} as CashWalletCutoverConfig + +const startedConfig = { + ...preConfig, + state: "in_progress", + cutoverVersion, + runId, + startedAt: now, +} as CashWalletCutoverConfig + +const runnableMigration = { + id: "migration-id", + accountId: "account-id" as AccountId, + legacyUsdWalletId: "11111111-1111-4111-8111-111111111111" as WalletId, + destinationUsdtWalletId: "22222222-2222-4222-8222-222222222222" as WalletId, + cutoverVersion, + runId, + status: "not_started", + idempotencyKey: `${runId}:account-id`, + attempts: 0, + updatedAt: now, +} as CashWalletMigration + +const makeRepo = ({ + config = preConfig, + runnable = [runnableMigration], + updateResult = startedConfig, +}: { + config?: CashWalletCutoverConfig | RepositoryError + runnable?: CashWalletMigration[] | RepositoryError + updateResult?: CashWalletCutoverConfig | RepositoryError +} = {}) => ({ + getConfig: jest.fn().mockResolvedValue(config), + updateConfig: jest.fn().mockResolvedValue(updateResult), + listRunnableMigrations: jest.fn().mockResolvedValue(runnable), + countByStatus: jest.fn(), +}) + +describe("startPrimaryCashWalletCutover", () => { + it("rejects start when the requested run has no prepared runnable migrations", async () => { + const repo = makeRepo({ runnable: [] }) + + const result = await startPrimaryCashWalletCutover({ + cutoverVersion, + runId, + actor, + now, + migrationsRepo: repo, + }) + + expect(result).toBeInstanceOf(CashWalletCutoverPreflightError) + expect(repo.listRunnableMigrations).toHaveBeenCalledWith({ + cutoverVersion, + runId, + limit: 1, + }) + expect(repo.updateConfig).not.toHaveBeenCalled() + }) + + it("propagates prepared-run repository errors before updating config", async () => { + const repoError = new UnknownRepositoryError("list runnable migrations failed") + const repo = makeRepo({ runnable: repoError }) + + const result = await startPrimaryCashWalletCutover({ + cutoverVersion, + runId, + actor, + now, + migrationsRepo: repo, + }) + + expect(result).toBe(repoError) + expect(repo.updateConfig).not.toHaveBeenCalled() + }) + + it("starts when the requested run has a prepared runnable migration", async () => { + const repo = makeRepo() + + const result = await startPrimaryCashWalletCutover({ + cutoverVersion, + runId, + actor, + now, + migrationsRepo: repo, + }) + + expect(result).toBe(startedConfig) + expect(repo.listRunnableMigrations).toHaveBeenCalledWith({ + cutoverVersion, + runId, + limit: 1, + }) + expect(repo.updateConfig).toHaveBeenCalledWith( + { + state: "in_progress", + cutoverVersion, + runId, + startedAt: now, + scheduledAt: undefined, + pausedAt: undefined, + pauseReason: undefined, + }, + actor, + ) + }) + + it("keeps same-run in-progress start idempotent without rechecking migrations", async () => { + const repo = makeRepo({ config: startedConfig, runnable: [] }) + + const result = await startPrimaryCashWalletCutover({ + cutoverVersion, + runId, + actor, + now, + migrationsRepo: repo, + }) + + expect(result).toBe(startedConfig) + expect(repo.listRunnableMigrations).not.toHaveBeenCalled() + expect(repo.updateConfig).not.toHaveBeenCalled() + }) +}) From f1d5f1070d467d21a26a15e361381f17f45c8184 Mon Sep 17 00:00:00 2001 From: Patoo Date: Thu, 4 Jun 2026 04:38:31 -0400 Subject: [PATCH 23/43] feat: add BridgeTransferRequest ERPNext audit model and webhook wiring (#386) * feat: add BridgeTransferRequest ERPNext audit model and webhook wiring Adds a full Bridge Transfer Request audit record system that writes ERPNext DocType rows from Bridge deposit, IBEX crypto receive, and Bridge transfer webhook events. - BridgeTransferRequest model with toErpnext() serialization - BridgeTransferRequestWriter with 4 event handlers - ErpNext.upsertBridgeTransferRequest with idempotent upsert - Webhook wiring in deposit, transfer, and crypto-receive routes - Bridge deposit log made idempotent by eventId * fix: normalize IBEX network casing for ERPNext consistency The IBEX crypto receive route lowercases the network field for Mongo storage, but was passing the lowercased value to the BridgeTransferRequest model. This created inconsistent casing in ERPNext records: deposits used 'Ethereum' (model default) while IBEX receives used 'ethereum'. Fix: capitalize the normalized network before passing to the writer. The lowercase value is still stored in the IBEX log and raw payload. --- .../bridge/webhook-server/routes/deposit.ts | 30 ++- .../bridge/webhook-server/routes/transfer.ts | 61 ++++-- .../frappe/BridgeTransferRequestWriter.ts | 179 ++++++++++++++++++ src/services/frappe/ErpNext.ts | 91 ++++++++- src/services/frappe/errors.ts | 1 + .../frappe/models/BridgeTransferRequest.ts | 85 +++++++++ .../webhook-server/routes/crypto-receive.ts | 25 +++ src/services/mongoose/bridge-deposit-log.ts | 6 +- .../bridge/webhook-server/deposit.spec.ts | 90 ++++++--- .../bridge/webhook-server/transfer.spec.ts | 101 +++++++++- .../BridgeTransferRequestWriter.spec.ts | 114 +++++++++++ .../unit/services/frappe/ErpNext.spec.ts | 81 ++++++++ .../models/BridgeTransferRequest.spec.ts | 65 +++++++ .../routes/crypto-receive.spec.ts | 74 +++++++- 14 files changed, 946 insertions(+), 57 deletions(-) create mode 100644 src/services/frappe/BridgeTransferRequestWriter.ts create mode 100644 src/services/frappe/models/BridgeTransferRequest.ts create mode 100644 test/flash/unit/services/frappe/BridgeTransferRequestWriter.spec.ts create mode 100644 test/flash/unit/services/frappe/ErpNext.spec.ts create mode 100644 test/flash/unit/services/frappe/models/BridgeTransferRequest.spec.ts diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index c5d02b4a1..1105ac2be 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -11,6 +11,7 @@ import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" import { createBridgeDeposit } from "@services/mongoose/bridge-deposit-log" import { reconcileByTxHash } from "@services/bridge/reconciliation" +import { writeBridgeDepositRequest } from "@services/frappe/BridgeTransferRequestWriter" export const depositHandler = async (req: Request, res: Response) => { const { event_id, event_object } = req.body @@ -70,8 +71,27 @@ export const depositHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to persist deposit log" }) } - // Idempotency: lock on event_id after successful DB write — aligns with the DB unique - // constraint on eventId and ensures Bridge retries are not blocked on transient failures + if (state === "payment_processed" && receipt?.destination_tx_hash) { + reconcileByTxHash({ txHash: receipt.destination_tx_hash }).catch((err) => + baseLogger.error({ err, event_id, id }, "Real-time reconciliation failed"), + ) + } + + const auditResult = await writeBridgeDepositRequest({ + eventId: event_id, + eventObject: event_object, + rawPayload: req.body, + }) + if (auditResult instanceof Error) { + baseLogger.error( + { error: auditResult, event_id, id }, + "Failed to persist Bridge deposit ERPNext audit row", + ) + return res.status(500).json({ error: "Failed to persist ERPNext audit row" }) + } + + // Idempotency: mark processed only after local and ERPNext writes succeed, so + // provider retries can recover audit gaps after transient ERPNext failures. const lockKey = `bridge-deposit:${event_id}` const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) if (lockResult instanceof Error) { @@ -79,12 +99,6 @@ export const depositHandler = async (req: Request, res: Response) => { return res.status(200).json({ status: "already_processed" }) } - if (state === "payment_processed" && receipt?.destination_tx_hash) { - reconcileByTxHash({ txHash: receipt.destination_tx_hash }).catch((err) => - baseLogger.error({ err, event_id, id }, "Real-time reconciliation failed"), - ) - } - return res.status(200).json({ status: "success" }) } catch (error) { baseLogger.error({ error, id, event_id }, "Error processing Bridge deposit webhook") diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts index 6330ad94a..88f00fad8 100644 --- a/src/services/bridge/webhook-server/routes/transfer.ts +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -9,6 +9,10 @@ import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" import { toBridgeTransferId } from "@domain/primitives/bridge" +import { + writeBridgeCashoutCompleted, + writeBridgeCashoutFailed, +} from "@services/frappe/BridgeTransferRequestWriter" const TERMINAL_FAILURE_STATES = new Set([ "undeliverable", @@ -42,7 +46,7 @@ const markProcessed = async ( } export const transferHandler = async (req: Request, res: Response) => { - const { event, data } = req.body + const { event, event_id, data } = req.body const { transfer_id, state, amount, currency, reason, return_reason } = data if (!transfer_id || !event) { @@ -93,11 +97,6 @@ export const transferHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to update status" }) } - const lockStatus = await markProcessed(transfer_id, event, state) - if (lockStatus === "already_processed") { - return res.status(200).json({ status: "already_processed" }) - } - baseLogger.info( { transfer_id, @@ -108,6 +107,28 @@ export const transferHandler = async (req: Request, res: Response) => { "Bridge transfer completed", ) + const auditResult = await writeBridgeCashoutCompleted({ + transferId: transfer_id, + amount: String(result.amount ?? amount ?? ""), + currency: String(result.currency ?? currency ?? ""), + accountId: result.accountId, + sourceEventId: event_id, + sourceEventType: event, + rawPayload: req.body, + }) + if (auditResult instanceof Error) { + baseLogger.error( + { transfer_id, error: auditResult }, + "Failed to persist Bridge transfer ERPNext audit row", + ) + return res.status(500).json({ error: "Failed to persist ERPNext audit row" }) + } + + const lockStatus = await markProcessed(transfer_id, event, state) + if (lockStatus === "already_processed") { + return res.status(200).json({ status: "already_processed" }) + } + await sendBridgeWithdrawalNotificationBestEffort({ accountId: result.accountId, amount: result.amount, @@ -148,11 +169,6 @@ export const transferHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to update status" }) } - const lockStatus = await markProcessed(transfer_id, event, state) - if (lockStatus === "already_processed") { - return res.status(200).json({ status: "already_processed" }) - } - baseLogger.warn( { transfer_id, @@ -165,6 +181,29 @@ export const transferHandler = async (req: Request, res: Response) => { "Bridge transfer failed", ) + const auditResult = await writeBridgeCashoutFailed({ + transferId: transfer_id, + amount: String(result.amount ?? amount ?? ""), + currency: String(result.currency ?? currency ?? ""), + accountId: result.accountId, + sourceEventId: event_id, + sourceEventType: event, + failureReason: result.failureReason ?? failureReason, + rawPayload: req.body, + }) + if (auditResult instanceof Error) { + baseLogger.error( + { transfer_id, error: auditResult }, + "Failed to persist Bridge transfer failure ERPNext audit row", + ) + return res.status(500).json({ error: "Failed to persist ERPNext audit row" }) + } + + const lockStatus = await markProcessed(transfer_id, event, state) + if (lockStatus === "already_processed") { + return res.status(200).json({ status: "already_processed" }) + } + await sendBridgeWithdrawalNotificationBestEffort({ accountId: result.accountId, amount: result.amount, diff --git a/src/services/frappe/BridgeTransferRequestWriter.ts b/src/services/frappe/BridgeTransferRequestWriter.ts new file mode 100644 index 000000000..a61a6af2f --- /dev/null +++ b/src/services/frappe/BridgeTransferRequestWriter.ts @@ -0,0 +1,179 @@ +import ErpNext from "@services/frappe/ErpNext" +import { BridgeTransferRequestUpsertError } from "@services/frappe/errors" + +import { + BridgeTransferRequest, + BridgeTransferRequestStatus, + BridgeTransferRequestTransactionType, +} from "./models/BridgeTransferRequest" + +type BridgeDepositEventObject = { + id: string + state?: string + amount: string + currency: string + on_behalf_of: string + receipt?: { + developer_fee?: unknown + initial_amount?: unknown + subtotal_amount?: unknown + final_amount?: unknown + destination_tx_hash?: string + } + developer_fee?: unknown +} + +const asOptionalString = (value: unknown): string | undefined => { + if (value === undefined || value === null) return undefined + return String(value) +} + +const upsert = async ( + request: BridgeTransferRequest, +): Promise => { + if (!ErpNext?.upsertBridgeTransferRequest) { + return new BridgeTransferRequestUpsertError("ERPNext client is not configured") + } + return ErpNext.upsertBridgeTransferRequest(request) +} + +export const writeBridgeDepositRequest = async ({ + eventId, + eventObject, + rawPayload, +}: { + eventId: string + eventObject: BridgeDepositEventObject + rawPayload: unknown +}): Promise => { + const receipt = eventObject.receipt + + return upsert( + new BridgeTransferRequest({ + requestId: eventObject.id, + transactionType: BridgeTransferRequestTransactionType.Topup, + status: BridgeTransferRequestStatus.FiatReceived, + amount: String(eventObject.amount), + currency: String(eventObject.currency), + developerFee: + asOptionalString(receipt?.developer_fee) ?? + asOptionalString(eventObject.developer_fee) ?? + "0", + initialAmount: asOptionalString(receipt?.initial_amount), + subtotalAmount: asOptionalString(receipt?.subtotal_amount), + finalAmount: asOptionalString(receipt?.final_amount), + bridgeCustomerId: eventObject.on_behalf_of, + bridgeTransferId: eventObject.id, + ibexTxHash: receipt?.destination_tx_hash, + sourceEventId: eventId, + sourceEventType: `deposit.${eventObject.state ?? "unknown"}`, + sourceSystemsSeen: ["bridge_deposit"], + rawPayload, + }), + ) +} + +export const writeIbexCryptoReceiveRequest = async ({ + txHash, + address, + amount, + currency, + network, + accountId, + walletId, + rawPayload, +}: { + txHash: string + address: string + amount: string + currency: string + network: string + accountId: AccountId + walletId: WalletId + rawPayload: unknown +}): Promise => { + return upsert( + new BridgeTransferRequest({ + requestId: `ibex:${txHash}`, + transactionType: BridgeTransferRequestTransactionType.Topup, + status: BridgeTransferRequestStatus.Settled, + amount: String(amount), + currency: String(currency), + network, + accountId, + walletId, + ibexTxHash: txHash, + address, + sourceEventId: txHash, + sourceEventType: "crypto.receive", + sourceSystemsSeen: ["ibex_crypto_receive"], + rawPayload, + }), + ) +} + +type BridgeCashoutWriteInput = { + transferId: string + amount: string + currency: string + accountId?: AccountId | string + sourceEventId?: string + sourceEventType: string + rawPayload: unknown +} + +export const writeBridgeCashoutCompleted = async ({ + transferId, + amount, + currency, + accountId, + sourceEventId, + sourceEventType, + rawPayload, +}: BridgeCashoutWriteInput): Promise => { + return upsert( + new BridgeTransferRequest({ + requestId: transferId, + transactionType: BridgeTransferRequestTransactionType.Cashout, + status: BridgeTransferRequestStatus.Completed, + amount: String(amount), + currency: String(currency), + accountId, + bridgeTransferId: transferId, + sourceEventId, + sourceEventType, + sourceSystemsSeen: ["bridge_transfer"], + rawPayload, + }), + ) +} + +export const writeBridgeCashoutFailed = async ({ + transferId, + amount, + currency, + accountId, + sourceEventId, + sourceEventType, + failureReason, + rawPayload, +}: BridgeCashoutWriteInput & { + failureReason?: string +}): Promise => { + return upsert( + new BridgeTransferRequest({ + requestId: transferId, + transactionType: BridgeTransferRequestTransactionType.Cashout, + status: BridgeTransferRequestStatus.Failed, + amount: String(amount), + currency: String(currency), + accountId, + bridgeTransferId: transferId, + sourceEventId, + sourceEventType, + sourceSystemsSeen: ["bridge_transfer"], + failureReason, + rawPayload, + }), + ) +} diff --git a/src/services/frappe/ErpNext.ts b/src/services/frappe/ErpNext.ts index 8c72f16c8..e0d42cb05 100644 --- a/src/services/frappe/ErpNext.ts +++ b/src/services/frappe/ErpNext.ts @@ -14,6 +14,7 @@ import { BanksQueryError, BankAccountQueryError, SetDocTypeValueError, + BridgeTransferRequestUpsertError, } from "./errors" import { AccountUpgradeRequest, @@ -21,6 +22,7 @@ import { } from "./models/AccountUpgradeRequest" import { Bank } from "./models/Bank" import { BankAccount } from "./models/BankAccount" +import { BridgeTransferRequest } from "./models/BridgeTransferRequest" import { Filter } from "./SearchFilters" export type AccountUpgradeRequestFilters = { username?: Filter, status?: Filter } @@ -37,7 +39,7 @@ const erpUsd = (usd: USDAmount): number => Number(usd.asCents(2)) export type CashoutId = string & { readonly brand: unique symbol } -class ErpNext { +export class ErpNext { url: string headers: Record @@ -240,6 +242,93 @@ class ErpNext { return new BanksQueryError(err) } } + + async upsertBridgeTransferRequest( + request: BridgeTransferRequest, + ): Promise { + const payload = request.toErpnext() + const requestId = payload.request_id + + try { + const existingName = await this.findBridgeTransferRequestName(requestId) + if (existingName instanceof BridgeTransferRequestUpsertError) return existingName + + if (existingName) { + await axios.put( + `${this.url}/api/resource/${encodeURIComponent(BridgeTransferRequest.doctype)}/${encodeURIComponent(existingName)}`, + payload, + { headers: this.headers }, + ) + return true + } + + try { + await axios.post( + `${this.url}/api/resource/${BridgeTransferRequest.doctype}`, + payload, + { headers: this.headers }, + ) + return true + } catch (err) { + if (!this.isDuplicateRequestError(err)) throw err + + const racedName = await this.findBridgeTransferRequestName(requestId) + if (racedName instanceof BridgeTransferRequestUpsertError) return racedName + if (!racedName) throw err + + await axios.put( + `${this.url}/api/resource/${encodeURIComponent(BridgeTransferRequest.doctype)}/${encodeURIComponent(racedName)}`, + payload, + { headers: this.headers }, + ) + return true + } + } catch (err) { + const responseData = isAxiosError(err) ? err.response?.data : undefined + baseLogger.error( + { err, responseData, requestId }, + "Error upserting Bridge Transfer Request in ERPNext", + ) + recordExceptionInCurrentSpan({ error: err, attributes: { "erpnext.exception": responseData?.exception } }) + return new BridgeTransferRequestUpsertError(err) + } + } + + private async findBridgeTransferRequestName( + requestId: string, + ): Promise { + try { + const filters = JSON.stringify([ + [BridgeTransferRequest.doctype, "request_id", "=", requestId], + ]) + const fields = JSON.stringify(["name"]) + const resp = await axios.get( + `${this.url}/api/resource/${encodeURIComponent(BridgeTransferRequest.doctype)}`, + { + params: { filters, fields, limit_page_length: 1 }, + headers: this.headers, + }, + ) + + return resp.data?.data?.[0]?.name + } catch (err) { + const responseData = isAxiosError(err) ? err.response?.data : undefined + baseLogger.error( + { err, responseData, requestId }, + "Error querying Bridge Transfer Request from ERPNext", + ) + recordExceptionInCurrentSpan({ error: err, attributes: { "erpnext.exception": responseData?.exception } }) + return new BridgeTransferRequestUpsertError(err) + } + } + + private isDuplicateRequestError(err: unknown): boolean { + if (!isAxiosError(err)) return false + const status = err.response?.status + const responseData = err.response?.data + const message = JSON.stringify(responseData ?? err.message).toLowerCase() + return status === 409 || message.includes("duplicate") || message.includes("unique") + } } // Only instantiate if config is available, otherwise export a null-safe placeholder diff --git a/src/services/frappe/errors.ts b/src/services/frappe/errors.ts index 0bdb3bb89..a64833018 100644 --- a/src/services/frappe/errors.ts +++ b/src/services/frappe/errors.ts @@ -9,3 +9,4 @@ export class UpgradeRequestQueryError extends ErpNextError {} export class SetDocTypeValueError extends ErpNextError {} export class BanksQueryError extends ErpNextError {} export class BankAccountQueryError extends ErpNextError {} +export class BridgeTransferRequestUpsertError extends ErpNextError {} diff --git a/src/services/frappe/models/BridgeTransferRequest.ts b/src/services/frappe/models/BridgeTransferRequest.ts new file mode 100644 index 000000000..9781f384b --- /dev/null +++ b/src/services/frappe/models/BridgeTransferRequest.ts @@ -0,0 +1,85 @@ +export enum BridgeTransferRequestTransactionType { + Topup = "Topup", + Cashout = "Cashout", +} + +export enum BridgeTransferRequestStatus { + Pending = "Pending", + FiatReceived = "Fiat Received", + Settled = "Settled", + Completed = "Completed", + Failed = "Failed", +} + +export type BridgeTransferRequestInput = { + requestId: string + transactionType: BridgeTransferRequestTransactionType + status: BridgeTransferRequestStatus + amount: string + currency: string + provider?: "Bridge" + asset?: string + network?: string + developerFee?: string + initialAmount?: string + subtotalAmount?: string + finalAmount?: string + accountId?: AccountId | string + walletId?: WalletId | string + bridgeCustomerId?: string + bridgeTransferId?: string + ibexTxHash?: string + address?: string + sourceEventId?: string + sourceEventType?: string + sourceSystemsSeen?: string[] + firstSeenAt?: string + lastSeenAt?: string + rawPayload?: unknown + failureReason?: string +} + +export class BridgeTransferRequest { + static doctype = "Bridge Transfer Request" + readonly input: BridgeTransferRequestInput + + constructor(input: BridgeTransferRequestInput) { + this.input = input + } + + toErpnext() { + const sourceSystemsSeen = [...new Set(this.input.sourceSystemsSeen ?? [])].join(",") + + return { + doctype: BridgeTransferRequest.doctype, + request_id: this.input.requestId, + transaction_type: this.input.transactionType, + status: this.input.status, + provider: this.input.provider ?? "Bridge", + asset: this.input.asset ?? "USDT", + network: this.input.network ?? "Ethereum", + amount: this.input.amount, + currency: this.input.currency, + developer_fee: this.input.developerFee, + initial_amount: this.input.initialAmount, + subtotal_amount: this.input.subtotalAmount, + final_amount: this.input.finalAmount, + account_id: this.input.accountId, + wallet_id: this.input.walletId, + bridge_customer_id: this.input.bridgeCustomerId, + bridge_transfer_id: this.input.bridgeTransferId, + ibex_tx_hash: this.input.ibexTxHash, + address: this.input.address, + source_event_id: this.input.sourceEventId, + source_event_type: this.input.sourceEventType, + source_systems_seen: sourceSystemsSeen || undefined, + first_seen_at: this.input.firstSeenAt, + last_seen_at: this.input.lastSeenAt ?? new Date().toISOString(), + raw_payload_json: + this.input.rawPayload === undefined + ? undefined + : JSON.stringify(this.input.rawPayload), + failure_reason: this.input.failureReason, + } + } +} diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index d03e72676..cb5e46666 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -6,6 +6,7 @@ import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" import { LockService } from "@services/lock" import { reconcileByTxHash } from "@services/bridge/reconciliation" +import { writeIbexCryptoReceiveRequest } from "@services/frappe/BridgeTransferRequestWriter" import { authenticate, logRequest } from "../middleware" @@ -101,6 +102,29 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { "USDT deposit received", ) + const auditResult = await writeIbexCryptoReceiveRequest({ + txHash: String(tx_hash), + address: String(address), + amount: String(amount), + currency: normalizedCurrency, + network: normalizedNetwork.charAt(0).toUpperCase() + normalizedNetwork.slice(1), + accountId: account.id, + walletId: usdtWallet.id, + rawPayload: req.body, + }) + if (auditResult instanceof Error) { + baseLogger.error( + { + error: auditResult, + tx_hash, + accountId: account.id, + walletId: usdtWallet.id, + }, + "Failed to persist IBEX crypto receive ERPNext audit row", + ) + return { status: "error", code: "erpnext_audit_failed" } as CryptoReceiveResult + } + return { status: "success" } as CryptoReceiveResult } catch (error) { baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") @@ -126,6 +150,7 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { wallet_list_failed: 500, usdt_wallet_not_found: 404, invalid_amount: 400, + erpnext_audit_failed: 500, internal_error: 500, } diff --git a/src/services/mongoose/bridge-deposit-log.ts b/src/services/mongoose/bridge-deposit-log.ts index 953cd8a3d..316fe2aac 100644 --- a/src/services/mongoose/bridge-deposit-log.ts +++ b/src/services/mongoose/bridge-deposit-log.ts @@ -14,7 +14,11 @@ export const createBridgeDeposit = async (data: { destinationTxHash?: string }): Promise<{ id: string } | Error> => { try { - const log = await BridgeDeposits.create(data) + const log = await BridgeDeposits.findOneAndUpdate( + { eventId: data.eventId }, + { $setOnInsert: data }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) return { id: log._id.toString() } } catch (error) { return error instanceof Error ? error : new Error(String(error)) diff --git a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts index 961b1f24c..44788f076 100644 --- a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts @@ -9,12 +9,21 @@ jest.mock("@services/logger", () => ({ })) jest.mock("@services/mongoose/bridge-deposit-log", () => ({ - createBridgeDepositLog: jest.fn(), + createBridgeDeposit: jest.fn(), +})) + +jest.mock("@services/bridge/reconciliation", () => ({ + reconcileByTxHash: jest.fn().mockResolvedValue({ status: "matched" }), +})) + +jest.mock("@services/frappe/BridgeTransferRequestWriter", () => ({ + writeBridgeDepositRequest: jest.fn(), })) import { Request, Response } from "express" import { LockService } from "@services/lock" import * as DepositLog from "@services/mongoose/bridge-deposit-log" +import { writeBridgeDepositRequest } from "@services/frappe/BridgeTransferRequestWriter" import { depositHandler } from "@services/bridge/webhook-server/routes/deposit" // ── Helpers ─────────────────────────────────────────────────────────────────── @@ -77,6 +86,7 @@ const mockLockService = (acquired = true) => { beforeEach(() => { jest.clearAllMocks() mockLockService(true) + ;(writeBridgeDepositRequest as jest.Mock).mockResolvedValue(true) }) // ── Invalid payload ─────────────────────────────────────────────────────────── @@ -88,7 +98,7 @@ describe("depositHandler — invalid payload", () => { delete body.event_id await depositHandler(makeReq(body), res) expect(res.status as jest.Mock).toHaveBeenCalledWith(400) - expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + expect(DepositLog.createBridgeDeposit).not.toHaveBeenCalled() }) it("returns 400 when event_object.id is missing", async () => { @@ -97,42 +107,33 @@ describe("depositHandler — invalid payload", () => { delete objWithoutId.id await depositHandler(makeReq({ ...VALID_BODY, event_object: objWithoutId }), res) expect(res.status as jest.Mock).toHaveBeenCalledWith(400) - expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + expect(DepositLog.createBridgeDeposit).not.toHaveBeenCalled() }) }) // ── Idempotency ─────────────────────────────────────────────────────────────── describe("depositHandler — idempotency", () => { - it("returns already_processed without persisting on duplicate state transition", async () => { + it("returns already_processed after idempotent local and audit writes on duplicate delivery", async () => { mockLockService(false) + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ id: "log-dup" }) const res = makeRes() await depositHandler(makeReq(VALID_BODY), res) expect(res.json as jest.Mock).toHaveBeenCalledWith({ status: "already_processed" }) - expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + expect(DepositLog.createBridgeDeposit).toHaveBeenCalledTimes(1) + expect(writeBridgeDepositRequest).toHaveBeenCalledTimes(1) }) - it("locks on transfer id + state so different states for same transfer are processed", async () => { - ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ id: "log-1" }) + it("locks on the event id after writing the audit row", async () => { + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ id: "log-1" }) const lockFn = jest.fn().mockResolvedValue({}) ;(LockService as jest.Mock).mockReturnValue({ lockIdempotencyKey: lockFn }) await depositHandler(makeReq(VALID_BODY), makeRes()) - expect(lockFn).toHaveBeenCalledWith("bridge-deposit:tr_xyz789abc123:funds_received") - - await depositHandler( - makeReq({ - ...VALID_BODY, - event_object: { ...VALID_EVENT_OBJECT, state: "payment_processed" }, - }), - makeRes(), - ) - expect(lockFn).toHaveBeenCalledWith( - "bridge-deposit:tr_xyz789abc123:payment_processed", - ) + expect(lockFn).toHaveBeenCalledWith("bridge-deposit:wh_789xyz654mno") }) }) @@ -140,15 +141,15 @@ describe("depositHandler — idempotency", () => { describe("depositHandler — fee persistence (AC3)", () => { it("persists developer_fee from the receipt on every deposit event", async () => { - ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ id: "log-fee-001", }) const res = makeRes() await depositHandler(makeReq(VALID_BODY), res) - expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledTimes(1) - expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect(DepositLog.createBridgeDeposit).toHaveBeenCalledTimes(1) + expect(DepositLog.createBridgeDeposit).toHaveBeenCalledWith( expect.objectContaining({ developerFee: "0.0", }), @@ -156,14 +157,14 @@ describe("depositHandler — fee persistence (AC3)", () => { }) it("persists the full deposit record with transfer id, customer, state and receipt breakdown", async () => { - ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ id: "log-fee-002", }) const res = makeRes() await depositHandler(makeReq(VALID_BODY), res) - expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect(DepositLog.createBridgeDeposit).toHaveBeenCalledWith( expect.objectContaining({ eventId: "wh_789xyz654mno", transferId: "tr_xyz789abc123", @@ -180,7 +181,7 @@ describe("depositHandler — fee persistence (AC3)", () => { }) it("returns 200 success after persisting the deposit log", async () => { - ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ id: "log-fee-003", }) @@ -192,7 +193,7 @@ describe("depositHandler — fee persistence (AC3)", () => { }) it("returns 500 when log persistence fails", async () => { - ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue( + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue( new Error("mongo timeout"), ) @@ -203,7 +204,7 @@ describe("depositHandler — fee persistence (AC3)", () => { }) it("handles null developer_fee gracefully", async () => { - ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ id: "log-fee-004", }) @@ -218,9 +219,42 @@ describe("depositHandler — fee persistence (AC3)", () => { const res = makeRes() await depositHandler(makeReq(body), res) - expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect(DepositLog.createBridgeDeposit).toHaveBeenCalledWith( expect.objectContaining({ developerFee: "0.0" }), ) expect(res.status as jest.Mock).toHaveBeenCalledWith(200) }) + + it("writes an ERPNext audit row after the deposit log is persisted", async () => { + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ + id: "log-audit-001", + }) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(writeBridgeDepositRequest).toHaveBeenCalledWith({ + eventId: "wh_789xyz654mno", + eventObject: VALID_EVENT_OBJECT, + rawPayload: VALID_BODY, + }) + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + }) + + it("returns 500 when the ERPNext audit write fails", async () => { + ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ + id: "log-audit-002", + }) + ;(writeBridgeDepositRequest as jest.Mock).mockResolvedValue( + new Error("erpnext timeout"), + ) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) + expect(res.json as jest.Mock).toHaveBeenCalledWith({ + error: "Failed to persist ERPNext audit row", + }) + }) }) diff --git a/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts b/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts index 30bb87f31..602e0d467 100644 --- a/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts @@ -15,10 +15,19 @@ jest.mock("@app/bridge/send-withdrawal-notification", () => ({ sendBridgeWithdrawalNotificationBestEffort: jest.fn().mockResolvedValue(undefined), })) +jest.mock("@services/frappe/BridgeTransferRequestWriter", () => ({ + writeBridgeCashoutCompleted: jest.fn(), + writeBridgeCashoutFailed: jest.fn(), +})) + import { Request, Response } from "express" import { LockService } from "@services/lock" import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" import { sendBridgeWithdrawalNotificationBestEffort } from "@app/bridge/send-withdrawal-notification" +import { + writeBridgeCashoutCompleted, + writeBridgeCashoutFailed, +} from "@services/frappe/BridgeTransferRequestWriter" import { transferHandler } from "@services/bridge/webhook-server/routes/transfer" const makeRes = () => { @@ -40,6 +49,8 @@ beforeEach(() => { lockFn = jest.fn().mockResolvedValue({}) updateFn.mockResolvedValue({ ...WITHDRAWAL_RECORD, status: "completed" }) ;(LockService as jest.Mock).mockReturnValue({ lockIdempotencyKey: lockFn }) + ;(writeBridgeCashoutCompleted as jest.Mock).mockResolvedValue(true) + ;(writeBridgeCashoutFailed as jest.Mock).mockResolvedValue(true) }) describe("transferHandler", () => { @@ -60,18 +71,35 @@ describe("transferHandler", () => { expect(lockFn).not.toHaveBeenCalled() }) - it("acquires idempotency lock only after a successful status update", async () => { + it("acquires idempotency lock only after a successful status and audit update", async () => { const res = makeRes() await transferHandler( makeReq({ event: "transfer.completed", - data: { transfer_id: "tr-abc", state: "payment_processed" }, + event_id: "wh-transfer-1", + data: { + transfer_id: "tr-abc", + state: "payment_processed", + amount: "25.00", + currency: "usdt", + }, }), res, ) expect(updateFn).toHaveBeenCalled() - expect(lockFn).toHaveBeenCalledWith("bridge-transfer:tr-abc:transfer.completed:payment_processed") + expect(writeBridgeCashoutCompleted).toHaveBeenCalledWith( + expect.objectContaining({ + transferId: "tr-abc", + amount: "25.00", + currency: "usdt", + sourceEventId: "wh-transfer-1", + sourceEventType: "transfer.completed", + }), + ) + expect(lockFn).toHaveBeenCalledWith( + "bridge-transfer:tr-abc:transfer.completed:payment_processed", + ) expect(res.status as jest.Mock).toHaveBeenCalledWith(200) }) @@ -167,6 +195,73 @@ describe("transferHandler", () => { expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() }) + it("returns 500 and does not mark processed when the completed audit write fails", async () => { + ;(writeBridgeCashoutCompleted as jest.Mock).mockResolvedValue( + new Error("erpnext timeout"), + ) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { + transfer_id: "tr-abc", + state: "payment_processed", + amount: "25.00", + currency: "usdt", + }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) + expect(res.json as jest.Mock).toHaveBeenCalledWith({ + error: "Failed to persist ERPNext audit row", + }) + expect(lockFn).not.toHaveBeenCalled() + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) + + it("writes a failed cashout audit row before marking a failed transfer processed", async () => { + updateFn.mockResolvedValue({ + ...WITHDRAWAL_RECORD, + status: "failed", + accountId: "acct-1", + amount: "25.00", + currency: "usdt", + failureReason: "ACH return", + }) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + event_id: "wh-transfer-2", + data: { + transfer_id: "tr-abc", + state: "returned", + reason: "ACH return", + amount: "25.00", + currency: "usdt", + }, + }), + res, + ) + + expect(writeBridgeCashoutFailed).toHaveBeenCalledWith( + expect.objectContaining({ + transferId: "tr-abc", + amount: "25.00", + currency: "usdt", + accountId: "acct-1", + failureReason: "ACH return", + sourceEventId: "wh-transfer-2", + }), + ) + expect(lockFn).toHaveBeenCalled() + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + }) + it("returns 200 already_terminal when failure arrives after completion", async () => { const { RepositoryError } = jest.requireActual("@domain/errors") updateFn.mockResolvedValue( diff --git a/test/flash/unit/services/frappe/BridgeTransferRequestWriter.spec.ts b/test/flash/unit/services/frappe/BridgeTransferRequestWriter.spec.ts new file mode 100644 index 000000000..173ecc03c --- /dev/null +++ b/test/flash/unit/services/frappe/BridgeTransferRequestWriter.spec.ts @@ -0,0 +1,114 @@ +jest.mock("@services/frappe/ErpNext", () => ({ + upsertBridgeTransferRequest: jest.fn(), +})) + +import ErpNext from "@services/frappe/ErpNext" +import { + writeBridgeCashoutCompleted, + writeBridgeCashoutFailed, + writeBridgeDepositRequest, + writeIbexCryptoReceiveRequest, +} from "@services/frappe/BridgeTransferRequestWriter" +import { BridgeTransferRequestStatus } from "@services/frappe/models/BridgeTransferRequest" + +const upsert = ErpNext.upsertBridgeTransferRequest as jest.Mock +const lastRequestInput = () => upsert.mock.calls[0][0].input + +describe("BridgeTransferRequestWriter", () => { + beforeEach(() => { + jest.clearAllMocks() + upsert.mockResolvedValue(true) + }) + + it("writes Bridge deposit events as topup audit requests", async () => { + await writeBridgeDepositRequest({ + eventId: "wh_123", + eventObject: { + id: "tr_123", + state: "funds_received", + amount: "10.00", + currency: "usd", + on_behalf_of: "cust_123", + receipt: { + developer_fee: "0.05", + initial_amount: "10.00", + subtotal_amount: "9.95", + final_amount: "9.95", + destination_tx_hash: "tx_123", + }, + }, + rawPayload: { event_id: "wh_123" }, + }) + + expect(lastRequestInput()).toEqual( + expect.objectContaining({ + requestId: "tr_123", + status: BridgeTransferRequestStatus.FiatReceived, + bridgeCustomerId: "cust_123", + bridgeTransferId: "tr_123", + ibexTxHash: "tx_123", + }), + ) + }) + + it("writes IBEX crypto receives as settled topup audit requests", async () => { + await writeIbexCryptoReceiveRequest({ + txHash: "tx_123", + address: "0xabc", + amount: "4.250000", + currency: "USDT", + network: "ethereum", + accountId: "acct_123" as AccountId, + walletId: "wallet_123" as WalletId, + rawPayload: { tx_hash: "tx_123" }, + }) + + expect(lastRequestInput()).toEqual( + expect.objectContaining({ + requestId: "ibex:tx_123", + status: BridgeTransferRequestStatus.Settled, + accountId: "acct_123", + walletId: "wallet_123", + }), + ) + }) + + it("writes completed Bridge transfers as completed cashout audit requests", async () => { + await writeBridgeCashoutCompleted({ + transferId: "tr_cashout", + amount: "5.00", + currency: "usdt", + accountId: "acct_123" as AccountId, + sourceEventType: "transfer.completed", + rawPayload: { event: "transfer.completed" }, + }) + + expect(lastRequestInput()).toEqual( + expect.objectContaining({ + requestId: "tr_cashout", + status: BridgeTransferRequestStatus.Completed, + accountId: "acct_123", + }), + ) + }) + + it("writes failed Bridge transfers as failed cashout audit requests", async () => { + await writeBridgeCashoutFailed({ + transferId: "tr_cashout", + amount: "5.00", + currency: "usdt", + accountId: "acct_123" as AccountId, + failureReason: "ACH returned", + sourceEventType: "transfer.failed", + rawPayload: { event: "transfer.failed" }, + }) + + expect(lastRequestInput()).toEqual( + expect.objectContaining({ + requestId: "tr_cashout", + status: BridgeTransferRequestStatus.Failed, + failureReason: "ACH returned", + }), + ) + }) +}) diff --git a/test/flash/unit/services/frappe/ErpNext.spec.ts b/test/flash/unit/services/frappe/ErpNext.spec.ts new file mode 100644 index 000000000..d473bae9b --- /dev/null +++ b/test/flash/unit/services/frappe/ErpNext.spec.ts @@ -0,0 +1,81 @@ +jest.mock("axios", () => ({ + get: jest.fn(), + post: jest.fn(), + put: jest.fn(), + isAxiosError: jest.fn((err) => Boolean(err?.isAxiosError)), +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/tracing", () => ({ + recordExceptionInCurrentSpan: jest.fn(), +})) + +jest.mock("@config", () => ({ + FrappeConfig: undefined, +})) + +import axios from "axios" +import { ErpNext } from "@services/frappe/ErpNext" +import { + BridgeTransferRequest, + BridgeTransferRequestStatus, + BridgeTransferRequestTransactionType, +} from "@services/frappe/models/BridgeTransferRequest" + +const mockedAxios = axios as unknown as { + get: jest.Mock + post: jest.Mock + put: jest.Mock +} + +const client = new ErpNext("https://erp.example", "erp.example", { + apiKey: "key", + apiSecret: "secret", +}) + +const request = new BridgeTransferRequest({ + requestId: "tr_123", + transactionType: BridgeTransferRequestTransactionType.Topup, + status: BridgeTransferRequestStatus.FiatReceived, + amount: "10.00", + currency: "usd", +}) + +describe("ErpNext.upsertBridgeTransferRequest", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("creates a Bridge Transfer Request when request_id is absent", async () => { + mockedAxios.get.mockResolvedValue({ data: { data: [] } }) + mockedAxios.post.mockResolvedValue({ data: { data: { name: "BTR-1" } } }) + + const result = await client.upsertBridgeTransferRequest(request) + + expect(result).toBe(true) + expect(mockedAxios.post).toHaveBeenCalledWith( + "https://erp.example/api/resource/Bridge Transfer Request", + expect.objectContaining({ request_id: "tr_123" }), + expect.any(Object), + ) + expect(mockedAxios.put).not.toHaveBeenCalled() + }) + + it("updates a Bridge Transfer Request when request_id already exists", async () => { + mockedAxios.get.mockResolvedValue({ data: { data: [{ name: "BTR-1" }] } }) + mockedAxios.put.mockResolvedValue({ data: { data: { name: "BTR-1" } } }) + + const result = await client.upsertBridgeTransferRequest(request) + + expect(result).toBe(true) + expect(mockedAxios.post).not.toHaveBeenCalled() + expect(mockedAxios.put).toHaveBeenCalledWith( + "https://erp.example/api/resource/Bridge%20Transfer%20Request/BTR-1", + expect.objectContaining({ request_id: "tr_123" }), + expect.any(Object), + ) + }) +}) diff --git a/test/flash/unit/services/frappe/models/BridgeTransferRequest.spec.ts b/test/flash/unit/services/frappe/models/BridgeTransferRequest.spec.ts new file mode 100644 index 000000000..37b3df425 --- /dev/null +++ b/test/flash/unit/services/frappe/models/BridgeTransferRequest.spec.ts @@ -0,0 +1,65 @@ +import { + BridgeTransferRequest, + BridgeTransferRequestStatus, + BridgeTransferRequestTransactionType, +} from "@services/frappe/models/BridgeTransferRequest" + +describe("BridgeTransferRequest", () => { + it("serializes a topup audit request to ERPNext field names", () => { + const request = new BridgeTransferRequest({ + requestId: "tr_123", + transactionType: BridgeTransferRequestTransactionType.Topup, + status: BridgeTransferRequestStatus.FiatReceived, + amount: "10.25", + currency: "usd", + bridgeCustomerId: "cust_123", + bridgeTransferId: "tr_123", + sourceEventId: "wh_123", + sourceEventType: "deposit.funds_received", + sourceSystemsSeen: ["bridge_deposit"], + rawPayload: { event_id: "wh_123" }, + }) + + expect(request.toErpnext()).toEqual({ + doctype: "Bridge Transfer Request", + request_id: "tr_123", + transaction_type: "Topup", + status: "Fiat Received", + provider: "Bridge", + asset: "USDT", + network: "Ethereum", + amount: "10.25", + currency: "usd", + developer_fee: undefined, + initial_amount: undefined, + subtotal_amount: undefined, + final_amount: undefined, + account_id: undefined, + wallet_id: undefined, + bridge_customer_id: "cust_123", + bridge_transfer_id: "tr_123", + ibex_tx_hash: undefined, + address: undefined, + source_event_id: "wh_123", + source_event_type: "deposit.funds_received", + source_systems_seen: "bridge_deposit", + first_seen_at: undefined, + last_seen_at: expect.any(String), + raw_payload_json: JSON.stringify({ event_id: "wh_123" }), + failure_reason: undefined, + }) + }) + + it("serializes source systems without duplicates", () => { + const request = new BridgeTransferRequest({ + requestId: "ibex:tx-123", + transactionType: BridgeTransferRequestTransactionType.Topup, + status: BridgeTransferRequestStatus.Settled, + amount: "2.500000", + currency: "USDT", + sourceSystemsSeen: ["ibex_crypto_receive", "ibex_crypto_receive"], + }) + + expect(request.toErpnext().source_systems_seen).toBe("ibex_crypto_receive") + }) +}) diff --git a/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts b/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts index b3249c702..e4e42d929 100644 --- a/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts +++ b/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts @@ -8,7 +8,7 @@ jest.mock("@services/mongoose/accounts", () => ({ })) jest.mock("@services/mongoose/ibex-crypto-receive-log", () => ({ - createIbexCryptoReceiveLog: jest.fn(), + createIbexCryptoReceive: jest.fn(), })) jest.mock("@app/wallets", () => ({ @@ -27,12 +27,17 @@ jest.mock("@services/bridge/reconciliation", () => ({ reconcileByTxHash: jest.fn().mockResolvedValue({ status: "matched" }), })) +jest.mock("@services/frappe/BridgeTransferRequestWriter", () => ({ + writeIbexCryptoReceiveRequest: jest.fn(), +})) + import { cryptoReceiveHandler } from "@services/ibex/webhook-server/routes/crypto-receive" import { AccountsRepository } from "@services/mongoose/accounts" -import { createIbexCryptoReceiveLog } from "@services/mongoose/ibex-crypto-receive-log" +import { createIbexCryptoReceive } from "@services/mongoose/ibex-crypto-receive-log" import { listWalletsByAccountId } from "@app/wallets" import { LockService } from "@services/lock" import { WalletCurrency } from "@domain/shared" +import { writeIbexCryptoReceiveRequest } from "@services/frappe/BridgeTransferRequestWriter" const ACCOUNT_ID = "account-001" as AccountId const WALLET_ID = "wallet-usdt-001" as WalletId @@ -56,10 +61,11 @@ describe("cryptoReceiveHandler", () => { ;(AccountsRepository as jest.Mock).mockReturnValue({ findByBridgeEthereumAddress: jest.fn().mockResolvedValue({ id: ACCOUNT_ID }), }) - ;(createIbexCryptoReceiveLog as jest.Mock).mockResolvedValue({ id: "log-001" }) + ;(createIbexCryptoReceive as jest.Mock).mockResolvedValue({ id: "log-001" }) ;(listWalletsByAccountId as jest.Mock).mockResolvedValue([ { id: WALLET_ID, currency: WalletCurrency.Usdt }, ]) + ;(writeIbexCryptoReceiveRequest as jest.Mock).mockResolvedValue(true) }) it("accepts Ethereum USDT receive webhooks and normalizes persisted currency/network", async () => { @@ -79,7 +85,7 @@ describe("cryptoReceiveHandler", () => { ) expect(AccountsRepository().findByBridgeEthereumAddress).toHaveBeenCalledWith(ADDRESS) - expect(createIbexCryptoReceiveLog).toHaveBeenCalledWith( + expect(createIbexCryptoReceive).toHaveBeenCalledWith( expect.objectContaining({ txHash: TX_HASH, address: ADDRESS, @@ -93,6 +99,64 @@ describe("cryptoReceiveHandler", () => { expect(res.json).toHaveBeenCalledWith({ status: "success" }) }) + it("writes an ERPNext audit row after resolving the account and USDT wallet", async () => { + const res = makeResponse() + + await cryptoReceiveHandler( + { + body: { + tx_hash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "USDT", + network: "ethereum", + }, + } as never, + res as never, + ) + + expect(writeIbexCryptoReceiveRequest).toHaveBeenCalledWith({ + txHash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "USDT", + network: "Ethereum", + accountId: ACCOUNT_ID, + walletId: WALLET_ID, + rawPayload: { + tx_hash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "USDT", + network: "ethereum", + }, + }) + expect(res.status).toHaveBeenCalledWith(200) + }) + + it("returns 500 when the ERPNext audit write fails", async () => { + ;(writeIbexCryptoReceiveRequest as jest.Mock).mockResolvedValue( + new Error("erpnext timeout"), + ) + const res = makeResponse() + + await cryptoReceiveHandler( + { + body: { + tx_hash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "USDT", + network: "ethereum", + }, + } as never, + res as never, + ) + + expect(res.status).toHaveBeenCalledWith(500) + expect(res.json).toHaveBeenCalledWith({ error: "erpnext_audit_failed" }) + }) + it("rejects legacy Tron USDT receive webhooks for the ETH-USDT Cash Wallet path", async () => { const res = makeResponse() @@ -110,7 +174,7 @@ describe("cryptoReceiveHandler", () => { ) expect(LockService().lockOnChainTxHash).not.toHaveBeenCalled() - expect(createIbexCryptoReceiveLog).not.toHaveBeenCalled() + expect(createIbexCryptoReceive).not.toHaveBeenCalled() expect(res.status).toHaveBeenCalledWith(400) expect(res.json).toHaveBeenCalledWith({ error: "Invalid payload" }) }) From 563153bc46a24e0fa62c0c1fc5d0a9e9b03e8135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olaniran=20=E2=9A=A1?= <93789719+heyolaniran@users.noreply.github.com> Date: Thu, 4 Jun 2026 01:39:03 -0700 Subject: [PATCH 24/43] fix(bridge): lower account level requirement from level 2 to level > 0 (#385) * fix(bridge): lower account level requirement from level 2 to level > 0 Bridge operations now allow any non-zero account level instead of requiring Pro (level 2+). * fix: account denomination for non zero level --------- Co-authored-by: Island Bitcoin <34528298+islandbitcoin@users.noreply.github.com> --- src/graphql/public/root/mutation/bridge-add-external-account.ts | 2 +- .../public/root/mutation/bridge-create-virtual-account.ts | 2 +- src/graphql/public/root/mutation/bridge-initiate-kyc.ts | 2 +- src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts | 2 +- src/graphql/public/root/query/bridge-virtual-account.ts | 2 +- src/services/bridge/errors.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/graphql/public/root/mutation/bridge-add-external-account.ts b/src/graphql/public/root/mutation/bridge-add-external-account.ts index 1c3ccfbe5..2877471e2 100644 --- a/src/graphql/public/root/mutation/bridge-add-external-account.ts +++ b/src/graphql/public/root/mutation/bridge-add-external-account.ts @@ -21,7 +21,7 @@ const bridgeAddExternalAccount = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } } - if (!domainAccount || domainAccount.level < 2) { + if (!domainAccount || domainAccount.level <= 0) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } diff --git a/src/graphql/public/root/mutation/bridge-create-virtual-account.ts b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts index 487a732f8..a7eb22fe2 100644 --- a/src/graphql/public/root/mutation/bridge-create-virtual-account.ts +++ b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts @@ -21,7 +21,7 @@ const bridgeCreateVirtualAccount = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } } - if (!domainAccount || domainAccount.level < 2) { + if (!domainAccount || domainAccount.level <= 0) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } diff --git a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts index 3f1574c1a..bf88890d9 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts @@ -34,7 +34,7 @@ const bridgeInitiateKyc = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } } - if (!domainAccount || domainAccount.level < 2) { + if (!domainAccount || domainAccount.level <= 0) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } diff --git a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts index 945e7624c..4eff628ba 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts @@ -57,7 +57,7 @@ const bridgeInitiateWithdrawal = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } } - if (!domainAccount || domainAccount.level < 2) { + if (!domainAccount || domainAccount.level <= 0) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } diff --git a/src/graphql/public/root/query/bridge-virtual-account.ts b/src/graphql/public/root/query/bridge-virtual-account.ts index eebcc6c84..41e877ef4 100644 --- a/src/graphql/public/root/query/bridge-virtual-account.ts +++ b/src/graphql/public/root/query/bridge-virtual-account.ts @@ -14,7 +14,7 @@ const bridgeVirtualAccount = GT.Field({ if (!domainAccount) return null - if (domainAccount.level < 2) { + if (domainAccount.level <= 0) { throw mapAndParseErrorForGqlResponse(new BridgeAccountLevelError()) } diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index 3cef54938..b0b6178fc 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -58,7 +58,7 @@ export class BridgeInsufficientFundsError extends BridgeError { } export class BridgeAccountLevelError extends BridgeError { - constructor(message: string = "Bridge requires Pro account (Level 2+)") { + constructor(message: string = "Bridge requires at least a Personal account (Level 1+)") { super(message) } } From 66863321b21adf37229306cc1bbcf812e0c119b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olaniran=20=E2=9A=A1?= <93789719+heyolaniran@users.noreply.github.com> Date: Thu, 4 Jun 2026 01:39:52 -0700 Subject: [PATCH 25/43] fix(bridge): correct replay tooling for Bridge webhook_events API (#381) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename createBridgeReplayLog → createBridgeReplay in replay.spec.ts to match the production export; spec was failing on every run before this. - Remove start_date/end_date from ListEventsParams — Bridge /webhook_events only supports starting_after, ending_before, limit and category; date range filtering is now applied locally inside listAllEvents. - Update replay-bridge-events.ts to pass start/end (local filter) instead of the silently-dropped start_date/end_date. - Update client.spec.ts to assert local filtering behaviour rather than asserting those params are forwarded to the Bridge API. --- src/scripts/replay-bridge-events.ts | 4 +-- src/services/bridge/client.ts | 16 ++++++--- .../flash/unit/services/bridge/client.spec.ts | 36 ++++++++++--------- .../bridge/webhook-server/replay.spec.ts | 26 +++++++------- 4 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/scripts/replay-bridge-events.ts b/src/scripts/replay-bridge-events.ts index 915e46bb0..88fe9a3b2 100644 --- a/src/scripts/replay-bridge-events.ts +++ b/src/scripts/replay-bridge-events.ts @@ -131,8 +131,8 @@ const main = async () => { : undefined for await (const event of listAllEvents({ - start_date: args.start, - end_date: args.end, + start: args.start, + end: args.end, event_type: bridgeFilter, })) { const replayEventObject: BridgeReplayEventEnvelope = { diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index d6d07db65..363e535bf 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -312,8 +312,6 @@ export interface BridgeWebhookEvent { } export interface ListEventsParams { - start_date?: string - end_date?: string event_type?: string after?: string page_size?: number @@ -563,16 +561,24 @@ export class BridgeClient { export default new BridgeClient() export async function* listAllEvents( - params?: Omit, + params?: Omit & { start?: string; end?: string }, ): AsyncGenerator { const client = new BridgeClient() + const startMs = params?.start ? new Date(params.start).getTime() : -Infinity + const endMs = params?.end ? new Date(params.end).getTime() : Infinity + + // Strip start/end — Bridge /webhook_events only supports cursor params; filter locally. + const { start: _s, end: _e, ...apiParams } = params ?? {} let cursor: string | undefined do { - const page = await client.listEvents({ ...params, after: cursor, page_size: 100 }) + const page = await client.listEvents({ ...apiParams, after: cursor, page_size: 100 }) for (const event of page.data) { - yield event + const eventMs = new Date(event.created_at).getTime() + if (eventMs >= startMs && eventMs <= endMs) { + yield event + } } cursor = page.has_more ? page.cursor : undefined diff --git a/test/flash/unit/services/bridge/client.spec.ts b/test/flash/unit/services/bridge/client.spec.ts index ef9c4c203..fc437762a 100644 --- a/test/flash/unit/services/bridge/client.spec.ts +++ b/test/flash/unit/services/bridge/client.spec.ts @@ -119,30 +119,34 @@ describe("listAllEvents", () => { ) }) - it("forwards start_date, end_date and event_type filters to every page", async () => { - listEventsSpy - .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "c1" }) - .mockResolvedValueOnce({ - data: [makeEvent("e2")], - has_more: false, - cursor: undefined, - }) + it("filters events locally by start/end window and does not forward start/end to Bridge API", async () => { + const inWindow = makeEvent("e1") // created_at: "2026-05-01T10:00:00Z" — inside window + const tooEarly = { ...makeEvent("e2"), created_at: "2026-04-30T23:59:59Z" } - const params = { - start_date: "2026-05-01T00:00:00Z", - end_date: "2026-05-02T00:00:00Z", - event_type: "transfer.completed", - } + listEventsSpy + .mockResolvedValueOnce({ data: [inWindow, tooEarly], has_more: true, cursor: "c1" }) + .mockResolvedValueOnce({ data: [makeEvent("e3")], has_more: false, cursor: undefined }) const drained: BridgeWebhookEvent[] = [] - for await (const event of listAllEvents(params)) { + for await (const event of listAllEvents({ + start: "2026-05-01T00:00:00Z", + end: "2026-05-02T00:00:00Z", + event_type: "transfer.completed", + })) { drained.push(event) } - expect(drained).toHaveLength(2) + // e2 is before the window start — only e1 and e3 pass through + expect(drained.map((e) => e.id)).toEqual(["e1", "e3"]) expect(listEventsSpy).toHaveBeenCalledTimes(2) for (const call of listEventsSpy.mock.calls) { - expect(call[0]).toMatchObject(params) + // start/end must NOT be sent to Bridge — it only understands cursor params + expect(call[0]).not.toHaveProperty("start") + expect(call[0]).not.toHaveProperty("end") + expect(call[0]).not.toHaveProperty("start_date") + expect(call[0]).not.toHaveProperty("end_date") + // event_type is still forwarded (mapped to category inside listEvents) + expect(call[0]).toMatchObject({ event_type: "transfer.completed" }) } }) diff --git a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts index 9b9988b64..e74c7898b 100644 --- a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts @@ -12,7 +12,7 @@ jest.mock("@services/logger", () => ({ })) jest.mock("@services/mongoose/bridge-replay-log", () => ({ - createBridgeReplayLog: jest.fn(), + createBridgeReplay: jest.fn(), })) jest.mock("@services/bridge/webhook-server/routes/deposit", () => ({ @@ -173,7 +173,7 @@ describe("replayHandler", () => { ] beforeEach(() => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-001" }) + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue({ id: "log-001" }) }) it("routes outbound withdrawal payment_processed replay to transfer handler", async () => { @@ -182,7 +182,7 @@ describe("replayHandler", () => { ;(res.json as jest.Mock)({ status: "success" }) return Promise.resolve(res) }) - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-wd-001" }) + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue({ id: "log-wd-001" }) const res = makeRes() await replayHandler( @@ -227,7 +227,7 @@ describe("replayHandler", () => { describe("dry_run mode", () => { it("returns 200 without calling any handler", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue({ id: "log-dry-001", }) @@ -241,20 +241,20 @@ describe("replayHandler", () => { }) it("persists a dry-run log entry with httpStatus 0", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue({ id: "log-dry-002", }) const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) - expect(ReplayLog.createBridgeReplayLog).toHaveBeenCalledWith( + expect(ReplayLog.createBridgeReplay).toHaveBeenCalledWith( expect.objectContaining({ httpStatus: 0, dryRun: true }), ) }) it("returns 500 when dry-run log creation fails", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue( + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue( new Error("db error"), ) @@ -277,7 +277,7 @@ describe("replayHandler", () => { }) it("returns the handler's status code and response body", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue({ id: "log-live-001", }) @@ -290,14 +290,14 @@ describe("replayHandler", () => { }) it("persists a replay log with triage context (operator + time window)", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue({ id: "log-live-002", }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) - expect(ReplayLog.createBridgeReplayLog).toHaveBeenCalledWith( + expect(ReplayLog.createBridgeReplay).toHaveBeenCalledWith( expect.objectContaining({ operator: "ops@example.com", timeWindowStart: new Date("2026-05-01T00:00:00Z"), @@ -310,7 +310,7 @@ describe("replayHandler", () => { }) it("includes log_id in the response so ops can trace the replay", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue({ id: "log-trace-007", }) @@ -322,7 +322,7 @@ describe("replayHandler", () => { }) it("returns 500 when log creation fails after a successful handler run", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue( + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue( new Error("mongo down"), ) @@ -340,7 +340,7 @@ describe("replayHandler", () => { return Promise.resolve(res) }, ) - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-4xx" }) + ;(ReplayLog.createBridgeReplay as jest.Mock).mockResolvedValue({ id: "log-4xx" }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) From cafd2d1604ffe348be1544a7e50d34fe28f96908 Mon Sep 17 00:00:00 2001 From: Patoo Date: Fri, 5 Jun 2026 21:23:49 -0400 Subject: [PATCH 26/43] fix(bridge): add external account webhook config (#390) Co-authored-by: Vandana --- dev/apollo-federation/supergraph.graphql | 9 ++++++++- dev/config/base-config.yaml | 1 + src/graphql/public/schema.graphql | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 09c7b3662..ee20520f6 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -268,7 +268,7 @@ type BridgeAddExternalAccountPayload @join__type(graph: PUBLIC) { errors: [Error!]! - externalAccount: BridgeExternalAccount + externalAccount: BridgeExternalAccountLink } type BridgeCreateVirtualAccountPayload @@ -287,6 +287,13 @@ type BridgeExternalAccount status: String! } +type BridgeExternalAccountLink + @join__type(graph: PUBLIC) +{ + expiresAt: String! + linkUrl: String! +} + input BridgeInitiateKycInput @join__type(graph: PUBLIC) { diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index fc232cf4c..58cc23919 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -43,6 +43,7 @@ bridge: dwIDAQAB -----END PUBLIC KEY----- transfer: "" + external_account: "" timestampSkewMs: 300000 # See Foreign Exchange Rates: https://www.firstglobal-bank.com/ diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index 30c15eada..e78c343a2 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -243,7 +243,7 @@ input BankAccountInput { type BridgeAddExternalAccountPayload { errors: [Error!]! - externalAccount: BridgeExternalAccount + externalAccount: BridgeExternalAccountLink } type BridgeCreateVirtualAccountPayload { @@ -258,6 +258,11 @@ type BridgeExternalAccount { status: String! } +type BridgeExternalAccountLink { + expiresAt: String! + linkUrl: String! +} + input BridgeInitiateKycInput { email: String full_name: String From 09644485f199b529bd5351d8091f07b136c6be4b Mon Sep 17 00:00:00 2001 From: Patoo Date: Sat, 6 Jun 2026 00:08:40 -0400 Subject: [PATCH 27/43] fix(bridge): align GraphQL contract and error codes (#393) Co-authored-by: Vandana --- dev/apollo-federation/supergraph.graphql | 4 +- docs/bridge-integration/API.md | 12 ++- src/graphql/error-map.ts | 90 +++++++++++++++---- src/graphql/public/schema.graphql | 4 +- .../public/types/object/bridge-withdrawal.ts | 4 +- .../unit/graphql/bridge-error-map.spec.ts | 54 +++++++++++ .../types/object/bridge-contract.spec.ts | 48 ++++++++++ 7 files changed, 194 insertions(+), 22 deletions(-) create mode 100644 test/flash/unit/graphql/bridge-error-map.spec.ts create mode 100644 test/flash/unit/graphql/public/types/object/bridge-contract.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index ee20520f6..5dcf55abd 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -351,8 +351,8 @@ type BridgeWithdrawal createdAt: String! currency: String! failureReason: String - id: ID! - status: String! + state: String! + transferId: ID! } """ diff --git a/docs/bridge-integration/API.md b/docs/bridge-integration/API.md index 2bd24e5fb..7628b9b90 100644 --- a/docs/bridge-integration/API.md +++ b/docs/bridge-integration/API.md @@ -193,7 +193,17 @@ query BridgeWithdrawals { | Code | Description | | --- | --- | | `BRIDGE_DISABLED` | Bridge integration is disabled in configuration. | -| `BRIDGE_ACCOUNT_LEVEL_ERROR` | User account level is below 2. | +| `BRIDGE_ACCOUNT_LEVEL_ERROR` | User account level is below the required Bridge access level. | +| `BRIDGE_INVALID_AMOUNT` | Withdrawal amount is malformed or not positive. | +| `BRIDGE_BELOW_MINIMUM_WITHDRAWAL` | Withdrawal amount is below the configured minimum. | | `BRIDGE_KYC_PENDING` | Operation requires approved KYC, but it is still pending. | | `BRIDGE_KYC_REJECTED` | KYC was rejected. | +| `BRIDGE_KYC_OFFBOARDED` | Bridge offboarded the customer. | | `BRIDGE_CUSTOMER_NOT_FOUND` | Bridge customer record not found for the user. | +| `BRIDGE_INSUFFICIENT_FUNDS` | USDT balance is insufficient for the withdrawal. | +| `BRIDGE_RATE_LIMIT` | Bridge rate-limited the request. | +| `BRIDGE_TIMEOUT` | Bridge request timed out. | +| `BRIDGE_TRANSFER_FAILED` | Bridge transfer failed. | +| `BRIDGE_WEBHOOK_VALIDATION` | Bridge webhook signature validation failed. | +| `BRIDGE_API_ERROR` | Bridge API returned an unclassified provider error. | +| `BRIDGE_ERROR` | Unclassified Bridge domain error. | diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 480711d87..6dce737f1 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -37,6 +37,7 @@ import { UnauthorizedIPMetadataCountryError, IbexError, InvalidLnurlError, + CustomApolloError, } from "@graphql/error" import { baseLogger } from "@services/logger" @@ -44,6 +45,20 @@ const assertUnreachable = (x: unknown): never => { throw new Error(`This should never compile with ${x}`) } +const bridgeGqlError = ({ + code, + message, +}: { + code: string + message: string +}): CustomApolloError => + new CustomApolloError({ + code, + message, + forwardToClient: true, + logger: baseLogger, + }) + export const mapError = (error: ApplicationError): CustomApolloError => { const errorName = error.name as ApplicationErrorKey let message = "" @@ -479,60 +494,105 @@ export const mapError = (error: ApplicationError): CustomApolloError => { case "BridgeInvalidAmountError": message = error.message || "Amount must be strictly positive with at most 6 decimal places" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_INVALID_AMOUNT", + message, + }) case "BridgeBelowMinimumWithdrawalError": message = error.message || "Withdrawal amount is below the minimum" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_BELOW_MINIMUM_WITHDRAWAL", + message, + }) case "BridgeDisabledError": message = "Bridge integration is currently disabled" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_DISABLED", + message, + }) case "BridgeAccountLevelError": - message = "Bridge requires Pro account (Level 2+)" - return new ValidationInternalError({ message, logger: baseLogger }) + message = error.message || "Bridge requires at least a Personal account (Level 1+)" + return bridgeGqlError({ + code: "BRIDGE_ACCOUNT_LEVEL_ERROR", + message, + }) case "BridgeKycPendingError": message = "KYC verification is pending" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_KYC_PENDING", + message, + }) case "BridgeKycRejectedError": message = "KYC verification was rejected" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_KYC_REJECTED", + message, + }) case "BridgeKycOffboardedError": message = "Your account has been offboarded from Bridge. Please contact support." - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_KYC_OFFBOARDED", + message, + }) case "BridgeCustomerNotFoundError": message = "Bridge customer not found" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_CUSTOMER_NOT_FOUND", + message, + }) case "BridgeInsufficientFundsError": message = "Insufficient funds for withdrawal" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_INSUFFICIENT_FUNDS", + message, + }) case "BridgeRateLimitError": message = "Rate limit exceeded, please try again later" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_RATE_LIMIT", + message, + }) case "BridgeTimeoutError": message = "Request timed out" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_TIMEOUT", + message, + }) case "BridgeTransferFailedError": message = error.message || "Transfer failed" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_TRANSFER_FAILED", + message, + }) case "BridgeWebhookValidationError": message = "Invalid webhook signature" - return new ValidationInternalError({ message, logger: baseLogger }) + return bridgeGqlError({ + code: "BRIDGE_WEBHOOK_VALIDATION", + message, + }) case "BridgeApiError": + message = error.message || "Bridge API error" + return bridgeGqlError({ + code: "BRIDGE_API_ERROR", + message, + }) + case "BridgeError": message = error.message || "Bridge API error" - return new UnknownClientError({ message, logger: baseLogger }) + return bridgeGqlError({ code: "BRIDGE_ERROR", message }) // ---------- // Unhandled below here diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index e78c343a2..d7f81eed8 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -306,8 +306,8 @@ type BridgeWithdrawal { createdAt: String! currency: String! failureReason: String - id: ID! - status: String! + state: String! + transferId: ID! } type BuildInformation { diff --git a/src/graphql/public/types/object/bridge-withdrawal.ts b/src/graphql/public/types/object/bridge-withdrawal.ts index 0c6298215..00a51c13c 100644 --- a/src/graphql/public/types/object/bridge-withdrawal.ts +++ b/src/graphql/public/types/object/bridge-withdrawal.ts @@ -3,10 +3,10 @@ import { GT } from "@graphql/index" const BridgeWithdrawal = GT.Object({ name: "BridgeWithdrawal", fields: () => ({ - id: { type: GT.NonNullID }, + transferId: { type: GT.NonNullID }, amount: { type: GT.NonNull(GT.String) }, currency: { type: GT.NonNull(GT.String) }, - status: { type: GT.NonNull(GT.String) }, + state: { type: GT.NonNull(GT.String) }, failureReason: { type: GT.String }, createdAt: { type: GT.NonNull(GT.String) }, }), diff --git a/test/flash/unit/graphql/bridge-error-map.spec.ts b/test/flash/unit/graphql/bridge-error-map.spec.ts new file mode 100644 index 000000000..194a83b39 --- /dev/null +++ b/test/flash/unit/graphql/bridge-error-map.spec.ts @@ -0,0 +1,54 @@ +import { mapAndParseErrorForGqlResponse, mapError } from "@graphql/error-map" +import { + BridgeAccountLevelError, + BridgeApiError, + BridgeBelowMinimumWithdrawalError, + BridgeCustomerNotFoundError, + BridgeDisabledError, + BridgeError, + BridgeInsufficientFundsError, + BridgeInvalidAmountError, + BridgeKycOffboardedError, + BridgeKycPendingError, + BridgeKycRejectedError, + BridgeRateLimitError, + BridgeTimeoutError, + BridgeTransferFailedError, + BridgeWebhookValidationError, +} from "@services/bridge/errors" + +describe("error-map: Bridge errors", () => { + const cases: Array<[Error, string]> = [ + [new BridgeInvalidAmountError(), "BRIDGE_INVALID_AMOUNT"], + [new BridgeBelowMinimumWithdrawalError(10), "BRIDGE_BELOW_MINIMUM_WITHDRAWAL"], + [new BridgeDisabledError(), "BRIDGE_DISABLED"], + [new BridgeAccountLevelError(), "BRIDGE_ACCOUNT_LEVEL_ERROR"], + [new BridgeKycPendingError(), "BRIDGE_KYC_PENDING"], + [new BridgeKycRejectedError(), "BRIDGE_KYC_REJECTED"], + [new BridgeKycOffboardedError(), "BRIDGE_KYC_OFFBOARDED"], + [new BridgeCustomerNotFoundError(), "BRIDGE_CUSTOMER_NOT_FOUND"], + [new BridgeInsufficientFundsError(), "BRIDGE_INSUFFICIENT_FUNDS"], + [new BridgeRateLimitError(), "BRIDGE_RATE_LIMIT"], + [new BridgeTimeoutError(), "BRIDGE_TIMEOUT"], + [new BridgeTransferFailedError(), "BRIDGE_TRANSFER_FAILED"], + [new BridgeWebhookValidationError(), "BRIDGE_WEBHOOK_VALIDATION"], + [new BridgeApiError("Bridge API error", 500), "BRIDGE_API_ERROR"], + [new BridgeError("Bridge unavailable"), "BRIDGE_ERROR"], + ] + + it.each(cases)("maps %p to %s", (input, expectedCode) => { + const result = mapError(input as ApplicationError) + + expect(result.extensions.code).toBe(expectedCode) + expect(result.extensions.code).not.toBe("INVALID_INPUT") + expect(result.message).toBeTruthy() + }) + + it.each(cases)("parses %p into payload error code %s", (input, expectedCode) => { + const result = mapAndParseErrorForGqlResponse(input as ApplicationError) + + expect(result.code).toBe(expectedCode) + expect(result.code).not.toBe("INVALID_INPUT") + expect(result.message).toBeTruthy() + }) +}) diff --git a/test/flash/unit/graphql/public/types/object/bridge-contract.spec.ts b/test/flash/unit/graphql/public/types/object/bridge-contract.spec.ts new file mode 100644 index 000000000..08afbfdc1 --- /dev/null +++ b/test/flash/unit/graphql/public/types/object/bridge-contract.spec.ts @@ -0,0 +1,48 @@ +import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" +import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" +import { defaultFieldResolver } from "graphql" + +describe("Bridge public GraphQL object contract", () => { + it("exposes withdrawal fields returned by BridgeService", () => { + const fields = BridgeWithdrawal.getFields() + + expect(fields).toHaveProperty("transferId") + expect(fields).toHaveProperty("amount") + expect(fields).toHaveProperty("currency") + expect(fields).toHaveProperty("state") + expect(fields).toHaveProperty("createdAt") + expect(fields).not.toHaveProperty("id") + expect(fields).not.toHaveProperty("status") + }) + + it("resolves withdrawal transferId and state from service-shaped results", () => { + const fields = BridgeWithdrawal.getFields() + const withdrawal = { + transferId: "transfer-001", + amount: "25.00", + currency: "usdt", + state: "pending", + createdAt: "2026-06-05T00:00:00.000Z", + } + + expect( + defaultFieldResolver(withdrawal, {}, {}, { fieldName: "transferId" } as never), + ).toBe("transfer-001") + expect( + defaultFieldResolver(withdrawal, {}, {}, { fieldName: "state" } as never), + ).toBe("pending") + }) + + it("uses bridgeVirtualAccountId as the virtual account id returned by read queries", () => { + const idField = BridgeVirtualAccount.getFields().id + const virtualAccount = { + bridgeVirtualAccountId: "bridge-va-001", + bankName: "Test Bank", + routingNumber: "123456789", + accountNumber: "123456789012", + accountNumberLast4: "9012", + } + + expect(idField.resolve?.(virtualAccount, {}, {}, {})).toBe("bridge-va-001") + }) +}) From 963325609a0394981062af55bb61d4d4e7534ddb Mon Sep 17 00:00:00 2001 From: Patoo Date: Sun, 7 Jun 2026 00:21:55 -0400 Subject: [PATCH 28/43] fix(ibex): wire USDT LNURL-pay msat conversion (#389) * fix(ibex): convert payToLnurl amount to explicit millisatoshis The IBEX LNURL-pay API (POST /v2/lnurl/pay/send) expects the amount in millisatoshis, but PayLnurlArgs.send.amount passed the wallet currency's base unit directly (USDT micros, USD cents, or BTC sats). Changed PayLnurlArgs to accept amountMsat: number instead of send: IbexCurrency. This makes the expected unit explicit and forces callers to perform the msat conversion at the app layer where the DealerPriceService is available. ENG-406 * feat(lnurl): add USD wallet LNURL payment mutation --- dev/apollo-federation/supergraph.graphql | 22 ++ .../2026-06-05-eng-406-usdt-lnurl-units.md | 368 ++++++++++++++++++ src/app/payments/lnurl-pay.ts | 59 +++ src/domain/errors.ts | 1 + src/graphql/error-map.ts | 3 + src/graphql/public/mutations.ts | 2 + .../root/mutation/lnurl-payment-send.ts | 220 +++++++++++ src/graphql/public/schema.graphql | 20 + src/services/ibex/client.ts | 11 +- src/services/ibex/index.types.d.ts | 14 +- .../flash/unit/app/payments/lnurl-pay.spec.ts | 105 +++++ .../root/mutation/lnurl-payment-send.spec.ts | 171 ++++++++ 12 files changed, 982 insertions(+), 14 deletions(-) create mode 100644 docs/plans/2026-06-05-eng-406-usdt-lnurl-units.md create mode 100644 src/app/payments/lnurl-pay.ts create mode 100644 src/graphql/public/root/mutation/lnurl-payment-send.ts create mode 100644 test/flash/unit/app/payments/lnurl-pay.spec.ts create mode 100644 test/flash/unit/graphql/public/root/mutation/lnurl-payment-send.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 5dcf55abd..8816d85df 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -1099,6 +1099,22 @@ A bech32-encoded HTTPS/Onion URL that can be interacted with automatically by a scalar Lnurl @join__type(graph: PUBLIC) +input LnurlPaymentSendInput + @join__type(graph: PUBLIC) +{ + """Amount to spend from the USD/USDT wallet, in USD cents.""" + amount: FractionalCentAmount! + + """LNURL-pay value to decode and pay.""" + lnurl: Lnurl! + + """Optional memo for the Lightning payment.""" + memo: Memo + + """Wallet ID with sufficient balance. Must belong to the current user.""" + walletId: WalletId! +} + input LnUsdInvoiceCreateInput @join__type(graph: PUBLIC) { @@ -1318,6 +1334,12 @@ type Mutation """ lnUsdInvoiceCreateOnBehalfOfRecipient(input: LnUsdInvoiceCreateOnBehalfOfRecipientInput!): LnInvoicePayload! lnUsdInvoiceFeeProbe(input: LnUsdInvoiceFeeProbeInput!): CentAmountPayload! + + """ + Pay a LNURL-pay endpoint using a USD/USDT wallet balance. + The wallet amount is converted to whole-satoshi millisatoshis before calling IBEX. + """ + lnurlPaymentSend(input: LnurlPaymentSendInput!): PaymentSendPayload! merchantMapSuggest(input: MerchantMapSuggestInput!): MerchantPayload! onChainAddressCreate(input: OnChainAddressCreateInput!): OnChainAddressPayload! onChainAddressCurrent(input: OnChainAddressCurrentInput!): OnChainAddressPayload! diff --git a/docs/plans/2026-06-05-eng-406-usdt-lnurl-units.md b/docs/plans/2026-06-05-eng-406-usdt-lnurl-units.md new file mode 100644 index 000000000..661a6ff55 --- /dev/null +++ b/docs/plans/2026-06-05-eng-406-usdt-lnurl-units.md @@ -0,0 +1,368 @@ +# Fix USDT LNURL-pay Amount Units + +> **ENG-406** — Fix USDT LNURL-pay amount units for Flash Lightning Address edge case + +## Overview + +The IBEX LNURL-pay API (`POST /v2/lnurl/pay/send`) expects the `amount` field in **millisatoshis**, but the `payToLnurl` wrapper was passing `args.send.amount` directly — the wallet currency's base unit (USDT micros, USD cents, or BTC sats). + +## Root Cause + +`src/services/ibex/client.ts:255` — `payToLnurl` passed `args.send.amount` directly to `Ibex.payToLnurl()`. The `args.send` was of type `IbexCurrency` (`{ amount: number, currencyId: IbexCurrencyId }`), and for USDT wallets `amount` is USDT micros (~10,000 per USD cent), not millisatoshis. + +## Fix + +**Interface change** — `src/services/ibex/index.types.d.ts`: +- Replaced `send: IbexCurrency` with `amountMsat: number` on `PayLnurlArgs` + +**Call-site change** — `src/services/ibex/client.ts`: +- Changed `amount: args.send.amount` → `amount: args.amountMsat` + +## Rationale + +- The field name `amountMsat` makes the expected unit unambiguous +- Conversion from wallet currency → msats requires the DealerPriceService (app layer), not available in the services layer +- Callers are forced to perform explicit conversion, preventing silent unit bugs +- `PayLnurlArgs` is only used by `payToLnurl` — no other callers to break + +## Remaining Work + +- Wire up a GraphQL mutation that calls `payToLnurl` with proper MSAT conversion (ENG-406 follow-up) — completed in the follow-up implementation described below +- Authored by: Vandana + +## Implementation Status + +Implemented locally on `eng-274/sandbox-e2e-plan`: + +- Strengthened `PayLnurlArgs.amountMsat` from raw `number` to branded `MilliSatoshis`. +- Added `src/app/payments/lnurl-pay.ts` with USD/USDT wallet amount conversion, whole-satoshi msat generation, IBEX int32 validation, and LNURL `minSendable` / `maxSendable` validation. +- Added `InvalidLnurlAmountError` and mapped it to the existing GraphQL LNURL validation error surface. +- Added wallet-level public GraphQL mutation `lnurlPaymentSend`. +- Registered the mutation in `src/graphql/public/mutations.ts`. +- Regenerated `src/graphql/public/schema.graphql` and `dev/apollo-federation/supergraph.graphql`. +- Added focused unit tests for the conversion helper and mutation resolver. + +Verification: + +- `yarn test:unit --testPathPattern=app/payments/lnurl-pay.spec.ts` — passed. +- `yarn test:unit --testPathPattern=graphql/public/root/mutation/lnurl-payment-send.spec.ts` — passed. +- `npx prettier --check ...` on touched files — passed. +- `npx tsc --noEmit --skipLibCheck` — no ENG-406 source/test errors after fixes; still fails on pre-existing unrelated test type errors in offers, cash-wallet history resolver specs, and Bridge webhook/reconciliation specs. +- `yarn check:sdl` — wrote schemas and composed the supergraph, then failed because `src/graphql/public/schema.graphql` had expected generated changes to commit. + +--- + +## Dual-Model Review Follow-Up + +The wrapper fix is correct, but the follow-up wiring needs more detail before implementation: + +- `amountMsat: number` is clearer than `send: IbexCurrency`, but a branded `MilliSatoshis` type is safer than a raw number. +- IBEX's LNURL-pay request field is named `amount`, but the unit is millisatoshis. +- IBEX documents the field as `int32`, so the plan must reject values above `2_147_483_647` msats before calling IBEX. +- Wallet compatibility requires whole-satoshi payments, so `amountMsat` should be a multiple of `1000`. +- Rounding must happen before LNURL `minSendable` / `maxSendable` validation, because rounding can move a value across a bound. +- Caller-side conversion belongs in the app/graphql layer. The IBEX service wrapper should receive msats and should not import dealer-price or wallet conversion logic. + +--- + +## Follow-Up Implementation Plan: LNURL-Pay From USD/USDT Wallets + +> **For implementer:** execute this task-by-task. Do not push without Dread's explicit approval. + +**Goal:** Add a wallet-level GraphQL mutation that pays a LNURL-pay endpoint from a USD/USDT cash wallet by converting the user-entered wallet amount into integer millisatoshis before calling `Ibex.payToLnurl`. + +**Architecture:** Keep IBEX service code as a thin API wrapper. Decode/fetch/validate the LNURL-pay metadata in the GraphQL/app layer, convert the wallet amount using existing wallet/dealer-price helpers, round to whole satoshis, validate against LNURL bounds, then call `Ibex.payToLnurl({ accountId, amountMsat, params })`. + +**Tech Stack:** TypeScript, GraphQL schema builders, existing Flash wallet helpers, `DealerPriceService`, IBEX LNURL-pay API. + +### Task 1: Strengthen the IBEX wrapper type + +**Files:** +- Modify: `src/services/ibex/index.types.d.ts` +- Modify: `src/services/ibex/client.ts` + +**Step 1: Change `PayLnurlArgs.amountMsat` to the existing branded type** + +```ts +type PayLnurlArgs = { + accountId: IbexAccountId, + amountMsat: MilliSatoshis, + params: string, +} +``` + +`MilliSatoshis` already exists globally in `src/domain/bitcoin/index.types.d.ts`. This keeps the service boundary explicit without inventing another unit type. + +**Step 2: Keep `client.ts` as a pass-through** + +```ts +const payToLnurl = async ( + args: PayLnurlArgs, +): Promise => { + return Ibex.payToLnurl({ + accountId: args.accountId, + amount: args.amountMsat, + params: args.params, + webhookUrl: WebhookServer.endpoints.onPay.lnurl, + webhookSecret: WebhookServer.secret, + }).then(errorHandler) +} +``` + +Do not add conversion logic here. + +### Task 2: Add LNURL-pay conversion helper + +**Files:** +- Create: `src/app/payments/lnurl-pay.ts` +- Test: `test/flash/unit/app/payments/lnurl-pay.spec.ts` + +**Step 1: Define constants** + +```ts +const MSATS_PER_SAT = 1000 +const IBEX_LNURL_PAY_AMOUNT_MAX_MSAT = 2_147_483_647 +``` + +**Step 2: Add helper to convert USD/USDT wallet cents to whole-satoshi msats** + +Use the same wallet-amount semantics as `ln-noamount-usd-invoice-payment-send.ts`: + +```ts +import { toMilliSatsFromNumber } from "@domain/bitcoin" +import { checkedToUsdPaymentAmount } from "@domain/shared" + +export const amountMsatFromUsdWalletAmount = async ({ + amount, + btcFromUsd, +}: { + amount: UsdWalletAmount + btcFromUsd: IDealerPriceService["getSatsFromCentsForImmediateSell"] +}): Promise => { + const usdPaymentAmount = checkedToUsdPaymentAmount( + Number(amount.asUsdCents()), + WalletCurrency.Usd, + ) + if (usdPaymentAmount instanceof Error) return usdPaymentAmount + + const sats = await btcFromUsd(usdPaymentAmount) + if (sats instanceof Error) return sats + + const wholeSats = Math.floor(Number(sats.amount)) + const msats = wholeSats * MSATS_PER_SAT + + return toMilliSatsFromNumber(msats) +} +``` + +Implementation note: verify the buy/sell dealer method against existing send-lightning semantics before finalizing. The intended behavior is "user spends USD/USDT wallet balance to send BTC over Lightning." Existing outgoing payment flows pass both `hedgeBuyUsd` and `hedgeSellUsd` into the payment-flow builder; tests should pin the chosen dealer method. + +**Step 3: Add validation helper** + +```ts +export const validateLnurlPayAmountMsat = ({ + amountMsat, + minSendable, + maxSendable, +}: { + amountMsat: MilliSatoshis + minSendable: number + maxSendable: number +}): ValidationError | true => { + if (!Number.isInteger(amountMsat) || amountMsat <= 0) { + return new InvalidLnurlAmountError("LNURL amount must be positive integer msats") + } + + if (amountMsat % MSATS_PER_SAT !== 0) { + return new InvalidLnurlAmountError("LNURL amount must be a whole-satoshi amount") + } + + if (amountMsat > IBEX_LNURL_PAY_AMOUNT_MAX_MSAT) { + return new InvalidLnurlAmountError("LNURL amount exceeds IBEX int32 limit") + } + + if (amountMsat < minSendable || amountMsat > maxSendable) { + return new InvalidLnurlAmountError("LNURL amount outside minSendable/maxSendable bounds") + } + + return true +} +``` + +If no suitable domain error exists, add a small `InvalidLnurlAmountError` near the existing LNURL errors rather than reusing a misleading generic error. + +**Step 4: Unit tests** + +Cover: + +- Converts USD cents to msats using the injected dealer conversion. +- Floors/rounds to whole satoshis, then multiplies by `1000`. +- Rejects `amountMsat % 1000 !== 0`. +- Rejects values below `minSendable` after rounding. +- Rejects values above `maxSendable` after rounding. +- Rejects values above `2_147_483_647` msats. +- Propagates dealer-price errors. + +### Task 3: Add wallet-level GraphQL mutation + +**Files:** +- Create: `src/graphql/public/root/mutation/lnurl-payment-send.ts` +- Modify: `src/graphql/public/mutations.ts` +- Test: `test/flash/unit/graphql/public/root/mutation/lnurl-payment-send.spec.ts` or the nearest existing GraphQL mutation test path. + +**Mutation name:** `lnurlPaymentSend` + +**Input shape:** + +```ts +const LnurlPaymentSendInput = GT.Input({ + name: "LnurlPaymentSendInput", + fields: () => ({ + walletId: { + type: GT.NonNull(WalletId), + description: "Wallet ID with sufficient balance. Must belong to the current user.", + }, + lnurl: { + type: GT.NonNull(Lnurl), + description: "LNURL-pay value to decode and pay.", + }, + amount: { + type: GT.NonNull(FractionalCentAmount), + description: "Amount to spend from the USD/USDT wallet, in USD cents.", + }, + memo: { + type: Memo, + description: "Optional memo for the Lightning payment.", + }, + }), +}) +``` + +**Payload:** reuse `PaymentSendPayload`. + +**Registration:** import the mutation in `src/graphql/public/mutations.ts` and add it under `mutationFields.authed.atWalletLevel`, next to the other Lightning payment send mutations. + +### Task 4: Implement the mutation resolver + +**Files:** +- Modify: `src/graphql/public/root/mutation/lnurl-payment-send.ts` + +Resolver flow: + +1. Validate GraphQL scalar outputs (`walletId`, `lnurl`, `amount`, `memo`) exactly like the existing invoice send mutations. +2. Require `domainAccount`. +3. Resolve the routed wallet ID through `resolveCashWalletMutationWalletIdForAccount({ account: domainAccount, walletId, client: cashWalletClientCapabilities })`. +4. Use `usdWalletAmountFromWalletId({ walletId: routedWalletId, amount: amount.toString() })`. +5. Validate the routed wallet is a USD wallet, allowing USDT by passing `includeUsdt: true` through the existing wallet validation path or by relying on `usdWalletAmountFromWalletId` to reject non-USD/non-USDT currencies. +6. Decode the LNURL through `Ibex.decodeLnurl({ lnurl })`. +7. Fetch the LNURL-pay metadata from the decoded URL if IBEX only returns the callback URL. The metadata must include `callback`, `minSendable`, `maxSendable`, and `metadata`. +8. Serialize the LNURL-pay metadata into the `params` string expected by `Ibex.payToLnurl`. Match IBEX docs for `params`: JSON string containing `callback`, `maxSendable`, `minSendable`, `metadata`, and `tag: "payRequest"`. +9. Convert wallet amount to msats using the helper from Task 2 and `DealerPriceService`. +10. Validate integer, whole-satoshi, int32, and LNURL bounds. +11. Call: + +```ts +const payment = await Ibex.payToLnurl({ + accountId: routedWalletId, + amountMsat, + params, +}) +``` + +12. Map `IbexError` through `mapAndParseErrorForGqlResponse`. +13. Map IBEX transaction payment status IDs to `PaymentSendStatus` using the same switch as `ln-noamount-usd-invoice-payment-send.ts`. +14. Return `{ errors: [], status: status.value }`. + +### Task 5: Add tests for resolver behavior + +**Files:** +- Test: `test/flash/unit/graphql/public/root/mutation/lnurl-payment-send.spec.ts` +- Possibly update shared test mocks for `@services/ibex/client` and `@services/dealer-price`. + +Test cases: + +- Rejects invalid `walletId`, invalid `lnurl`, invalid `amount`, and invalid `memo` scalar outputs. +- Rejects unauthenticated context. +- Routes wallet ID through `resolveCashWalletMutationWalletIdForAccount`. +- Allows USDT cash wallet IDs after cutover routing. +- Calls `Ibex.decodeLnurl` before `Ibex.payToLnurl`. +- Builds `params` with `callback`, `minSendable`, `maxSendable`, `metadata`, and `tag`. +- Converts wallet amount to whole-satoshi msats before calling IBEX. +- Rejects below `minSendable` after rounding. +- Rejects above `maxSendable` after rounding. +- Rejects above the IBEX int32 limit. +- Propagates dealer-price errors. +- Propagates IBEX decode and pay errors as GraphQL payload errors. +- Maps pending/success/failure status IDs to `PaymentSendStatus`. + +### Task 6: Update schema artifacts and docs + +**Files:** +- Modify if generated: `src/graphql/public/schema.graphql` +- Modify if needed: mobile/backend API docs that list public payment mutations. + +Commands: + +```bash +yarn write-sdl +``` + +If `check:sdl` is the project-standard gate for schema drift, run: + +```bash +yarn check:sdl +``` + +### Task 7: Verification + +Run focused checks first: + +```bash +yarn test:unit --testPathPattern=lnurl-payment-send +``` + +Run related existing tests: + +```bash +yarn test:unit --testPathPattern=ln-noamount-usd-invoice-payment-send +yarn test:unit --testPathPattern=services/ibex/client-usd-wallet +``` + +Run type checks that cover the changed files: + +```bash +npx tsc --noEmit --skipLibCheck +``` + +If the full repo type check still has unrelated pre-existing failures, capture the exact failure set and verify that none are in: + +- `src/services/ibex/client.ts` +- `src/services/ibex/index.types.d.ts` +- `src/app/payments/lnurl-pay.ts` +- `src/graphql/public/root/mutation/lnurl-payment-send.ts` +- new tests + +### Task 8: Commit locally only + +Stage explicit paths: + +```bash +git add \ + src/services/ibex/client.ts \ + src/services/ibex/index.types.d.ts \ + src/app/payments/lnurl-pay.ts \ + src/graphql/public/root/mutation/lnurl-payment-send.ts \ + src/graphql/public/mutations.ts \ + src/graphql/public/schema.graphql \ + test/flash/unit/app/payments/lnurl-pay.spec.ts \ + test/flash/unit/graphql/public/root/mutation/lnurl-payment-send.spec.ts \ + docs/plans/2026-06-05-eng-406-usdt-lnurl-units.md +``` + +Commit message: + +```bash +git commit -m "fix(ibex): wire LNURL-pay msat conversion" +``` + +Do not push until Dread explicitly asks. diff --git a/src/app/payments/lnurl-pay.ts b/src/app/payments/lnurl-pay.ts new file mode 100644 index 000000000..066fcef0c --- /dev/null +++ b/src/app/payments/lnurl-pay.ts @@ -0,0 +1,59 @@ +import { toMilliSatsFromNumber } from "@domain/bitcoin" +import { InvalidLnurlAmountError } from "@domain/errors" +import { checkedToUsdPaymentAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +import { UsdWalletAmount } from "@app/wallets/usd-wallet-amount" + +export const MSATS_PER_SAT = 1000 +export const IBEX_LNURL_PAY_AMOUNT_MAX_MSAT = 2_147_483_647 + +export const amountMsatFromUsdWalletAmount = async ({ + amount, + btcFromUsd, +}: { + amount: UsdWalletAmount + btcFromUsd: IDealerPriceService["getSatsFromCentsForImmediateSell"] +}): Promise => { + const usdCents = amount instanceof USDTAmount ? amount.asUsdCents() : amount.asCents() + + const usdPaymentAmount = checkedToUsdPaymentAmount(Number(usdCents), WalletCurrency.Usd) + if (usdPaymentAmount instanceof Error) return usdPaymentAmount + + const sats = await btcFromUsd(usdPaymentAmount) + if (sats instanceof Error) return sats + + const wholeSats = Math.floor(Number(sats.amount)) + const msats = wholeSats * MSATS_PER_SAT + + return toMilliSatsFromNumber(msats) +} + +export const validateLnurlPayAmountMsat = ({ + amountMsat, + minSendable, + maxSendable, +}: { + amountMsat: MilliSatoshis + minSendable: number + maxSendable: number +}): true | ValidationError => { + if (!Number.isInteger(amountMsat) || amountMsat <= 0) { + return new InvalidLnurlAmountError("LNURL amount must be positive integer msats") + } + + if (amountMsat % MSATS_PER_SAT !== 0) { + return new InvalidLnurlAmountError("LNURL amount must be a whole-satoshi amount") + } + + if (amountMsat > IBEX_LNURL_PAY_AMOUNT_MAX_MSAT) { + return new InvalidLnurlAmountError("LNURL amount exceeds IBEX int32 limit") + } + + if (amountMsat < minSendable || amountMsat > maxSendable) { + return new InvalidLnurlAmountError( + "LNURL amount outside minSendable/maxSendable bounds", + ) + } + + return true +} diff --git a/src/domain/errors.ts b/src/domain/errors.ts index 9908293d3..d4ea375bb 100644 --- a/src/domain/errors.ts +++ b/src/domain/errors.ts @@ -158,3 +158,4 @@ export class MultipleCurrenciesForSingleCurrencyOperationError extends Validatio export class InvalidIdempotencyKeyError extends ValidationError {} export class InvalidLnurlError extends ValidationError {} +export class InvalidLnurlAmountError extends ValidationError {} diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 6dce737f1..392cfb0e5 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -852,6 +852,9 @@ export const mapError = (error: ApplicationError): CustomApolloError => { case "InvalidLnurlError": return new InvalidLnurlError({ message: error.message, logger: baseLogger }) + case "InvalidLnurlAmountError": + return new InvalidLnurlError({ message: error.message, logger: baseLogger }) + case "CashWalletCutoverInProgressError": message = "Cash Wallet cutover is in progress. Please try again shortly." return new ValidationInternalError({ message, logger: baseLogger }) diff --git a/src/graphql/public/mutations.ts b/src/graphql/public/mutations.ts index 8469c5266..75207efc4 100644 --- a/src/graphql/public/mutations.ts +++ b/src/graphql/public/mutations.ts @@ -16,6 +16,7 @@ import LnNoAmountInvoiceFeeProbeMutation from "@graphql/public/root/mutation/ln- import LnNoAmountUsdInvoiceFeeProbeMutation from "@graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe" import LnNoAmountInvoicePaymentSendMutation from "@graphql/public/root/mutation/ln-noamount-invoice-payment-send" import LnNoAmountUsdInvoicePaymentSendMutation from "@graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send" +import LnurlPaymentSendMutation from "@graphql/public/root/mutation/lnurl-payment-send" import OnChainAddressCreateMutation from "@graphql/public/root/mutation/on-chain-address-create" import OnChainAddressCurrentMutation from "@graphql/public/root/mutation/on-chain-address-current" import UserLoginMutation from "@graphql/shared/root/mutation/user-login" @@ -141,6 +142,7 @@ export const mutationFields = { lnInvoicePaymentSend: LnInvoicePaymentSendMutation, lnNoAmountInvoicePaymentSend: LnNoAmountInvoicePaymentSendMutation, lnNoAmountUsdInvoicePaymentSend: LnNoAmountUsdInvoicePaymentSendMutation, + lnurlPaymentSend: LnurlPaymentSendMutation, onChainAddressCreate: OnChainAddressCreateMutation, onChainAddressCurrent: OnChainAddressCurrentMutation, diff --git a/src/graphql/public/root/mutation/lnurl-payment-send.ts b/src/graphql/public/root/mutation/lnurl-payment-send.ts new file mode 100644 index 000000000..f689b8d81 --- /dev/null +++ b/src/graphql/public/root/mutation/lnurl-payment-send.ts @@ -0,0 +1,220 @@ +import axios from "axios" +import dedent from "dedent" + +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" +import { + amountMsatFromUsdWalletAmount, + validateLnurlPayAmountMsat, +} from "@app/payments/lnurl-pay" +import { usdWalletAmountFromWalletId } from "@app/wallets" +import { PaymentSendStatus } from "@domain/bitcoin/lightning" +import { InvalidLnurlError } from "@domain/errors" +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import PaymentSendPayload from "@graphql/public/types/payload/payment-send" +import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" +import { InputValidationError } from "@graphql/error" +import Lnurl from "@graphql/shared/types/scalar/lnurl" +import Memo from "@graphql/shared/types/scalar/memo" +import WalletId from "@graphql/shared/types/scalar/wallet-id" +import { DealerPriceService } from "@services/dealer-price" +import Ibex from "@services/ibex/client" +import { IbexError } from "@services/ibex/errors" + +type LnurlPayMetadata = { + callback: string + minSendable: number + maxSendable: number + metadata: string + tag?: string +} + +const LnurlPaymentSendInput = GT.Input({ + name: "LnurlPaymentSendInput", + fields: () => ({ + walletId: { + type: GT.NonNull(WalletId), + description: "Wallet ID with sufficient balance. Must belong to the current user.", + }, + lnurl: { + type: GT.NonNull(Lnurl), + description: "LNURL-pay value to decode and pay.", + }, + amount: { + type: GT.NonNull(FractionalCentAmount), + description: "Amount to spend from the USD/USDT wallet, in USD cents.", + }, + memo: { + type: Memo, + description: "Optional memo for the Lightning payment.", + }, + }), +}) + +const isLnurlPayMetadata = (value: unknown): value is LnurlPayMetadata => { + if (!value || typeof value !== "object") return false + const candidate = value as Partial + return ( + typeof candidate.callback === "string" && + Number.isFinite(candidate.minSendable) && + Number.isFinite(candidate.maxSendable) && + typeof candidate.metadata === "string" + ) +} + +const paramsFromMetadata = ({ + callback, + minSendable, + maxSendable, + metadata, +}: LnurlPayMetadata): string => + JSON.stringify({ + callback, + maxSendable, + minSendable, + metadata, + tag: "payRequest", + }) + +const paymentStatusFromIbex = (payment: Record): PaymentSendStatus => { + switch (payment.transaction?.payment?.status?.id) { + case 1: + return PaymentSendStatus.Pending + case 2: + return PaymentSendStatus.Success + case 3: + return PaymentSendStatus.Failure + default: + return PaymentSendStatus.Pending + } +} + +const LnurlPaymentSendMutation = GT.Field< + null, + GraphQLPublicContextAuth, + { + input: { + walletId: WalletId | InputValidationError + lnurl: Lnurl | InputValidationError + amount: FractionalCentAmount | InputValidationError + memo?: Memo | InputValidationError + } + } +>({ + extensions: { + complexity: 120, + }, + type: GT.NonNull(PaymentSendPayload), + description: dedent`Pay a LNURL-pay endpoint using a USD/USDT wallet balance. + The wallet amount is converted to whole-satoshi millisatoshis before calling IBEX.`, + args: { + input: { type: GT.NonNull(LnurlPaymentSendInput) }, + }, + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { + const { walletId, lnurl, amount, memo } = args.input + + if (walletId instanceof InputValidationError) { + return { status: "failed", errors: [{ message: walletId.message }] } + } + if (lnurl instanceof InputValidationError) { + return { status: "failed", errors: [{ message: lnurl.message }] } + } + if (amount instanceof InputValidationError) { + return { status: "failed", errors: [{ message: amount.message }] } + } + if (memo instanceof InputValidationError) { + return { status: "failed", errors: [{ message: memo.message }] } + } + + if (!domainAccount) throw new Error("Authentication required") + + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, + walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(routedWalletId)], + } + } + + const walletAmount = await usdWalletAmountFromWalletId({ + walletId: routedWalletId, + amount: amount.toString(), + }) + if (walletAmount instanceof Error) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(walletAmount)], + } + } + + const decoded = await Ibex.decodeLnurl({ lnurl }) + if (decoded instanceof IbexError) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(decoded)], + } + } + if (!decoded.decodedLnurl) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(new InvalidLnurlError())], + } + } + + const metadataResponse = await axios.get(decoded.decodedLnurl) + const metadata = metadataResponse.data + if (!isLnurlPayMetadata(metadata)) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(new InvalidLnurlError())], + } + } + + const dealer = DealerPriceService() + const amountMsat = await amountMsatFromUsdWalletAmount({ + amount: walletAmount, + btcFromUsd: dealer.getSatsFromCentsForImmediateSell, + }) + if (amountMsat instanceof Error) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(amountMsat)], + } + } + + const validAmount = validateLnurlPayAmountMsat({ + amountMsat, + minSendable: metadata.minSendable, + maxSendable: metadata.maxSendable, + }) + if (validAmount instanceof Error) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(validAmount)], + } + } + + const payment = await Ibex.payToLnurl({ + accountId: routedWalletId, + amountMsat, + params: paramsFromMetadata(metadata), + }) + if (payment instanceof IbexError) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(payment)], + } + } + + return { + errors: [], + status: paymentStatusFromIbex(payment).value, + } + }, +}) + +export default LnurlPaymentSendMutation diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index d7f81eed8..fbc3366e0 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -883,6 +883,20 @@ A bech32-encoded HTTPS/Onion URL that can be interacted with automatically by a """ scalar Lnurl +input LnurlPaymentSendInput { + """Amount to spend from the USD/USDT wallet, in USD cents.""" + amount: FractionalCentAmount! + + """LNURL-pay value to decode and pay.""" + lnurl: Lnurl! + + """Optional memo for the Lightning payment.""" + memo: Memo + + """Wallet ID with sufficient balance. Must belong to the current user.""" + walletId: WalletId! +} + type MapInfo { coordinates: Coordinates! title: String! @@ -1044,6 +1058,12 @@ type Mutation { """ lnUsdInvoiceCreateOnBehalfOfRecipient(input: LnUsdInvoiceCreateOnBehalfOfRecipientInput!): LnInvoicePayload! lnUsdInvoiceFeeProbe(input: LnUsdInvoiceFeeProbeInput!): CentAmountPayload! + + """ + Pay a LNURL-pay endpoint using a USD/USDT wallet balance. + The wallet amount is converted to whole-satoshi millisatoshis before calling IBEX. + """ + lnurlPaymentSend(input: LnurlPaymentSendInput!): PaymentSendPayload! merchantMapSuggest(input: MerchantMapSuggestInput!): MerchantPayload! onChainAddressCreate(input: OnChainAddressCreateInput!): OnChainAddressPayload! onChainAddressCurrent(input: OnChainAddressCurrentInput!): OnChainAddressPayload! diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index cf448778e..b72e571fc 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -254,7 +254,7 @@ const payToLnurl = async ( ): Promise => { return Ibex.payToLnurl({ accountId: args.accountId, - amount: args.send.amount, + amount: args.amountMsat, params: args.params, webhookUrl: WebhookServer.endpoints.onPay.lnurl, webhookSecret: WebhookServer.secret, @@ -328,7 +328,6 @@ const ibexGet = (token: string, path: string) => const ibexPost = (token: string, path: string, body: unknown) => ibexFetch(token, path, { method: "POST", body: JSON.stringify(body) }) - const createIbexAccount = async ( name: string, currencyId: IbexCurrencyId, @@ -336,10 +335,10 @@ const createIbexAccount = async ( try { const token = await getIbexToken() if (token instanceof IbexError) return token - const data = await ibexPost( - token, - "/account/create" - , { name, currencyId }) + const data = await ibexPost(token, "/account/create", { + name, + currencyId, + }) if (data instanceof IbexError) return data return data } catch (err) { diff --git a/src/services/ibex/index.types.d.ts b/src/services/ibex/index.types.d.ts index 01e8c559a..f10631984 100644 --- a/src/services/ibex/index.types.d.ts +++ b/src/services/ibex/index.types.d.ts @@ -13,20 +13,19 @@ interface IbexAccount { type IbexAccountId = WalletId -// type IbexInvoiceArgs = { +// type IbexInvoiceArgs = { // accountId: IbexAccountId, // amount?: IbexAmount // memo: string -// expiration?: Seconds +// expiration?: Seconds // }; -type AccountArgs = { name: string, currency: WalletCurrency } +type AccountArgs = { name: string; currency: WalletCurrency } type IbexTransactionId = string & { readonly brand: unique symbol } - type PayLnurlArgs = { - accountId: IbexAccountId, - send: IbexCurrency, - params: string, // what is this? + accountId: IbexAccountId + amountMsat: MilliSatoshis + params: string // what is this? } // Flash types @@ -34,4 +33,3 @@ type PayLnurlArgs = { // fee: T, // } type Bolt11 = string & { readonly brand: unique symbol } - diff --git a/test/flash/unit/app/payments/lnurl-pay.spec.ts b/test/flash/unit/app/payments/lnurl-pay.spec.ts new file mode 100644 index 000000000..4caad3339 --- /dev/null +++ b/test/flash/unit/app/payments/lnurl-pay.spec.ts @@ -0,0 +1,105 @@ +import { + amountMsatFromUsdWalletAmount, + IBEX_LNURL_PAY_AMOUNT_MAX_MSAT, + MSATS_PER_SAT, + validateLnurlPayAmountMsat, +} from "@app/payments/lnurl-pay" +import { UnknownDealerPriceServiceError } from "@domain/dealer-price" +import { InvalidLnurlAmountError } from "@domain/errors" +import { paymentAmountFromNumber, USDTAmount, WalletCurrency } from "@domain/shared" + +describe("amountMsatFromUsdWalletAmount", () => { + it("converts USDT wallet cents to whole-satoshi millisatoshis using dealer sell pricing", async () => { + const amount = USDTAmount.usdCents("19446") as USDTAmount + const btcFromUsd = jest.fn(async (usdAmount) => { + expect(usdAmount.amount).toBe(19446n) + expect(usdAmount.currency).toBe(WalletCurrency.Usd) + return paymentAmountFromNumber({ + amount: 1234, + currency: WalletCurrency.Btc, + }) as BtcPaymentAmount + }) + + const result = await amountMsatFromUsdWalletAmount({ amount, btcFromUsd }) + + expect(result).toBe(1_234_000) + expect(btcFromUsd).toHaveBeenCalledTimes(1) + }) + + it("converts dealer satoshis to millisatoshis", async () => { + const amount = USDTAmount.usdCents("500") as USDTAmount + const btcFromUsd = jest.fn( + async () => + paymentAmountFromNumber({ + amount: 42, + currency: WalletCurrency.Btc, + }) as BtcPaymentAmount, + ) + + const result = await amountMsatFromUsdWalletAmount({ amount, btcFromUsd }) + + expect(result).toBe(42 * MSATS_PER_SAT) + }) + + it("propagates dealer-price errors", async () => { + const amount = USDTAmount.usdCents("500") as USDTAmount + const error = new UnknownDealerPriceServiceError("dealer unavailable") + const btcFromUsd = jest.fn(async () => error) + + const result = await amountMsatFromUsdWalletAmount({ amount, btcFromUsd }) + + expect(result).toBe(error) + }) +}) + +describe("validateLnurlPayAmountMsat", () => { + it("accepts positive whole-satoshi millisatoshis inside LNURL bounds", () => { + const result = validateLnurlPayAmountMsat({ + amountMsat: 10_000 as MilliSatoshis, + minSendable: 1_000, + maxSendable: 20_000, + }) + + expect(result).toBe(true) + }) + + it("rejects sub-satoshi millisatoshis", () => { + const result = validateLnurlPayAmountMsat({ + amountMsat: 1_500 as MilliSatoshis, + minSendable: 1_000, + maxSendable: 20_000, + }) + + expect(result).toBeInstanceOf(InvalidLnurlAmountError) + }) + + it("rejects values below minSendable after rounding", () => { + const result = validateLnurlPayAmountMsat({ + amountMsat: 1_000 as MilliSatoshis, + minSendable: 2_000, + maxSendable: 20_000, + }) + + expect(result).toBeInstanceOf(InvalidLnurlAmountError) + }) + + it("rejects values above maxSendable after rounding", () => { + const result = validateLnurlPayAmountMsat({ + amountMsat: 21_000 as MilliSatoshis, + minSendable: 1_000, + maxSendable: 20_000, + }) + + expect(result).toBeInstanceOf(InvalidLnurlAmountError) + }) + + it("rejects values above the IBEX int32 request limit", () => { + const result = validateLnurlPayAmountMsat({ + amountMsat: (IBEX_LNURL_PAY_AMOUNT_MAX_MSAT + 1) as MilliSatoshis, + minSendable: 1_000, + maxSendable: IBEX_LNURL_PAY_AMOUNT_MAX_MSAT + 2, + }) + + expect(result).toBeInstanceOf(InvalidLnurlAmountError) + }) +}) diff --git a/test/flash/unit/graphql/public/root/mutation/lnurl-payment-send.spec.ts b/test/flash/unit/graphql/public/root/mutation/lnurl-payment-send.spec.ts new file mode 100644 index 000000000..5b00e4a7e --- /dev/null +++ b/test/flash/unit/graphql/public/root/mutation/lnurl-payment-send.spec.ts @@ -0,0 +1,171 @@ +const mockResolveCashWalletMutationWalletIdForAccount = jest.fn() +const mockUsdWalletAmountFromWalletId = jest.fn() +const mockDecodeLnurl = jest.fn() +const mockPayToLnurl = jest.fn() +const mockGetSatsFromCentsForImmediateSell = jest.fn() +const mockAxiosGet = jest.fn() + +jest.mock("@app/cash-wallet-cutover", () => ({ + resolveCashWalletMutationWalletIdForAccount: ( + ...args: Parameters + ) => mockResolveCashWalletMutationWalletIdForAccount(...args), +})) + +jest.mock("@app/wallets", () => ({ + usdWalletAmountFromWalletId: ( + ...args: Parameters + ) => mockUsdWalletAmountFromWalletId(...args), +})) + +jest.mock("@services/dealer-price", () => ({ + DealerPriceService: jest.fn(() => ({ + getSatsFromCentsForImmediateSell: ( + ...args: Parameters + ) => mockGetSatsFromCentsForImmediateSell(...args), + })), +})) + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + decodeLnurl: (...args: Parameters) => + mockDecodeLnurl(...args), + payToLnurl: (...args: Parameters) => mockPayToLnurl(...args), + }, +})) + +jest.mock("axios", () => ({ + get: (...args: Parameters) => mockAxiosGet(...args), +})) + +import LnurlPaymentSendMutation from "@graphql/public/root/mutation/lnurl-payment-send" +import { paymentAmountFromNumber, USDTAmount, WalletCurrency } from "@domain/shared" +import { IbexError } from "@services/ibex/errors" + +const walletId = "11111111-1111-4111-8111-111111111111" as WalletId +const routedWalletId = "22222222-2222-4222-8222-222222222222" as WalletId +const domainAccount = { id: "account-id" } as Account +const client = { + cashWalletPresentation: "usdt", + hasUsdtCashWalletSupport: true, +} as const + +type MutationResult = { + status: string + errors: { message: string }[] +} + +const resolveMutation = (overrides = {}) => + LnurlPaymentSendMutation.resolve?.( + null, + { + input: { + walletId, + lnurl: "LNURL1DP68GURN8GHJ7MRWW4EXCTN" as Lnurl, + amount: 19446 as FractionalCentAmount, + memo: "memo" as Memo, + ...overrides, + }, + }, + { + domainAccount, + cashWalletClientCapabilities: client, + } as GraphQLPublicContextAuth, + {} as never, + ) as Promise + +describe("LnurlPaymentSendMutation", () => { + beforeEach(() => { + jest.clearAllMocks() + mockResolveCashWalletMutationWalletIdForAccount.mockResolvedValue(routedWalletId) + mockUsdWalletAmountFromWalletId.mockResolvedValue( + USDTAmount.usdCents("19446") as USDTAmount, + ) + mockDecodeLnurl.mockResolvedValue({ + decodedLnurl: "https://lnurl.example/.well-known/lnurlp/alice", + }) + mockAxiosGet.mockResolvedValue({ + data: { + callback: "https://lnurl.example/callback", + minSendable: 1_000, + maxSendable: 2_000_000, + metadata: '[["text/plain","alice"]]', + tag: "payRequest", + }, + }) + mockGetSatsFromCentsForImmediateSell.mockResolvedValue( + paymentAmountFromNumber({ + amount: 1234, + currency: WalletCurrency.Btc, + }), + ) + mockPayToLnurl.mockResolvedValue({ + transaction: { payment: { status: { id: 2 } } }, + }) + }) + + it("decodes LNURL metadata, converts USDT wallet amount to msats, and pays IBEX", async () => { + const result = await resolveMutation() + + expect(mockResolveCashWalletMutationWalletIdForAccount).toHaveBeenCalledWith({ + account: domainAccount, + walletId, + client, + }) + expect(mockUsdWalletAmountFromWalletId).toHaveBeenCalledWith({ + walletId: routedWalletId, + amount: "19446", + }) + expect(mockDecodeLnurl).toHaveBeenCalledWith({ + lnurl: "LNURL1DP68GURN8GHJ7MRWW4EXCTN", + }) + expect(mockAxiosGet).toHaveBeenCalledWith( + "https://lnurl.example/.well-known/lnurlp/alice", + ) + expect(mockPayToLnurl).toHaveBeenCalledWith({ + accountId: routedWalletId, + amountMsat: 1_234_000, + params: JSON.stringify({ + callback: "https://lnurl.example/callback", + maxSendable: 2_000_000, + minSendable: 1_000, + metadata: '[["text/plain","alice"]]', + tag: "payRequest", + }), + }) + expect(result).toEqual({ errors: [], status: "success" }) + }) + + it("rejects converted msats below LNURL minSendable before calling IBEX pay", async () => { + mockAxiosGet.mockResolvedValueOnce({ + data: { + callback: "https://lnurl.example/callback", + minSendable: 2_000, + maxSendable: 2_000_000, + metadata: '[["text/plain","alice"]]', + tag: "payRequest", + }, + }) + mockGetSatsFromCentsForImmediateSell.mockResolvedValueOnce( + paymentAmountFromNumber({ + amount: 1, + currency: WalletCurrency.Btc, + }), + ) + + const result = await resolveMutation() + + expect(mockPayToLnurl).not.toHaveBeenCalled() + expect(result?.status).toBe("failed") + expect(result?.errors[0].message).toMatch(/minSendable|maxSendable/i) + }) + + it("maps IBEX pay failures into payload errors", async () => { + mockPayToLnurl.mockResolvedValueOnce(new IbexError(new Error("ibex failed"))) + + const result = await resolveMutation() + + expect(result?.status).toBe("failed") + expect(result?.errors[0].message).toBeTruthy() + }) +}) From ca878ae5cc09e4f36b14d46e5a03b528bd51f1f4 Mon Sep 17 00:00:00 2001 From: Island Bitcoin <34528298+islandbitcoin@users.noreply.github.com> Date: Sat, 6 Jun 2026 21:24:57 -0700 Subject: [PATCH 29/43] feat(bridge): push notification on deposit settlement [ENG-275] (#392) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fires a best-effort push when a Bridge USDT deposit settles via the IBEX crypto.received webhook (the must-have launch gate). Mirrors the existing sendBridgeWithdrawalNotification pattern: - new src/app/bridge/send-deposit-notification.ts - wired at the crypto-receive settlement success (idempotent — inside the per-txHash lock; fires once) - notification.bridgeDeposit i18n phrases (en + es) Withdrawal-completion push already exists (transfer.ts) — this completes the deposit side. IBEX→USD currency display mirrors the withdrawal notif. Co-authored-by: Dread --- src/app/bridge/send-deposit-notification.ts | 98 +++++++++++++++++++ src/config/locales/en.json | 4 + src/config/locales/es.json | 4 + .../webhook-server/routes/crypto-receive.ts | 7 ++ 4 files changed, 113 insertions(+) create mode 100644 src/app/bridge/send-deposit-notification.ts diff --git a/src/app/bridge/send-deposit-notification.ts b/src/app/bridge/send-deposit-notification.ts new file mode 100644 index 000000000..9845f676a --- /dev/null +++ b/src/app/bridge/send-deposit-notification.ts @@ -0,0 +1,98 @@ +import { getI18nInstance } from "@config" +import { checkedToAccountId } from "@domain/accounts" +import { getLanguageOrDefault } from "@domain/locale" +import { + DeviceTokensNotRegisteredNotificationsServiceError, + FlashNotificationCategories, + NotificationsServiceError, +} from "@domain/notifications" +import { removeDeviceTokens } from "@app/users/remove-device-tokens" +import { baseLogger } from "@services/logger" +import { AccountsRepository } from "@services/mongoose/accounts" +import { UsersRepository } from "@services/mongoose/users" +import { + PushNotificationsService, + SendFilteredPushNotificationStatus, +} from "@services/notifications/push-notifications" + +const i18n = getI18nInstance() + +const formatDepositAmount = (amount: string, currency: string): string => + `${amount} ${currency.toUpperCase()}` + +export const sendBridgeDepositNotification = async ({ + accountId: accountIdRaw, + amount, + currency, +}: { + accountId: string + amount: string + currency: string +}): Promise => { + const accountId = checkedToAccountId(accountIdRaw) + if (accountId instanceof Error) return accountId + + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + + const user = await UsersRepository().findById(account.kratosUserId) + if (user instanceof Error) return user + + const locale = getLanguageOrDefault(user.language) + const formattedAmount = formatDepositAmount(amount, currency) + const phraseBase = "notification.bridgeDeposit" + + const title = i18n.__({ phrase: `${phraseBase}.title`, locale }) + const body = i18n.__( + { phrase: `${phraseBase}.body`, locale }, + { amount: formattedAmount }, + ) + + const result = await PushNotificationsService().sendFilteredNotification({ + deviceTokens: user.deviceTokens, + title, + body, + notificationCategory: FlashNotificationCategories.Payments, + notificationSettings: account.notificationSettings, + data: { + type: "bridge_deposit_completed", + amount, + currency: currency == "usdt" ? "USD" : currency.toUpperCase(), + }, + }) + + if (result instanceof NotificationsServiceError) return result + + if (result.status === SendFilteredPushNotificationStatus.Filtered) { + return true + } + + return true +} + +export const sendBridgeDepositNotificationBestEffort = async ( + args: Parameters[0], +): Promise => { + const result = await sendBridgeDepositNotification(args) + + if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) { + const accountId = checkedToAccountId(args.accountId) + if (accountId instanceof Error) return + + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return + + await removeDeviceTokens({ + userId: account.kratosUserId, + deviceTokens: result.tokens, + }) + return + } + + if (result instanceof Error) { + baseLogger.warn( + { accountId: args.accountId, error: result }, + "Failed to send Bridge deposit push notification", + ) + } +} diff --git a/src/config/locales/en.json b/src/config/locales/en.json index 55eafc82d..da4398833 100644 --- a/src/config/locales/en.json +++ b/src/config/locales/en.json @@ -41,6 +41,10 @@ "body": "Your cashout of {{amount}} has been deposited to your bank account.", "title": "Cashout" }, + "bridgeDeposit": { + "body": "Your deposit of {{amount}} has been added to your account.", + "title": "Deposit received" + }, "bridgeWithdrawal": { "completed": { "body": "Your withdrawal of {{amount}} has been sent to your bank account.", diff --git a/src/config/locales/es.json b/src/config/locales/es.json index 7325c9fe0..aa4cef58f 100644 --- a/src/config/locales/es.json +++ b/src/config/locales/es.json @@ -37,6 +37,10 @@ "title": "Transacción {{walletCurrency}}" } }, + "bridgeDeposit": { + "body": "Su depósito de {{amount}} se agregó a su cuenta.", + "title": "Depósito recibido" + }, "bridgeWithdrawal": { "completed": { "body": "Su retiro de {{amount}} se envió a su cuenta bancaria.", diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index cb5e46666..7a45d129c 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -2,6 +2,7 @@ import express, { Request, Response } from "express" import { AccountsRepository } from "@services/mongoose/accounts" import { createIbexCryptoReceive } from "@services/mongoose/ibex-crypto-receive-log" import { listWalletsByAccountId } from "@app/wallets" +import { sendBridgeDepositNotificationBestEffort } from "@app/bridge/send-deposit-notification" import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" import { LockService } from "@services/lock" @@ -125,6 +126,12 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return { status: "error", code: "erpnext_audit_failed" } as CryptoReceiveResult } + await sendBridgeDepositNotificationBestEffort({ + accountId: account.id, + amount: String(usdtAmount.asNumber()), + currency: normalizedCurrency, + }) + return { status: "success" } as CryptoReceiveResult } catch (error) { baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") From a3acbc121509cfba86d972cbec3028ddc9330977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olaniran=20=E2=9A=A1?= <93789719+heyolaniran@users.noreply.github.com> Date: Sun, 7 Jun 2026 20:18:41 -0700 Subject: [PATCH 30/43] feat(bridge): split withdrawal into request, confirm, and cancel steps (#387) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(bridge): split withdrawal into request, confirm, and cancel steps Introduce a two-step off-ramp flow so users review a pending withdrawal before Bridge is called, with deduplication, cancellation, and push notifications for the cancelled outcome. * docs(bridge): document two-step withdrawal flow in FLOWS and API Update bridge-integration docs to describe bridgeRequestWithdrawal, bridgeInitiateWithdrawal(withdrawalId), and bridgeCancelWithdrawalRequest. * chore(bruno): add GraphQL requests for bridge withdrawal flow Add Bruno mutations and query for request, initiate, cancel, and fetch pending withdrawal, with local env vars for bridgeWithdrawalId. * fix(config): add external_account webhook public key to dev config Required by the Bridge webhook schema so local write-sdl and config validation succeed. * chore(graphql): regenerate supergraph for bridge withdrawal mutations Add bridgeRequestWithdrawal, bridgeCancelWithdrawalRequest, and bridgeWithdrawalRequest to the federated supergraph schema. * fix(bridge): map withdrawal request errors to Bridge codes BridgeWithdrawalNotFoundError and BridgeWithdrawalAlreadyInitiatedError now map to dedicated BRIDGE_WITHDRAWAL_NOT_FOUND and BRIDGE_WITHDRAWAL_ALREADY_INITIATED codes instead of the generic INVALID_INPUT. Tests updated to assert the correct Bridge-specific codes. * test(bridge): add resolver coverage for request/initiate/cancel withdrawal Eight resolver-layer tests across the three bridge withdrawal mutations: happy-path delegation to BridgeService and the two new Bridge-specific error codes (BRIDGE_WITHDRAWAL_NOT_FOUND, BRIDGE_WITHDRAWAL_ALREADY_INITIATED) flowing through the full error-map pipeline. * fix(bridge): resolve duplicate const declarations in deposit webhook handler Auto-merge conflict left two `const lockKey`/`const lockResult` pairs in the same function scope. Rename the audit-idempotency pair to `auditLockKey`/`auditLockResult` to fix TS2451. * fix(graphql): align BridgeWithdrawal type with new id/status contract The type had transferId: NonNull and state: NonNull left over from the old single-step flow. All new resolvers (request/initiate/cancel mutations plus bridgeWithdrawalRequest query) return id + status, so those NonNull fields were always null at runtime. Changes: - bridge-withdrawal.ts: transferId → id (NonNull), state removed, bridgeTransferId added (nullable) - index.ts: WithdrawalResult and InitiateWithdrawalResult updated to match; getWithdrawals map rewritten (id/status/bridgeTransferId/ externalAccountId) and the always-true filter removed - bridge-withdrawal-request.ts: add bridgeTransferId to return object - bridge-contract.spec.ts: assertions updated to new field set * test(bridge): cover getWithdrawals mapping to id/status/bridgeTransferId Five tests asserting that getWithdrawals emits the new GQL-facing shape (id, status, bridgeTransferId) and not the old transferId/state fields. Also adds findWithdrawalsByAccountId to the bridge-accounts mock so the describe block can exercise the mapping path. * feat(bridge): add submitted status for initiated withdrawals Introduces an intermediary "submitted" status so the UI can distinguish between a pending withdrawal (awaiting user confirmation) and one that has already been submitted to Bridge (awaiting Bridge processing). - schema.ts: adds "submitted" to IBridgeWithdrawalRecord.status union and Mongoose enum - bridge-accounts.ts: updateWithdrawalTransferId now sets status → "submitted" atomically with the bridgeTransferId write; updateWithdrawalStatus query filter changed from "pending" to "submitted" so webhook-driven transitions (completed/failed) match the correct row - index.spec.ts: mock fixture updated; initiate test now asserts result.status === "submitted" and result.bridgeTransferId is set * fix(bridge): restore getWithdrawals filter with correct && operator The original filter `w.bridgeTransferId !== null || w.bridgeTransferId !== undefined` was a tautology (always true), so pending rows with no bridgeTransferId leaked through — surfacing a null id on a NonNull field when transferId was the source. The filter is now `!== null && !== undefined`, preserving the intended invariant: getWithdrawals (bridgeWithdrawals query) returns only rows that have been submitted to Bridge. Clients use bridgeWithdrawalRequest(id) to inspect individual pending/cancelled rows. Tests updated to assert the exclusion of pending/cancelled rows and the inclusion of submitted/completed/failed rows. * fix(graphql): map bridge withdrawal errors through bridgeGqlError Use BRIDGE_WITHDRAWAL_NOT_FOUND and BRIDGE_WITHDRAWAL_ALREADY_INITIATED via the shared Bridge error helper so these codes match the documented contract and other Bridge GraphQL mappings. * test(bridge): cover withdrawal request confirm and cancel flow Add chained service tests for request, dedupe, initiate, cancel, and error guards, and tighten resolver assertions for pending/submitted status and cancelled delegation. * chore(graphql): regenerate schema for withdrawal request flow Align BridgeWithdrawal SDL with id/status/bridgeTransferId and recompose the federated supergraph. * test(bridge): guard getWithdrawals id/status GraphQL contract Add return-shape regression tests so getWithdrawals and initiateWithdrawal emit id/status (not legacy transferId/state) and exclude rows without a bridgeTransferId. Add bridgeWithdrawals resolver coverage for passthrough. * chore(bridge): fix surgical lint issues in withdrawal flow * chore(bridge): finish withdrawal lint and docs cleanup --- dev/apollo-federation/supergraph.graphql | 39 +- .../Flash GraphQL API/environments/local.bru | 2 + .../bridgeCancelWithdrawalRequest.bru | 45 ++ .../mutations/bridgeInitiateWithdrawal.bru | 45 ++ .../mutations/bridgeRequestWithdrawal.bru | 47 ++ .../token/queries/bridgeWithdrawalRequest.bru | 40 + docs/bridge-integration/API.md | 118 ++- docs/bridge-integration/FLOWS.md | 42 +- .../bridge/send-withdrawal-notification.ts | 2 +- src/config/locales/en.json | 4 + src/config/locales/es.json | 4 + src/graphql/error-map.ts | 26 +- src/graphql/public/mutations.ts | 4 + src/graphql/public/queries.ts | 2 + .../bridge-cancel-withdrawal-request.ts | 52 ++ .../mutation/bridge-initiate-withdrawal.ts | 36 +- .../mutation/bridge-request-withdrawal.ts | 75 ++ .../root/query/bridge-withdrawal-request.ts | 40 + src/graphql/public/schema.graphql | 31 +- .../public/types/object/bridge-withdrawal.ts | 6 +- src/services/bridge/errors.ts | 12 + src/services/bridge/index.ts | 263 +++++- .../bridge/webhook-server/routes/deposit.ts | 15 +- src/services/mongoose/bridge-accounts.ts | 26 +- src/services/mongoose/schema.ts | 4 +- .../send-withdrawal-notification.spec.ts | 29 + test/flash/unit/graphql/error-map.spec.ts | 18 + .../root/mutation/bridge-withdrawal.spec.ts | 214 +++++ .../root/query/bridge-withdrawals.spec.ts | 62 ++ .../types/object/bridge-contract.spec.ts | 42 +- test/flash/unit/services/bridge/index.spec.ts | 757 +++++++++++++++--- .../services/bridge/return-shapes.spec.ts | 242 ++++++ 32 files changed, 2112 insertions(+), 232 deletions(-) create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeCancelWithdrawalRequest.bru create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateWithdrawal.bru create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeRequestWithdrawal.bru create mode 100644 dev/bruno/Flash GraphQL API/token/queries/bridgeWithdrawalRequest.bru create mode 100644 src/graphql/public/root/mutation/bridge-cancel-withdrawal-request.ts create mode 100644 src/graphql/public/root/mutation/bridge-request-withdrawal.ts create mode 100644 src/graphql/public/root/query/bridge-withdrawal-request.ts create mode 100644 test/flash/unit/graphql/public/root/mutation/bridge-withdrawal.spec.ts create mode 100644 test/flash/unit/graphql/public/root/query/bridge-withdrawals.spec.ts create mode 100644 test/flash/unit/services/bridge/return-shapes.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 8816d85df..f4545b6bb 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -271,6 +271,19 @@ type BridgeAddExternalAccountPayload externalAccount: BridgeExternalAccountLink } +input BridgeCancelWithdrawalRequestInput + @join__type(graph: PUBLIC) +{ + withdrawalId: ID! +} + +type BridgeCancelWithdrawalRequestPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + withdrawal: BridgeWithdrawal +} + type BridgeCreateVirtualAccountPayload @join__type(graph: PUBLIC) { @@ -312,8 +325,7 @@ type BridgeInitiateKycPayload input BridgeInitiateWithdrawalInput @join__type(graph: PUBLIC) { - amount: String! - externalAccountId: ID! + withdrawalId: ID! } type BridgeInitiateWithdrawalPayload @@ -330,6 +342,20 @@ type BridgeKycLink tosLink: String! } +input BridgeRequestWithdrawalInput + @join__type(graph: PUBLIC) +{ + amount: String! + externalAccountId: ID! +} + +type BridgeRequestWithdrawalPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + withdrawal: BridgeWithdrawal +} + type BridgeVirtualAccount @join__type(graph: PUBLIC) { @@ -348,11 +374,13 @@ type BridgeWithdrawal @join__type(graph: PUBLIC) { amount: String! + bridgeTransferId: String createdAt: String! currency: String! + externalAccountId: String failureReason: String - state: String! - transferId: ID! + id: ID! + status: String! } """ @@ -1232,9 +1260,11 @@ type Mutation accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! bridgeAddExternalAccount: BridgeAddExternalAccountPayload! + bridgeCancelWithdrawalRequest(input: BridgeCancelWithdrawalRequestInput!): BridgeCancelWithdrawalRequestPayload! bridgeCreateVirtualAccount: BridgeCreateVirtualAccountPayload! bridgeInitiateKyc(input: BridgeInitiateKycInput!): BridgeInitiateKycPayload! bridgeInitiateWithdrawal(input: BridgeInitiateWithdrawalInput!): BridgeInitiateWithdrawalPayload! + bridgeRequestWithdrawal(input: BridgeRequestWithdrawalInput!): BridgeRequestWithdrawalPayload! businessAccountUpgradeRequest(input: BusinessAccountUpgradeRequestInput!): AccountUpgradePayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! @@ -1700,6 +1730,7 @@ type Query bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount + bridgeWithdrawalRequest(id: ID!): BridgeWithdrawal bridgeWithdrawals: [BridgeWithdrawal] btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] diff --git a/dev/bruno/Flash GraphQL API/environments/local.bru b/dev/bruno/Flash GraphQL API/environments/local.bru index 1cda70a56..55107b224 100644 --- a/dev/bruno/Flash GraphQL API/environments/local.bru +++ b/dev/bruno/Flash GraphQL API/environments/local.bru @@ -10,4 +10,6 @@ vars { walletIdUsd: c593736e-5a58-42e4-93fa-dc895856c1f1 userEmail: mauriente@gmail.com userFullName: maurientes + bridgeExternalAccountId: + bridgeWithdrawalId: } diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeCancelWithdrawalRequest.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeCancelWithdrawalRequest.bru new file mode 100644 index 000000000..cbcfbc6fa --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeCancelWithdrawalRequest.bru @@ -0,0 +1,45 @@ +meta { + name: bridgeCancelWithdrawalRequest + type: graphql + seq: 39 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeCancelWithdrawalRequest($input: BridgeCancelWithdrawalRequestInput!) { + bridgeCancelWithdrawalRequest(input: $input) { + errors { + message + } + withdrawal { + id + amount + currency + status + createdAt + } + } + } +} + +body:graphql:vars { + { + "input": { + "withdrawalId": "{{bridgeWithdrawalId}}" + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateWithdrawal.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateWithdrawal.bru new file mode 100644 index 000000000..e938f7ddd --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateWithdrawal.bru @@ -0,0 +1,45 @@ +meta { + name: bridgeInitiateWithdrawal + type: graphql + seq: 38 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeInitiateWithdrawal($input: BridgeInitiateWithdrawalInput!) { + bridgeInitiateWithdrawal(input: $input) { + errors { + message + } + withdrawal { + id + amount + currency + status + createdAt + } + } + } +} + +body:graphql:vars { + { + "input": { + "withdrawalId": "{{bridgeWithdrawalId}}" + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeRequestWithdrawal.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeRequestWithdrawal.bru new file mode 100644 index 000000000..49fe78ba9 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeRequestWithdrawal.bru @@ -0,0 +1,47 @@ +meta { + name: bridgeRequestWithdrawal + type: graphql + seq: 37 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeRequestWithdrawal($input: BridgeRequestWithdrawalInput!) { + bridgeRequestWithdrawal(input: $input) { + errors { + message + } + withdrawal { + id + amount + currency + externalAccountId + status + createdAt + } + } + } +} + +body:graphql:vars { + { + "input": { + "amount": "50.00", + "externalAccountId": "{{bridgeExternalAccountId}}" + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/queries/bridgeWithdrawalRequest.bru b/dev/bruno/Flash GraphQL API/token/queries/bridgeWithdrawalRequest.bru new file mode 100644 index 000000000..68a428f40 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/queries/bridgeWithdrawalRequest.bru @@ -0,0 +1,40 @@ +meta { + name: bridgeWithdrawalRequest + type: graphql + seq: 33 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + query BridgeWithdrawalRequest($id: ID!) { + bridgeWithdrawalRequest(id: $id) { + id + amount + currency + externalAccountId + status + failureReason + createdAt + } + } +} + +body:graphql:vars { + { + "id": "{{bridgeWithdrawalId}}" + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/docs/bridge-integration/API.md b/docs/bridge-integration/API.md index 7628b9b90..f43046226 100644 --- a/docs/bridge-integration/API.md +++ b/docs/bridge-integration/API.md @@ -83,9 +83,43 @@ mutation BridgeAddExternalAccount { --- +### `bridgeRequestWithdrawal` + +Validates a withdrawal and creates a pending record for the confirmation screen. Does **not** call the Bridge API. If an identical pending request already exists (same account, amount, and external account), the existing record is returned. + +**Request:** +```graphql +mutation BridgeRequestWithdrawal($input: BridgeRequestWithdrawalInput!) { + bridgeRequestWithdrawal(input: $input) { + errors { + message + } + withdrawal { + id + amount + currency + externalAccountId + status + createdAt + } + } +} +``` + +**Input:** +- `amount`: String representation of the amount (e.g., "100.00"). Must be positive with at most 6 decimal places and above the configured minimum. +- `externalAccountId`: The ID of the linked bank account. + +**Response:** +- `id`: MongoDB withdrawal record ID — pass this to `bridgeInitiateWithdrawal` or `bridgeCancelWithdrawalRequest`. +- `status`: Always `"pending"` on success. +- `externalAccountId`: Linked bank account used for the withdrawal. + +--- + ### `bridgeInitiateWithdrawal` -Initiates a withdrawal from the user's USDT balance to a linked external bank account. +Submits a previously requested withdrawal to Bridge. Re-checks USDT balance at execution time. **Request:** ```graphql @@ -95,22 +129,61 @@ mutation BridgeInitiateWithdrawal($input: BridgeInitiateWithdrawalInput!) { message } withdrawal { - transferId + id amount currency - state + status + createdAt } } } ``` **Input:** -- `amount`: String representation of the amount (e.g., "100.00"). -- `externalAccountId`: The ID of the linked bank account. +- `withdrawalId`: The `id` returned by `bridgeRequestWithdrawal`. **Response:** -- `transferId`: Unique identifier for the transfer. -- `state`: Current state of the transfer (e.g., "pending", "processing"). +- `id`: Withdrawal record ID. +- `status`: Withdrawal status after Bridge transfer creation (typically `"pending"` until the webhook settles). + +**Errors:** +- `BridgeWithdrawalNotFoundError`: Withdrawal ID does not exist or belongs to another account. +- `BridgeWithdrawalAlreadyInitiatedError`: Withdrawal was already submitted to Bridge. +- `BridgeInsufficientFundsError`: Balance dropped between request and confirm. + +--- + +### `bridgeCancelWithdrawalRequest` + +Cancels a pending withdrawal before it has been submitted to Bridge. + +**Request:** +```graphql +mutation BridgeCancelWithdrawalRequest($input: BridgeCancelWithdrawalRequestInput!) { + bridgeCancelWithdrawalRequest(input: $input) { + errors { + message + } + withdrawal { + id + amount + currency + status + createdAt + } + } +} +``` + +**Input:** +- `withdrawalId`: The `id` returned by `bridgeRequestWithdrawal`. + +**Response:** +- `status`: `"cancelled"` on success. + +**Errors:** +- `BridgeWithdrawalNotFoundError`: Withdrawal ID does not exist or belongs to another account. +- `BridgeWithdrawalAlreadyInitiatedError`: Transfer was already submitted to Bridge and cannot be cancelled. --- @@ -171,18 +244,41 @@ query BridgeExternalAccounts { --- +### `bridgeWithdrawalRequest` + +Fetches a single withdrawal record by ID for the confirmation screen. Returns `null` if the ID does not exist or belongs to another account (no cross-account leakage). + +**Request:** +```graphql +query BridgeWithdrawalRequest($id: ID!) { + bridgeWithdrawalRequest(id: $id) { + id + amount + currency + externalAccountId + status + failureReason + createdAt + } +} +``` + +--- + ### `bridgeWithdrawals` -Lists the user's withdrawal history. +Lists the user's withdrawal history (submitted transfers only). **Request:** ```graphql query BridgeWithdrawals { bridgeWithdrawals { - transferId + id amount currency - state + status + bridgeTransferId + failureReason createdAt } } @@ -200,6 +296,8 @@ query BridgeWithdrawals { | `BRIDGE_KYC_REJECTED` | KYC was rejected. | | `BRIDGE_KYC_OFFBOARDED` | Bridge offboarded the customer. | | `BRIDGE_CUSTOMER_NOT_FOUND` | Bridge customer record not found for the user. | +| `BRIDGE_WITHDRAWAL_NOT_FOUND` | Withdrawal request not found or does not belong to the caller. | +| `BRIDGE_WITHDRAWAL_ALREADY_INITIATED` | Withdrawal was already submitted to Bridge. | | `BRIDGE_INSUFFICIENT_FUNDS` | USDT balance is insufficient for the withdrawal. | | `BRIDGE_RATE_LIMIT` | Bridge rate-limited the request. | | `BRIDGE_TIMEOUT` | Bridge request timed out. | diff --git a/docs/bridge-integration/FLOWS.md b/docs/bridge-integration/FLOWS.md index 9b76cffe8..f147d3c4b 100644 --- a/docs/bridge-integration/FLOWS.md +++ b/docs/bridge-integration/FLOWS.md @@ -96,17 +96,23 @@ User Flash App Flash Backend Bridge.xyz | | | 13. ext_acc.verified| | | | |<--------------------| | | 14. Withdraw | | | | - |----------------->| 15. bridgeInitWith| | | - | |------------------>| 16. Create Transfer | | - | | |-------------------->| | - | | 17. Pending | | | + |----------------->| 15. bridgeRequest | | | + | | Withdrawal | | | + | |------------------>| 16. Store pending | | + | | 17. Confirm screen| withdrawal | | + | |<------------------| | | + | 18. Confirm | | | | + |----------------->| 19. bridgeInitWith| | | + | | (withdrawalId)| 20. Create Transfer | | + | |------------------>|-------------------->| | + | | 21. Pending | | | |<-----------------| | | | - | | | | 18. Convert USDT | - | | | | 19. Send ACH | + | | | | 22. Convert USDT | + | | | | 23. Send ACH | | | | |------------------>| - | | | 20. trans.completed | | + | | | 24. trans.completed | | | | |<--------------------| | - | 21. Funds Arrive | | | | + | 25. Funds Arrive | | | | |<-------------------------------------------------------------------------------| ``` @@ -118,14 +124,18 @@ User Flash App Flash Backend Bridge.xyz 4. **Redirect**: App opens the Bridge/Plaid flow. 5. **Authentication**: User logs into their bank and selects an account. 6. **Verification Webhook**: Bridge notifies Flash when the external account is verified. -7. **Initiate Withdrawal**: User enters amount and selects the linked bank account. -8. **GraphQL Mutation**: App calls `bridgeInitiateWithdrawal`. -9. **Bridge Transfer**: Flash creates a transfer in Bridge from the user's Tron address to the external account. -10. **Confirmation**: App shows the withdrawal as "Pending". -11. **Conversion**: Bridge converts USDT from the user's balance to USD. -12. **ACH Transfer**: Bridge sends USD to the user's bank via ACH. -13. **Transfer Webhook**: Bridge sends `transfer.completed` webhook to Flash. -14. **Completion**: User receives funds in their bank account (usually 1-3 business days). +7. **Request Withdrawal**: User enters amount and selects the linked bank account. +8. **GraphQL Mutation**: App calls `bridgeRequestWithdrawal` with `amount` and `externalAccountId`. +9. **Validation**: Flash checks USDT balance, account level, and external account ownership/verification. A `pending` withdrawal record is stored in MongoDB. If an identical pending request already exists (same account, amount, and bank account), the existing record is reused. +10. **Confirmation Screen**: App fetches the pending withdrawal via `bridgeWithdrawalRequest(id)` and displays amount, bank account, and fees for user review. +11. **User Confirms or Cancels**: + - **Confirm**: App calls `bridgeInitiateWithdrawal` with `withdrawalId`. Flash re-checks balance, then creates a transfer in Bridge from the user's Ethereum USDT address to the external account. + - **Cancel**: App calls `bridgeCancelWithdrawalRequest` with `withdrawalId`. The pending record is marked `cancelled` and a push notification is sent. +12. **Pending State**: After initiation, app shows the withdrawal as "Pending". +13. **Conversion**: Bridge converts USDT from the user's balance to USD. +14. **ACH Transfer**: Bridge sends USD to the user's bank via ACH. +15. **Transfer Webhook**: Bridge sends `transfer.completed` (or failure) webhook to Flash. +16. **Completion**: User receives funds in their bank account (usually 1-3 business days). ## Fee Structure diff --git a/src/app/bridge/send-withdrawal-notification.ts b/src/app/bridge/send-withdrawal-notification.ts index cc5c7ae29..07003a18f 100644 --- a/src/app/bridge/send-withdrawal-notification.ts +++ b/src/app/bridge/send-withdrawal-notification.ts @@ -20,7 +20,7 @@ const i18n = getI18nInstance() const formatWithdrawalAmount = (amount: string, currency: string): string => `${amount} ${currency.toUpperCase()}` -export type BridgeWithdrawalNotificationOutcome = "completed" | "failed" +export type BridgeWithdrawalNotificationOutcome = "completed" | "failed" | "cancelled" export const sendBridgeWithdrawalNotification = async ({ accountId: accountIdRaw, diff --git a/src/config/locales/en.json b/src/config/locales/en.json index da4398833..2b286f01c 100644 --- a/src/config/locales/en.json +++ b/src/config/locales/en.json @@ -46,6 +46,10 @@ "title": "Deposit received" }, "bridgeWithdrawal": { + "cancelled": { + "body": "Your withdrawal of {{amount}} has been cancelled.", + "title": "Withdrawal cancelled" + }, "completed": { "body": "Your withdrawal of {{amount}} has been sent to your bank account.", "title": "Withdrawal complete" diff --git a/src/config/locales/es.json b/src/config/locales/es.json index aa4cef58f..9d3530d0a 100644 --- a/src/config/locales/es.json +++ b/src/config/locales/es.json @@ -42,6 +42,10 @@ "title": "Depósito recibido" }, "bridgeWithdrawal": { + "cancelled": { + "body": "Su retiro de {{amount}} ha sido cancelado.", + "title": "Retiro cancelado" + }, "completed": { "body": "Su retiro de {{amount}} se envió a su cuenta bancaria.", "title": "Retiro completado" diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 392cfb0e5..9defa8f00 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -555,6 +555,22 @@ export const mapError = (error: ApplicationError): CustomApolloError => { message, }) + case "BridgeWithdrawalNotFoundError": + message = error.message || "Withdrawal request not found" + return bridgeGqlError({ + code: "BRIDGE_WITHDRAWAL_NOT_FOUND", + message, + }) + + case "BridgeWithdrawalAlreadyInitiatedError": + message = + error.message || + "Withdrawal has already been submitted to Bridge and cannot be cancelled" + return bridgeGqlError({ + code: "BRIDGE_WITHDRAWAL_ALREADY_INITIATED", + message, + }) + case "BridgeRateLimitError": message = "Rate limit exceeded, please try again later" return bridgeGqlError({ @@ -794,9 +810,8 @@ export const mapError = (error: ApplicationError): CustomApolloError => { case "InvalidCarrierForPhoneMetadataError": case "InvalidCarrierTypeForPhoneMetadataError": case "InvalidCountryCodeForPhoneMetadataError": - message = `Unexpected error occurred, please try again or contact support if it persists (code: ${ - error.name - }${error.message ? ": " + error.message : ""})` + message = `Unexpected error occurred, please try again or contact support if it persists (code: ${error.name + }${error.message ? ": " + error.message : ""})` return new UnexpectedClientError({ message, logger: baseLogger }) case "MissingSessionIdError": @@ -892,9 +907,8 @@ export const mapError = (error: ApplicationError): CustomApolloError => { return new ValidationInternalError({ message, logger: baseLogger }) case "UnknownCaptchaError": - message = `Unknown error occurred (code: ${error.name}${ - error.message ? ": " + error.message : "" - })` + message = `Unknown error occurred (code: ${error.name}${error.message ? ": " + error.message : "" + })` return new UnknownClientError({ message, logger: baseLogger }) default: diff --git a/src/graphql/public/mutations.ts b/src/graphql/public/mutations.ts index 75207efc4..79f9d3df7 100644 --- a/src/graphql/public/mutations.ts +++ b/src/graphql/public/mutations.ts @@ -65,7 +65,9 @@ import UpdateExternalWalletMutation from "./root/mutation/update-external-wallet import BridgeInitiateKycMutation from "./root/mutation/bridge-initiate-kyc" import BridgeCreateVirtualAccountMutation from "./root/mutation/bridge-create-virtual-account" import BridgeAddExternalAccountMutation from "./root/mutation/bridge-add-external-account" +import BridgeRequestWithdrawalMutation from "./root/mutation/bridge-request-withdrawal" import BridgeInitiateWithdrawalMutation from "./root/mutation/bridge-initiate-withdrawal" +import BridgeCancelWithdrawalRequestMutation from "./root/mutation/bridge-cancel-withdrawal-request" // TODO: // const fields: { [key: string]: GraphQLFieldConfig } export const mutationFields = { @@ -123,7 +125,9 @@ export const mutationFields = { bridgeInitiateKyc: BridgeInitiateKycMutation, bridgeCreateVirtualAccount: BridgeCreateVirtualAccountMutation, bridgeAddExternalAccount: BridgeAddExternalAccountMutation, + bridgeRequestWithdrawal: BridgeRequestWithdrawalMutation, bridgeInitiateWithdrawal: BridgeInitiateWithdrawalMutation, + bridgeCancelWithdrawalRequest: BridgeCancelWithdrawalRequestMutation, }, atWalletLevel: { diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index 7a401d97e..7bb5a98ab 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -26,6 +26,7 @@ import SupportedBanksQuery from "./root/query/supported-banks" import BridgeKycStatusQuery from "./root/query/bridge-kyc-status" import BridgeVirtualAccountQuery from "./root/query/bridge-virtual-account" import BridgeExternalAccountsQuery from "./root/query/bridge-external-accounts" +import BridgeWithdrawalRequestQuery from "./root/query/bridge-withdrawal-request" import BridgeWithdrawalsQuery from "./root/query/bridge-withdrawals" export const queryFields = { @@ -55,6 +56,7 @@ export const queryFields = { bridgeKycStatus: BridgeKycStatusQuery, bridgeVirtualAccount: BridgeVirtualAccountQuery, bridgeExternalAccounts: BridgeExternalAccountsQuery, + bridgeWithdrawalRequest: BridgeWithdrawalRequestQuery, bridgeWithdrawals: BridgeWithdrawalsQuery, }, atWalletLevel: { diff --git a/src/graphql/public/root/mutation/bridge-cancel-withdrawal-request.ts b/src/graphql/public/root/mutation/bridge-cancel-withdrawal-request.ts new file mode 100644 index 000000000..1cdd9d9f1 --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-cancel-withdrawal-request.ts @@ -0,0 +1,52 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" +import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeCancelWithdrawalRequestInput = GT.Input({ + name: "BridgeCancelWithdrawalRequestInput", + fields: () => ({ + withdrawalId: { type: GT.NonNull(GT.ID) }, + }), +}) + +const BridgeCancelWithdrawalRequestPayload = GT.Object({ + name: "BridgeCancelWithdrawalRequestPayload", + fields: () => ({ + errors: { type: GT.NonNullList(IError) }, + withdrawal: { type: BridgeWithdrawal }, + }), +}) + +const bridgeCancelWithdrawalRequest = GT.Field({ + type: GT.NonNull(BridgeCancelWithdrawalRequestPayload), + args: { + input: { type: GT.NonNull(BridgeCancelWithdrawalRequestInput) }, + }, + resolve: async (_, args, { domainAccount }: GraphQLPublicContextAuth) => { + const { withdrawalId } = args.input + + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level <= 0) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.cancelWithdrawalRequest( + domainAccount.id, + withdrawalId, + ) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { withdrawal: result, errors: [] } + }, +}) + +export default bridgeCancelWithdrawalRequest diff --git a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts index 4eff628ba..855136aaf 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts @@ -4,18 +4,12 @@ import IError from "@graphql/shared/types/abstract/error" import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" -import { - BridgeDisabledError, - BridgeAccountLevelError, - BridgeInvalidAmountError, - BridgeBelowMinimumWithdrawalError, -} from "@services/bridge/errors" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" const BridgeInitiateWithdrawalInput = GT.Input({ name: "BridgeInitiateWithdrawalInput", fields: () => ({ - amount: { type: GT.NonNull(GT.String) }, - externalAccountId: { type: GT.NonNull(GT.ID) }, + withdrawalId: { type: GT.NonNull(GT.ID) }, }), }) @@ -33,25 +27,7 @@ const bridgeInitiateWithdrawal = GT.Field({ input: { type: GT.NonNull(BridgeInitiateWithdrawalInput) }, }, resolve: async (_, args, { domainAccount }: GraphQLPublicContextAuth) => { - const { amount, externalAccountId } = args.input - - // validate the amount is positive and has at most 6 decimal places - if (!/^\d+(\.\d{1,6})?$/.test(amount) || parseFloat(amount) <= 0) { - return { - errors: [mapAndParseErrorForGqlResponse(new BridgeInvalidAmountError())], - } - } - - // validate the amount is greater than the minimum withdrawal amount - if (parseFloat(amount) < BridgeConfig.minWithdrawalAmount) { - return { - errors: [ - mapAndParseErrorForGqlResponse( - new BridgeBelowMinimumWithdrawalError(BridgeConfig.minWithdrawalAmount), - ), - ], - } - } + const { withdrawalId } = args.input if (!BridgeConfig.enabled) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } @@ -61,11 +37,7 @@ const bridgeInitiateWithdrawal = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } - const result = await BridgeService.initiateWithdrawal( - domainAccount.id, - amount, - externalAccountId, - ) + const result = await BridgeService.initiateWithdrawal(domainAccount.id, withdrawalId) if (result instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(result)] } } diff --git a/src/graphql/public/root/mutation/bridge-request-withdrawal.ts b/src/graphql/public/root/mutation/bridge-request-withdrawal.ts new file mode 100644 index 000000000..2aba8f945 --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-request-withdrawal.ts @@ -0,0 +1,75 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" +import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { + BridgeDisabledError, + BridgeAccountLevelError, + BridgeInvalidAmountError, + BridgeBelowMinimumWithdrawalError, +} from "@services/bridge/errors" + +const BridgeRequestWithdrawalInput = GT.Input({ + name: "BridgeRequestWithdrawalInput", + fields: () => ({ + amount: { type: GT.NonNull(GT.String) }, + externalAccountId: { type: GT.NonNull(GT.ID) }, + }), +}) + +const BridgeRequestWithdrawalPayload = GT.Object({ + name: "BridgeRequestWithdrawalPayload", + fields: () => ({ + errors: { type: GT.NonNullList(IError) }, + withdrawal: { type: BridgeWithdrawal }, + }), +}) + +const bridgeRequestWithdrawal = GT.Field({ + type: GT.NonNull(BridgeRequestWithdrawalPayload), + args: { + input: { type: GT.NonNull(BridgeRequestWithdrawalInput) }, + }, + resolve: async (_, args, { domainAccount }: GraphQLPublicContextAuth) => { + const { amount, externalAccountId } = args.input + + if (!/^\d+(\.\d{1,6})?$/.test(amount) || parseFloat(amount) <= 0) { + return { + errors: [mapAndParseErrorForGqlResponse(new BridgeInvalidAmountError())], + } + } + + if (parseFloat(amount) < BridgeConfig.minWithdrawalAmount) { + return { + errors: [ + mapAndParseErrorForGqlResponse( + new BridgeBelowMinimumWithdrawalError(BridgeConfig.minWithdrawalAmount), + ), + ], + } + } + + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level <= 0) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.requestWithdrawal( + domainAccount.id, + amount, + externalAccountId, + ) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { withdrawal: result, errors: [] } + }, +}) + +export default bridgeRequestWithdrawal diff --git a/src/graphql/public/root/query/bridge-withdrawal-request.ts b/src/graphql/public/root/query/bridge-withdrawal-request.ts new file mode 100644 index 000000000..289fd83d2 --- /dev/null +++ b/src/graphql/public/root/query/bridge-withdrawal-request.ts @@ -0,0 +1,40 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" +import { BridgeConfig } from "@config" +import { BridgeDisabledError } from "@services/bridge/errors" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import { RepositoryError } from "@domain/errors" + +const bridgeWithdrawalRequest = GT.Field({ + type: BridgeWithdrawal, + args: { + id: { type: GT.NonNull(GT.ID) }, + }, + resolve: async (_, args, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const withdrawal = await BridgeAccountsRepo.findWithdrawalById(args.id) + if (withdrawal instanceof RepositoryError) return null + + // Ownership check — never expose another account's withdrawal + if (withdrawal.accountId !== (domainAccount.id as string)) return null + + return { + id: withdrawal.id, + amount: withdrawal.amount, + currency: withdrawal.currency, + externalAccountId: withdrawal.externalAccountId, + status: withdrawal.status, + bridgeTransferId: withdrawal.bridgeTransferId, + failureReason: withdrawal.failureReason, + createdAt: withdrawal.createdAt.toISOString(), + } + }, +}) + +export default bridgeWithdrawalRequest diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index fbc3366e0..26dd8eae0 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -246,6 +246,15 @@ type BridgeAddExternalAccountPayload { externalAccount: BridgeExternalAccountLink } +input BridgeCancelWithdrawalRequestInput { + withdrawalId: ID! +} + +type BridgeCancelWithdrawalRequestPayload { + errors: [Error!]! + withdrawal: BridgeWithdrawal +} + type BridgeCreateVirtualAccountPayload { errors: [Error!]! virtualAccount: BridgeVirtualAccount @@ -275,8 +284,7 @@ type BridgeInitiateKycPayload { } input BridgeInitiateWithdrawalInput { - amount: String! - externalAccountId: ID! + withdrawalId: ID! } type BridgeInitiateWithdrawalPayload { @@ -289,6 +297,16 @@ type BridgeKycLink { tosLink: String! } +input BridgeRequestWithdrawalInput { + amount: String! + externalAccountId: ID! +} + +type BridgeRequestWithdrawalPayload { + errors: [Error!]! + withdrawal: BridgeWithdrawal +} + type BridgeVirtualAccount { accountNumber: String accountNumberLast4: String @@ -303,11 +321,13 @@ type BridgeVirtualAccount { type BridgeWithdrawal { amount: String! + bridgeTransferId: String createdAt: String! currency: String! + externalAccountId: String failureReason: String - state: String! - transferId: ID! + id: ID! + status: String! } type BuildInformation { @@ -956,9 +976,11 @@ type Mutation { accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! bridgeAddExternalAccount: BridgeAddExternalAccountPayload! + bridgeCancelWithdrawalRequest(input: BridgeCancelWithdrawalRequestInput!): BridgeCancelWithdrawalRequestPayload! bridgeCreateVirtualAccount: BridgeCreateVirtualAccountPayload! bridgeInitiateKyc(input: BridgeInitiateKycInput!): BridgeInitiateKycPayload! bridgeInitiateWithdrawal(input: BridgeInitiateWithdrawalInput!): BridgeInitiateWithdrawalPayload! + bridgeRequestWithdrawal(input: BridgeRequestWithdrawalInput!): BridgeRequestWithdrawalPayload! businessAccountUpgradeRequest(input: BusinessAccountUpgradeRequestInput!): AccountUpgradePayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! @@ -1336,6 +1358,7 @@ type Query { bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount + bridgeWithdrawalRequest(id: ID!): BridgeWithdrawal bridgeWithdrawals: [BridgeWithdrawal] btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] diff --git a/src/graphql/public/types/object/bridge-withdrawal.ts b/src/graphql/public/types/object/bridge-withdrawal.ts index 00a51c13c..7d95cbf33 100644 --- a/src/graphql/public/types/object/bridge-withdrawal.ts +++ b/src/graphql/public/types/object/bridge-withdrawal.ts @@ -3,10 +3,12 @@ import { GT } from "@graphql/index" const BridgeWithdrawal = GT.Object({ name: "BridgeWithdrawal", fields: () => ({ - transferId: { type: GT.NonNullID }, + id: { type: GT.NonNullID }, amount: { type: GT.NonNull(GT.String) }, currency: { type: GT.NonNull(GT.String) }, - state: { type: GT.NonNull(GT.String) }, + externalAccountId: { type: GT.String }, + status: { type: GT.NonNull(GT.String) }, + bridgeTransferId: { type: GT.String }, failureReason: { type: GT.String }, createdAt: { type: GT.NonNull(GT.String) }, }), diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index b0b6178fc..9eff26707 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -95,6 +95,18 @@ export class BridgeWebhookValidationError extends BridgeError { } } +export class BridgeWithdrawalNotFoundError extends BridgeError { + constructor(message: string = "Withdrawal request not found") { + super(message) + } +} + +export class BridgeWithdrawalAlreadyInitiatedError extends BridgeError { + constructor(message: string = "Withdrawal has already been submitted to Bridge and cannot be cancelled") { + super(message) + } +} + /** * Maps HTTP status codes from Bridge API to domain error types */ diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index ba0fab74e..b2c19fe57 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -10,12 +10,14 @@ import { BridgeConfig } from "@config" import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" import { AccountsRepository } from "@services/mongoose/accounts" +import { BridgeVirtualAccount } from "@services/mongoose/schema" import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" import { baseLogger } from "@services/logger" import { RepositoryError } from "@domain/errors" import { toBridgeCustomerId, toBridgeVirtualAccountId } from "@domain/primitives/bridge" import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import { sendBridgeWithdrawalNotificationBestEffort } from "@app/bridge/send-withdrawal-notification" import { USDTAmount, WalletCurrency } from "@domain/shared" import { WalletType } from "@domain/wallets" import { WalletsRepository } from "@services/mongoose/wallets" @@ -32,9 +34,10 @@ import { BridgeKycRejectedError, BridgeKycOffboardedError, BridgeCustomerNotFoundError, + BridgeWithdrawalNotFoundError, + BridgeWithdrawalAlreadyInitiatedError, } from "./errors" -import BridgeApiClient, { BridgeClient } from "./client" -import { BridgeVirtualAccount } from "@services/mongoose/schema" +import BridgeApiClient from "./client" // ============ Types ============ @@ -57,18 +60,41 @@ type AddExternalAccountResult = { expiresAt: string } +type WithdrawalRequestResult = { + id: string + amount: string + currency: string + externalAccountId: string + status: string + failureReason?: string + createdAt: string +} + type InitiateWithdrawalResult = { - transferId: string + id: string + amount: string + currency: string + status: string + bridgeTransferId?: string + createdAt: string +} + +type CancelWithdrawalResult = { + id: string amount: string currency: string - state: string + status: string + createdAt: string } type WithdrawalResult = { - transferId: string + id: string amount: string currency: string - state: string + externalAccountId: string + status: string + bridgeTransferId?: string + failureReason?: string createdAt: string } @@ -221,7 +247,7 @@ const initiateKyc = async ({ // store the customer id and the kyc status const customerId = toBridgeCustomerId(bridgeError.response.existing_kyc_link.customer_id) - const updateResult = await AccountsRepository().updateBridgeFields(accountId, { + await AccountsRepository().updateBridgeFields(accountId, { bridgeCustomerId: customerId, bridgeKycStatus: "not_started", }) @@ -295,7 +321,7 @@ const createVirtualAccount = async ( return customer } - let kycStatus = customer.status + const kycStatus = customer.status // Check KYC status @@ -333,7 +359,7 @@ const createVirtualAccount = async ( let ethereumAddress = account.bridgeEthereumAddress if (!ethereumAddress) { - let option = await IbexClient.getEthereumUsdtOption() + const option = await IbexClient.getEthereumUsdtOption() if (option instanceof Error) return new BridgeError(option.message) option.name = `USDT-ETH ${account.username}-${crypto.randomBytes(4).toString("hex")}` @@ -456,16 +482,17 @@ const addExternalAccount = async ( } /** - * Initiates a withdrawal from USDT to USD bank account - * - Orchestrates IBEX → Bridge transfer + * Requests a withdrawal — validates everything and stores a pending record in MongoDB. + * Does NOT call the Bridge API. Returns the pending withdrawal so the frontend can + * display a confirmation screen before the user commits. */ -const initiateWithdrawal = async ( +const requestWithdrawal = async ( accountId: AccountId, amount: string, externalAccountId: string, -): Promise => { +): Promise => { baseLogger.info( - { accountId, amount, externalAccountId, operation: "initiateWithdrawal" }, + { accountId, amount, externalAccountId, operation: "requestWithdrawal" }, "Bridge operation started", ) @@ -513,12 +540,7 @@ const initiateWithdrawal = async ( const availableBalance = balance.toIbex() if (availableBalance < withdrawalAmount) { baseLogger.warn( - { - accountId, - availableBalance, - withdrawalAmount, - operation: "initiateWithdrawal", - }, + { accountId, availableBalance, withdrawalAmount, operation: "requestWithdrawal" }, "Insufficient USDT balance for withdrawal", ) return new BridgeInsufficientFundsError( @@ -526,8 +548,7 @@ const initiateWithdrawal = async ( ) } - // CRIT-2 (ENG-281): Verify caller owns this external account (ownership enforced here - // and at DB level via compound index — see schema.ts BridgeExternalAccountSchema) + // CRIT-2 (ENG-281): Verify caller owns this external account const externalAccounts = await BridgeAccountsRepo.findExternalAccountsByAccountId( accountId as string, ) @@ -537,7 +558,6 @@ const initiateWithdrawal = async ( (acc) => acc.bridgeExternalAccountId === externalAccountId, ) if (!targetAccount) { - // Do not leak existence — return same error regardless of whether account exists return new Error("External account not found") } if (targetAccount.status !== "verified") { @@ -552,7 +572,6 @@ const initiateWithdrawal = async ( ) if (existingWithdrawal instanceof Error) return existingWithdrawal - // Store withdrawal record, or reuse the in-flight row for a retry of the same request. const pendingWithdrawal = existingWithdrawal || (await BridgeAccountsRepo.createWithdrawal({ @@ -564,9 +583,100 @@ const initiateWithdrawal = async ( })) if (pendingWithdrawal instanceof Error) return pendingWithdrawal + baseLogger.info( + { accountId, operation: "requestWithdrawal", withdrawalId: pendingWithdrawal.id }, + "Bridge operation completed", + ) + + return { + id: pendingWithdrawal.id, + amount: pendingWithdrawal.amount, + currency: pendingWithdrawal.currency, + externalAccountId: pendingWithdrawal.externalAccountId, + status: pendingWithdrawal.status, + failureReason: pendingWithdrawal.failureReason, + createdAt: pendingWithdrawal.createdAt.toISOString(), + } + } catch (error) { + baseLogger.error( + { accountId, operation: "requestWithdrawal", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Initiates a previously requested withdrawal — fetches the pending record by ID, + * re-checks balance, then submits the transfer to Bridge. + */ +const initiateWithdrawal = async ( + accountId: AccountId, + withdrawalId: string, +): Promise => { + baseLogger.info( + { accountId, withdrawalId, operation: "initiateWithdrawal" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const customerId = account.bridgeCustomerId + if (!customerId) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + const ethereumAddress = account.bridgeEthereumAddress + if (!ethereumAddress) { + return new Error("Account has no Ethereum address. Create virtual account first.") + } + + const pendingWithdrawal = await BridgeAccountsRepo.findWithdrawalById(withdrawalId) + if (pendingWithdrawal instanceof Error) { + return new BridgeWithdrawalNotFoundError() + } + if (pendingWithdrawal.accountId !== (accountId as string)) { + return new BridgeWithdrawalNotFoundError() + } + if (pendingWithdrawal.status !== "pending" || pendingWithdrawal.bridgeTransferId) { + return new BridgeWithdrawalAlreadyInitiatedError() + } + + const { amount, externalAccountId } = pendingWithdrawal + + // Re-check balance at execution time — funds may have changed since the request + const wallets = await WalletsRepository().listByAccountId(accountId) + if (wallets instanceof Error) return wallets + const usdtWallet = wallets.find( + (w) => w.currency === WalletCurrency.Usdt && w.type === WalletType.Checking, + ) + if (!usdtWallet) { + return new BridgeInsufficientFundsError("No USDT wallet found on account") + } + const balance = await getBalanceForWallet({ + walletId: usdtWallet.id, + currency: WalletCurrency.Usdt, + }) + if (balance instanceof Error) return balance + if (!(balance instanceof USDTAmount)) { + return new BridgeInsufficientFundsError("Invalid balance type") + } + const availableBalance = balance.toIbex() + if (availableBalance < parseFloat(amount)) { + return new BridgeInsufficientFundsError( + `Insufficient USDT balance: available ${availableBalance}, requested ${amount}`, + ) + } + const idempotencyKey = deriveWithdrawalIdempotencyKey(pendingWithdrawal.id) - // Create transfer via Bridge const transfer = await BridgeApiClient.createTransfer( customerId, { @@ -586,28 +696,27 @@ const initiateWithdrawal = async ( idempotencyKey, ) - const result: InitiateWithdrawalResult = { - transferId: transfer.id, - amount: transfer.amount, - currency: transfer.currency, - state: transfer.state, - } - - const withdrawalResult = await BridgeAccountsRepo.updateWithdrawalTransferId( + const updated = await BridgeAccountsRepo.updateWithdrawalTransferId( pendingWithdrawal.id, transfer.id, transfer.amount, transfer.currency, ) - - if (withdrawalResult instanceof Error) return withdrawalResult + if (updated instanceof Error) return updated baseLogger.info( { accountId, operation: "initiateWithdrawal", transferId: transfer.id }, "Bridge operation completed", ) - return result + return { + id: updated.id, + amount: updated.amount, + currency: updated.currency, + status: updated.status, + bridgeTransferId: updated.bridgeTransferId, + createdAt: updated.createdAt.toISOString(), + } } catch (error) { baseLogger.error( { accountId, operation: "initiateWithdrawal", error }, @@ -617,6 +726,74 @@ const initiateWithdrawal = async ( } } +/** + * Cancels a pending withdrawal request before it has been submitted to Bridge. + * Fails if the withdrawal already has a bridgeTransferId (transfer in-flight). + */ +const cancelWithdrawalRequest = async ( + accountId: AccountId, + withdrawalId: string, +): Promise => { + baseLogger.info( + { accountId, withdrawalId, operation: "cancelWithdrawalRequest" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + // Verify the withdrawal exists and belongs to this account before attempting cancel + const withdrawal = await BridgeAccountsRepo.findWithdrawalById(withdrawalId) + if (withdrawal instanceof Error) { + return new BridgeWithdrawalNotFoundError() + } + if (withdrawal.accountId !== (accountId as string)) { + return new BridgeWithdrawalNotFoundError() + } + if (withdrawal.bridgeTransferId) { + return new BridgeWithdrawalAlreadyInitiatedError() + } + + const cancelled = await BridgeAccountsRepo.cancelWithdrawal( + accountId as string, + withdrawalId, + ) + if (cancelled instanceof Error) { + return new BridgeWithdrawalNotFoundError() + } + + baseLogger.info( + { accountId, operation: "cancelWithdrawalRequest", withdrawalId }, + "Bridge operation completed", + ) + + await sendBridgeWithdrawalNotificationBestEffort({ + accountId: accountId as string, + amount: cancelled.amount, + currency: cancelled.currency, + outcome: "cancelled", + }) + + return { + id: cancelled.id, + amount: cancelled.amount, + currency: cancelled.currency, + status: cancelled.status, + createdAt: cancelled.createdAt.toISOString(), + } + } catch (error) { + baseLogger.error( + { accountId, operation: "cancelWithdrawalRequest", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + /** * Returns KYC status for an account */ @@ -791,11 +968,10 @@ const getVirtualAccount = async ( // delete the virtual account from our repo since it's no longer valid - const deleteResult = await BridgeVirtualAccount.deleteOne({ + await BridgeVirtualAccount.deleteOne({ bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId! as string }) - return null } @@ -893,12 +1069,15 @@ const getWithdrawals = async ( if (withdrawals instanceof Error) return withdrawals const result: WithdrawalResult[] = withdrawals - .filter((w) => w.bridgeTransferId !== null || w.bridgeTransferId !== undefined) + .filter((w) => w.bridgeTransferId !== null && w.bridgeTransferId !== undefined) .map((w) => ({ - transferId: w.bridgeTransferId!, + id: w.id, amount: w.amount, currency: w.currency, - state: w.status, + externalAccountId: w.externalAccountId, + status: w.status, + bridgeTransferId: w.bridgeTransferId, + failureReason: w.failureReason, createdAt: w.createdAt.toISOString(), })) @@ -925,7 +1104,9 @@ export default wrapAsyncFunctionsToRunInSpan({ initiateKyc, createVirtualAccount, addExternalAccount, + requestWithdrawal, initiateWithdrawal, + cancelWithdrawalRequest, getKycStatus, getVirtualAccount, getExternalAccounts, diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index 1105ac2be..4fe09f432 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -22,6 +22,13 @@ export const depositHandler = async (req: Request, res: Response) => { } try { + const lockKey = `bridge-deposit:${id}:${state}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) + if (lockResult instanceof Error) { + baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") + return res.status(200).json({ status: "already_processed" }) + } + baseLogger.info( { id, @@ -53,7 +60,7 @@ export const depositHandler = async (req: Request, res: Response) => { ? String(receipt.developer_fee) : event_object?.developer_fee != null ? String(event_object.developer_fee) - : "0", + : "0.0", subtotalAmount: receipt?.subtotal_amount != null ? String(receipt.subtotal_amount) : undefined, initialAmount: @@ -92,9 +99,9 @@ export const depositHandler = async (req: Request, res: Response) => { // Idempotency: mark processed only after local and ERPNext writes succeed, so // provider retries can recover audit gaps after transient ERPNext failures. - const lockKey = `bridge-deposit:${event_id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) - if (lockResult instanceof Error) { + const auditLockKey = `bridge-deposit:${event_id}` + const auditLockResult = await LockService().lockIdempotencyKey(auditLockKey as IdempotencyKey) + if (auditLockResult instanceof Error) { baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") return res.status(200).json({ status: "already_processed" }) } diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index 4afd2c974..41f7fec4c 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -167,7 +167,7 @@ export const updateWithdrawalTransferId = async ( try { const record = await BridgeWithdrawal.findByIdAndUpdate( id, - { bridgeTransferId, amount, currency, updatedAt: new Date() }, + { bridgeTransferId, amount, currency, status: "submitted", updatedAt: new Date() }, { new: true }, ) return record || new RepositoryError("Withdrawal not found") @@ -196,7 +196,7 @@ export const updateWithdrawalStatus = async ( if (truncatedReason !== undefined) update.failureReason = truncatedReason const record = await BridgeWithdrawal.findOneAndUpdate( - { bridgeTransferId, status: "pending" }, + { bridgeTransferId, status: "submitted" }, update, { new: true }, ) @@ -224,3 +224,25 @@ export const findWithdrawalByBridgeTransferId = async (transferId: BridgeTransfe return new RepositoryError(String(error)) } } + +export const findWithdrawalById = async (id: string) => { + try { + const record = await BridgeWithdrawal.findById(id) + return record || new RepositoryError("Withdrawal not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const cancelWithdrawal = async (accountId: string, withdrawalId: string) => { + try { + const record = await BridgeWithdrawal.findOneAndUpdate( + { _id: withdrawalId, accountId, status: "pending", bridgeTransferId: { $exists: false } }, + { status: "cancelled", updatedAt: new Date() }, + { new: true }, + ) + return record || new RepositoryError("Withdrawal not found or cannot be cancelled") + } catch (error) { + return new RepositoryError(String(error)) + } +} diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index a15023362..291a8c8b7 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -47,7 +47,7 @@ interface IBridgeWithdrawalRecord { bridgeTransferId?: string amount: string currency: string - status: "pending" | "completed" | "failed" + status: "pending" | "submitted" | "completed" | "failed" | "cancelled" failureReason?: string externalAccountId: string createdAt: Date @@ -715,7 +715,7 @@ const BridgeWithdrawalSchema = new Schema({ bridgeTransferId: { type: String, unique: true, sparse: true }, amount: { type: String, required: true }, currency: { type: String, required: true }, - status: { type: String, enum: ["pending", "completed", "failed"], default: "pending" }, + status: { type: String, enum: ["pending", "submitted", "completed", "failed", "cancelled"], default: "pending" }, failureReason: { type: String, maxlength: 512 }, externalAccountId: { type: String, required: true }, createdAt: { type: Date, default: Date.now }, diff --git a/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts b/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts index d00d89dfa..30f3bb9c0 100644 --- a/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts +++ b/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts @@ -119,4 +119,33 @@ describe("sendBridgeWithdrawalNotification", () => { expect(result).toBe(true) }) + + it("sends a cancelled withdrawal notification with the correct phrase key and data type", async () => { + const result = await sendBridgeWithdrawalNotification({ + accountId, + amount: "25.00", + currency: "usdt", + outcome: "cancelled", + }) + + expect(result).toBe(true) + expect(sendFilteredNotification).toHaveBeenCalledWith( + expect.objectContaining({ + deviceTokens: mockUser.deviceTokens, + notificationCategory: "Cashout", + data: expect.objectContaining({ type: "bridge_withdrawal_cancelled" }), + }), + ) + expect(mockI18n.__).toHaveBeenCalledWith( + expect.objectContaining({ + phrase: "notification.bridgeWithdrawal.cancelled.title", + }), + ) + expect(mockI18n.__).toHaveBeenCalledWith( + expect.objectContaining({ + phrase: "notification.bridgeWithdrawal.cancelled.body", + }), + expect.objectContaining({ amount: "25.00 USDT" }), + ) + }) }) diff --git a/test/flash/unit/graphql/error-map.spec.ts b/test/flash/unit/graphql/error-map.spec.ts index 9be4c30e9..0fd27d505 100644 --- a/test/flash/unit/graphql/error-map.spec.ts +++ b/test/flash/unit/graphql/error-map.spec.ts @@ -1,7 +1,25 @@ import { mapError } from "@graphql/error-map" import { PhoneAccountAlreadyExistsCannotUpgradeError } from "@services/kratos" +import { + BridgeWithdrawalNotFoundError, + BridgeWithdrawalAlreadyInitiatedError, +} from "@services/bridge/errors" describe("error-map", () => { + it("maps BridgeWithdrawalNotFoundError to BRIDGE_WITHDRAWAL_NOT_FOUND", () => { + const result = mapError(new BridgeWithdrawalNotFoundError()) + + expect(result.extensions.code).toBe("BRIDGE_WITHDRAWAL_NOT_FOUND") + expect(result.message).toContain("Withdrawal request not found") + }) + + it("maps BridgeWithdrawalAlreadyInitiatedError to BRIDGE_WITHDRAWAL_ALREADY_INITIATED", () => { + const result = mapError(new BridgeWithdrawalAlreadyInitiatedError()) + + expect(result.extensions.code).toBe("BRIDGE_WITHDRAWAL_ALREADY_INITIATED") + expect(result.message).toContain("already been submitted") + }) + it("maps PhoneAccountAlreadyExistsCannotUpgradeError to correct GQL error", () => { const input = new PhoneAccountAlreadyExistsCannotUpgradeError() const result = mapError(input) diff --git a/test/flash/unit/graphql/public/root/mutation/bridge-withdrawal.spec.ts b/test/flash/unit/graphql/public/root/mutation/bridge-withdrawal.spec.ts new file mode 100644 index 000000000..9d9223261 --- /dev/null +++ b/test/flash/unit/graphql/public/root/mutation/bridge-withdrawal.spec.ts @@ -0,0 +1,214 @@ +// jest.mock calls are hoisted before imports + +jest.mock("@services/bridge", () => ({ + __esModule: true, + default: { + requestWithdrawal: jest.fn(), + initiateWithdrawal: jest.fn(), + cancelWithdrawalRequest: jest.fn(), + }, +})) + +jest.mock("@config", () => ({ + BridgeConfig: { enabled: true, minWithdrawalAmount: 10 }, + getOnChainWalletConfig: jest.fn().mockReturnValue({ dustThreshold: 546 }), +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +import BridgeService from "@services/bridge" +import BridgeRequestWithdrawalMutation from "@graphql/public/root/mutation/bridge-request-withdrawal" +import BridgeInitiateWithdrawalMutation from "@graphql/public/root/mutation/bridge-initiate-withdrawal" +import BridgeCancelWithdrawalRequestMutation from "@graphql/public/root/mutation/bridge-cancel-withdrawal-request" +import { + BridgeWithdrawalNotFoundError, + BridgeWithdrawalAlreadyInitiatedError, +} from "@services/bridge/errors" + +// ── Fixtures ────────────────────────────────────────────────────────────────── + +const ACCOUNT_ID = "account-001" as AccountId +const EXTERNAL_ACCOUNT_ID = "ext-001" +const AMOUNT = "50" +const WITHDRAWAL_ID = "withdrawal-001" +const TRANSFER_ID = "transfer-001" +const CREATED_AT = new Date("2026-01-01T00:00:00Z") + +const ctx = { + domainAccount: { id: ACCOUNT_ID, level: 2 }, +} as unknown as GraphQLPublicContextAuth + +const makePendingRow = (overrides: Record = {}) => ({ + id: WITHDRAWAL_ID, + accountId: ACCOUNT_ID as string, + amount: AMOUNT, + currency: "usdt", + externalAccountId: EXTERNAL_ACCOUNT_ID, + status: "pending" as const, + createdAt: CREATED_AT, + ...overrides, +}) + +// ── bridgeRequestWithdrawal ─────────────────────────────────────────────────── + +describe("bridgeRequestWithdrawal resolver", () => { + beforeEach(() => jest.clearAllMocks()) + + it("creates a pending withdrawal and returns it", async () => { + const pendingRow = makePendingRow() + ;(BridgeService.requestWithdrawal as jest.Mock).mockResolvedValue(pendingRow) + + const result = await BridgeRequestWithdrawalMutation.resolve?.( + null, + { input: { amount: AMOUNT, externalAccountId: EXTERNAL_ACCOUNT_ID } }, + ctx, + {} as never, + ) + + expect(BridgeService.requestWithdrawal).toHaveBeenCalledWith( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + expect(result?.errors).toEqual([]) + expect(result?.withdrawal).toEqual(pendingRow) + expect(result?.withdrawal?.status).toBe("pending") + expect(result?.withdrawal?.externalAccountId).toBe(EXTERNAL_ACCOUNT_ID) + }) + + it("returns an existing pending row when the service deduplicates the request", async () => { + const existingRow = makePendingRow({ id: "existing-withdrawal-001" }) + ;(BridgeService.requestWithdrawal as jest.Mock).mockResolvedValue(existingRow) + + const result = await BridgeRequestWithdrawalMutation.resolve?.( + null, + { input: { amount: AMOUNT, externalAccountId: EXTERNAL_ACCOUNT_ID } }, + ctx, + {} as never, + ) + + expect(result?.errors).toEqual([]) + expect(result?.withdrawal?.id).toBe("existing-withdrawal-001") + expect(result?.withdrawal?.status).toBe("pending") + }) +}) + +// ── bridgeInitiateWithdrawal ────────────────────────────────────────────────── + +describe("bridgeInitiateWithdrawal resolver", () => { + beforeEach(() => jest.clearAllMocks()) + + it("submits the pending row and returns the withdrawal with bridgeTransferId recorded", async () => { + const initiatedRow = makePendingRow({ bridgeTransferId: TRANSFER_ID, status: "submitted" }) + ;(BridgeService.initiateWithdrawal as jest.Mock).mockResolvedValue(initiatedRow) + + const result = await BridgeInitiateWithdrawalMutation.resolve?.( + null, + { input: { withdrawalId: WITHDRAWAL_ID } }, + ctx, + {} as never, + ) + + expect(BridgeService.initiateWithdrawal).toHaveBeenCalledWith(ACCOUNT_ID, WITHDRAWAL_ID) + expect(result?.errors).toEqual([]) + expect(result?.withdrawal?.status).toBe("submitted") + expect(result?.withdrawal?.bridgeTransferId).toBe(TRANSFER_ID) + }) + + it("maps BridgeWithdrawalNotFoundError to BRIDGE_WITHDRAWAL_NOT_FOUND when ID is missing or wrong-owner", async () => { + ;(BridgeService.initiateWithdrawal as jest.Mock).mockResolvedValue( + new BridgeWithdrawalNotFoundError(), + ) + + const result = await BridgeInitiateWithdrawalMutation.resolve?.( + null, + { input: { withdrawalId: WITHDRAWAL_ID } }, + ctx, + {} as never, + ) + + expect(result?.errors).toHaveLength(1) + expect(result?.errors[0].code).toBe("BRIDGE_WITHDRAWAL_NOT_FOUND") + expect(result?.withdrawal).toBeUndefined() + }) + + it("maps BridgeWithdrawalAlreadyInitiatedError to BRIDGE_WITHDRAWAL_ALREADY_INITIATED", async () => { + ;(BridgeService.initiateWithdrawal as jest.Mock).mockResolvedValue( + new BridgeWithdrawalAlreadyInitiatedError(), + ) + + const result = await BridgeInitiateWithdrawalMutation.resolve?.( + null, + { input: { withdrawalId: WITHDRAWAL_ID } }, + ctx, + {} as never, + ) + + expect(result?.errors).toHaveLength(1) + expect(result?.errors[0].code).toBe("BRIDGE_WITHDRAWAL_ALREADY_INITIATED") + expect(result?.withdrawal).toBeUndefined() + }) +}) + +// ── bridgeCancelWithdrawalRequest ───────────────────────────────────────────── + +describe("bridgeCancelWithdrawalRequest resolver", () => { + beforeEach(() => jest.clearAllMocks()) + + it("delegates to cancelWithdrawalRequest and returns the cancelled withdrawal", async () => { + const cancelledRow = makePendingRow({ status: "cancelled" }) + ;(BridgeService.cancelWithdrawalRequest as jest.Mock).mockResolvedValue(cancelledRow) + + const result = await BridgeCancelWithdrawalRequestMutation.resolve?.( + null, + { input: { withdrawalId: WITHDRAWAL_ID } }, + ctx, + {} as never, + ) + + expect(BridgeService.cancelWithdrawalRequest).toHaveBeenCalledWith( + ACCOUNT_ID, + WITHDRAWAL_ID, + ) + expect(result?.errors).toEqual([]) + expect(result?.withdrawal?.status).toBe("cancelled") + expect(result?.withdrawal?.id).toBe(WITHDRAWAL_ID) + expect(result?.withdrawal?.amount).toBe(AMOUNT) + }) + + it("maps BridgeWithdrawalNotFoundError to BRIDGE_WITHDRAWAL_NOT_FOUND when ID is missing or wrong-owner", async () => { + ;(BridgeService.cancelWithdrawalRequest as jest.Mock).mockResolvedValue( + new BridgeWithdrawalNotFoundError(), + ) + + const result = await BridgeCancelWithdrawalRequestMutation.resolve?.( + null, + { input: { withdrawalId: WITHDRAWAL_ID } }, + ctx, + {} as never, + ) + + expect(result?.errors).toHaveLength(1) + expect(result?.errors[0].code).toBe("BRIDGE_WITHDRAWAL_NOT_FOUND") + expect(result?.withdrawal).toBeUndefined() + }) + + it("maps BridgeWithdrawalAlreadyInitiatedError to BRIDGE_WITHDRAWAL_ALREADY_INITIATED when already submitted", async () => { + ;(BridgeService.cancelWithdrawalRequest as jest.Mock).mockResolvedValue( + new BridgeWithdrawalAlreadyInitiatedError(), + ) + + const result = await BridgeCancelWithdrawalRequestMutation.resolve?.( + null, + { input: { withdrawalId: WITHDRAWAL_ID } }, + ctx, + {} as never, + ) + + expect(result?.errors).toHaveLength(1) + expect(result?.errors[0].code).toBe("BRIDGE_WITHDRAWAL_ALREADY_INITIATED") + expect(result?.withdrawal).toBeUndefined() + }) +}) diff --git a/test/flash/unit/graphql/public/root/query/bridge-withdrawals.spec.ts b/test/flash/unit/graphql/public/root/query/bridge-withdrawals.spec.ts new file mode 100644 index 000000000..2f3023543 --- /dev/null +++ b/test/flash/unit/graphql/public/root/query/bridge-withdrawals.spec.ts @@ -0,0 +1,62 @@ +jest.mock("@services/bridge", () => ({ + __esModule: true, + default: { + getWithdrawals: jest.fn(), + }, +})) + +jest.mock("@config", () => ({ + BridgeConfig: { enabled: true }, + getOnChainWalletConfig: jest.fn().mockReturnValue({ dustThreshold: 546 }), +})) + +jest.mock("@services/logger", () => { + const logger = { + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + child: jest.fn(), + } + logger.child.mockReturnValue(logger) + return { baseLogger: logger } +}) + +import BridgeService from "@services/bridge" +import bridgeWithdrawals from "@graphql/public/root/query/bridge-withdrawals" + +const ACCOUNT_ID = "account-001" as AccountId +const WITHDRAWAL_ID = "withdrawal-001" +const TRANSFER_ID = "transfer-001" +const CREATED_AT = "2026-01-01T00:00:00.000Z" + +const ctx = { + domainAccount: { id: ACCOUNT_ID, level: 2 }, +} as unknown as GraphQLPublicContextAuth + +describe("bridgeWithdrawals resolver", () => { + beforeEach(() => jest.clearAllMocks()) + + it("returns service rows with id/status for the BridgeWithdrawal GraphQL type", async () => { + const serviceRow = { + id: WITHDRAWAL_ID, + amount: "50", + currency: "usdt", + externalAccountId: "ext-001", + status: "submitted", + bridgeTransferId: TRANSFER_ID, + failureReason: undefined, + createdAt: CREATED_AT, + } + ;(BridgeService.getWithdrawals as jest.Mock).mockResolvedValue([serviceRow]) + + const result = await bridgeWithdrawals.resolve?.(null, {}, ctx, {} as never) + + expect(BridgeService.getWithdrawals).toHaveBeenCalledWith(ACCOUNT_ID) + expect(result).toEqual([serviceRow]) + expect(result?.[0].id).toBe(WITHDRAWAL_ID) + expect(result?.[0].status).toBe("submitted") + expect((result?.[0] as Record).transferId).toBeUndefined() + expect((result?.[0] as Record).state).toBeUndefined() + }) +}) diff --git a/test/flash/unit/graphql/public/types/object/bridge-contract.spec.ts b/test/flash/unit/graphql/public/types/object/bridge-contract.spec.ts index 08afbfdc1..1a373d2c0 100644 --- a/test/flash/unit/graphql/public/types/object/bridge-contract.spec.ts +++ b/test/flash/unit/graphql/public/types/object/bridge-contract.spec.ts @@ -6,31 +6,53 @@ describe("Bridge public GraphQL object contract", () => { it("exposes withdrawal fields returned by BridgeService", () => { const fields = BridgeWithdrawal.getFields() - expect(fields).toHaveProperty("transferId") + expect(fields).toHaveProperty("id") expect(fields).toHaveProperty("amount") expect(fields).toHaveProperty("currency") - expect(fields).toHaveProperty("state") + expect(fields).toHaveProperty("externalAccountId") + expect(fields).toHaveProperty("status") + expect(fields).toHaveProperty("bridgeTransferId") + expect(fields).toHaveProperty("failureReason") expect(fields).toHaveProperty("createdAt") - expect(fields).not.toHaveProperty("id") - expect(fields).not.toHaveProperty("status") + expect(fields).not.toHaveProperty("transferId") + expect(fields).not.toHaveProperty("state") }) - it("resolves withdrawal transferId and state from service-shaped results", () => { + it("resolves withdrawal id and status from service-shaped results", () => { const fields = BridgeWithdrawal.getFields() const withdrawal = { - transferId: "transfer-001", + id: "withdrawal-001", amount: "25.00", currency: "usdt", - state: "pending", + externalAccountId: "ext-001", + status: "pending", + bridgeTransferId: undefined, createdAt: "2026-06-05T00:00:00.000Z", } + expect(fields.id).toBeDefined() + expect(fields.status).toBeDefined() + expect(fields.bridgeTransferId).toBeDefined() + expect( - defaultFieldResolver(withdrawal, {}, {}, { fieldName: "transferId" } as never), - ).toBe("transfer-001") + defaultFieldResolver(withdrawal, {}, {}, { fieldName: "id", field: fields.id } as never), + ).toBe("withdrawal-001") expect( - defaultFieldResolver(withdrawal, {}, {}, { fieldName: "state" } as never), + defaultFieldResolver( + withdrawal, + {}, + {}, + { fieldName: "status", field: fields.status } as never, + ), ).toBe("pending") + expect( + defaultFieldResolver( + withdrawal, + {}, + {}, + { fieldName: "bridgeTransferId", field: fields.bridgeTransferId } as never, + ), + ).toBeUndefined() }) it("uses bridgeVirtualAccountId as the virtual account id returned by read queries", () => { diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts index 67d6b50c1..7fd60e2ad 100644 --- a/test/flash/unit/services/bridge/index.spec.ts +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -11,6 +11,12 @@ jest.mock("@services/tracing", () => ({ jest.mock("@config", () => ({ BridgeConfig: { enabled: true, minWithdrawalAmount: 10 }, + // Minimal stubs so schema.ts can run its module-level initialisation + getFeesConfig: jest.fn().mockReturnValue({ depositFeeVariable: 0, depositFeeFixed: 0, withdrawFeeVariable: 0, withdrawFeeFixed: 0 }), + getDefaultAccountsConfig: jest.fn().mockReturnValue({ initialStatus: "active", initialLevel: 0, maxCurrencies: 5 }), + getDefaultFCMTopics: jest.fn().mockReturnValue([]), + Levels: [0, 1, 2, 3], + getI18nInstance: jest.fn().mockReturnValue({ __: jest.fn() }), })) jest.mock("@services/logger", () => ({ @@ -24,18 +30,17 @@ jest.mock("@services/mongoose/bridge-accounts", () => ({ findPendingWithdrawalWithoutTransfer: jest.fn(), findExternalAccountsByAccountId: jest.fn(), updateWithdrawalTransferId: jest.fn(), + findWithdrawalById: jest.fn(), + findWithdrawalsByAccountId: jest.fn(), + cancelWithdrawal: jest.fn(), })) jest.mock("@services/bridge/client", () => ({ - __esModule: true, - default: { createVirtualAccount: jest.fn(), createTransfer: jest.fn() }, -})) - -jest.mock("@services/ibex/client", () => ({ __esModule: true, default: { - getEthereumUsdtOption: jest.fn(), - createCryptoReceiveInfo: jest.fn(), + createVirtualAccount: jest.fn(), + createTransfer: jest.fn(), + getCustomer: jest.fn().mockResolvedValue({ status: "active" }), }, })) @@ -68,18 +73,17 @@ jest.mock("@domain/primitives/bridge", () => ({ toBridgeExternalAccountId: (id: string) => id, })) -// USDTAmount is not re-exported from @domain/shared — provide a minimal stand-in so the -// service's `instanceof USDTAmount` guard is satisfied during tests. -// The class is defined inside the factory because jest.mock factories are hoisted before -// variable declarations; access it at runtime via require("@domain/shared").USDTAmount. -// USDTAmount is not re-exported from @domain/shared/index.ts (pre-existing issue). -// Spread the real module and inject a minimal stand-in so the service's -// `instanceof USDTAmount` guard is satisfied without breaking other domain exports. +// USDTAmount stand-in: the real type is not exported from @domain/shared. +// We spread the real module and inject a minimal class so `instanceof USDTAmount` +// guards in the service are satisfied during tests. jest.mock("@domain/shared", () => { class USDTAmount { - constructor(private readonly ibexValue: number) { - // Parameter property initializes ibexValue. + ibexValue: number + + constructor(ibexValue: number) { + this.ibexValue = ibexValue } + toIbex() { return this.ibexValue } @@ -87,6 +91,10 @@ jest.mock("@domain/shared", () => { return { ...jest.requireActual("@domain/shared"), USDTAmount } }) +jest.mock("@app/bridge/send-withdrawal-notification", () => ({ + sendBridgeWithdrawalNotificationBestEffort: jest.fn().mockResolvedValue(undefined), +})) + import BridgeService, { deriveWithdrawalIdempotencyKey } from "@services/bridge" import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" import BridgeClient from "@services/bridge/client" @@ -95,6 +103,7 @@ import { WalletsRepository } from "@services/mongoose/wallets" import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" import IbexClient from "@services/ibex/client" import { RepositoryError } from "@domain/errors" +import { sendBridgeWithdrawalNotificationBestEffort } from "@app/bridge/send-withdrawal-notification" // ── Fixtures ────────────────────────────────────────────────────────────────── @@ -104,9 +113,11 @@ const AMOUNT = "50" const CUSTOMER_ID = "cust-001" const ETHEREUM_ADDRESS = "ETH_ADDR_001" const TRANSFER_ID = "transfer-bridge-001" +const WITHDRAWAL_ID = "withdrawal-mongo-001" const USDT_WALLET_ID = "ibex-eth-usdt-wallet-001" const RECEIVE_INFO_ID = "receive-info-001" const VIRTUAL_ACCOUNT_ID = "virtual-account-001" +const CREATED_AT = new Date("2026-01-01T00:00:00Z") const mockAccount = { id: ACCOUNT_ID, @@ -117,7 +128,7 @@ const mockAccount = { kratosUserId: "kratos-001", } -const makeRow = (id: string) => ({ +const makeRow = (id: string, overrides: Record = {}) => ({ id, accountId: ACCOUNT_ID as string, amount: AMOUNT, @@ -125,6 +136,9 @@ const makeRow = (id: string) => ({ externalAccountId: EXTERNAL_ACCOUNT_ID, status: "pending" as const, bridgeTransferId: undefined, + failureReason: undefined, + createdAt: CREATED_AT, + ...overrides, }) const mockTransfer = { @@ -152,27 +166,40 @@ const makeWallet = (id: string, currency: string) => ({ // ── Helpers ─────────────────────────────────────────────────────────────────── -const setupGuards = () => { +const getUSDTAmount = (ibex: number) => { const { USDTAmount } = jest.requireMock("@domain/shared") as { USDTAmount: new (ibexValue: number) => { toIbex: () => number } } - const balance = new USDTAmount(1000) // 1000 USDT — well above minWithdrawalAmount + return new USDTAmount(ibex) +} +const expectSuccess = (result: T | Error): T => { + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) throw result + return result +} + +/** Sets up the guards common to requestWithdrawal and initiateWithdrawal. */ +const setupGuards = () => { ;(AccountsRepository as jest.Mock).mockReturnValue({ findById: jest.fn().mockResolvedValue(mockAccount), + update: jest.fn(), + updateBridgeFields: jest.fn(), }) ;(WalletsRepository as jest.Mock).mockReturnValue({ listByAccountId: jest.fn().mockResolvedValue([ - { id: "wallet-001", currency: "USDT", type: "checking" }, + { id: USDT_WALLET_ID, currency: "USDT", type: "checking" }, ]), + persistNew: jest.fn(), }) - ;(getBalanceForWallet as jest.Mock).mockResolvedValue(balance) + ;(getBalanceForWallet as jest.Mock).mockResolvedValue(getUSDTAmount(1000)) ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ { bridgeExternalAccountId: EXTERNAL_ACCOUNT_ID, status: "verified" }, ]) ;(BridgeAccountsRepo.updateWithdrawalTransferId as jest.Mock).mockResolvedValue({ - ...makeRow("any"), + ...makeRow(WITHDRAWAL_ID), bridgeTransferId: TRANSFER_ID, + status: "submitted" as const, }) ;(BridgeClient.createTransfer as jest.Mock).mockResolvedValue(mockTransfer) } @@ -211,13 +238,6 @@ describe("deriveWithdrawalIdempotencyKey", () => { /** * Linear ENG-296 — ETH-USDT Cash Wallet + Bridge virtual account - * @see https://linear.app/island-bitcoin/issue/ENG-296 - * - * Acceptance ↔ tests (staging steps: `dev/qa/ENG-296-staging-checklist.md`): - * - AC1 IBEX ETH-USDT as Cash Wallet: first `it` — USDT checking persisted/reused, Ibex - * `createCryptoReceiveInfo` uses USDT wallet id; account `bridgeEthereumAddress` updated. - * - AC2 USD not primary: first `it` — `AccountsRepository.update` sets `defaultWalletId` to USDT wallet. - * - AC3 createVirtualAccount E2E: first & second `it` — Bridge + repo; third `it` — idempotent VA return. */ describe("createVirtualAccount — ETH-USDT Cash Wallet provisioning (ENG-296)", () => { beforeEach(() => { @@ -384,100 +404,641 @@ describe("createVirtualAccount — ETH-USDT Cash Wallet provisioning (ENG-296)", }) }) -describe("initiateWithdrawal — idempotency key wiring", () => { +// ───────────────────────────────────────────────────────────────────────────── +// requestWithdrawal +// Step 1 of the split flow: validates everything and writes a pending MongoDB +// record — does NOT call the Bridge API. +// ───────────────────────────────────────────────────────────────────────────── + +describe("requestWithdrawal", () => { beforeEach(() => { jest.clearAllMocks() setupGuards() + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + null, + ) + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID), + ) + }) + + it("creates a pending withdrawal record and returns the full result", async () => { + const result = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + + expect(BridgeAccountsRepo.createWithdrawal).toHaveBeenCalledWith({ + accountId: ACCOUNT_ID as string, + amount: AMOUNT, + currency: "usdt", + externalAccountId: EXTERNAL_ACCOUNT_ID, + status: "pending", + }) + expect(expectSuccess(result)).toMatchObject({ + id: WITHDRAWAL_ID, + amount: AMOUNT, + currency: "usdt", + externalAccountId: EXTERNAL_ACCOUNT_ID, + status: "pending", + createdAt: expect.any(String), + }) + }) + + it("never calls the Bridge API", async () => { + await BridgeService.requestWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("reuses an existing pending withdrawal for the same account, amount, and external account", async () => { + const existingRow = makeRow("withdrawal-existing-001") + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + existingRow, + ) + + const result = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + + expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + expect(expectSuccess(result)).toMatchObject({ + id: "withdrawal-existing-001", + status: "pending", + }) + }) + + it("returns an error when the external account does not belong to the caller (CRIT-2)", async () => { + ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ + { bridgeExternalAccountId: "somebody-elses-account", status: "verified" }, + ]) + + const result = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + + expect(result).toBeInstanceOf(Error) + expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + }) + + it("returns an error when the external account is not yet verified", async () => { + ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ + { bridgeExternalAccountId: EXTERNAL_ACCOUNT_ID, status: "pending" }, + ]) + + const result = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + + expect(result).toBeInstanceOf(Error) + expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + }) + + it("returns BridgeInsufficientFundsError when USDT balance is below the requested amount", async () => { + ;(getBalanceForWallet as jest.Mock).mockResolvedValue(getUSDTAmount(5)) // < AMOUNT=50 + + const result = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + + const { BridgeInsufficientFundsError } = jest.requireActual("@services/bridge/errors") + expect(result).toBeInstanceOf(BridgeInsufficientFundsError) + expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + }) + + it("returns BridgeCustomerNotFoundError when account has no Bridge customer ID", async () => { + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue({ ...mockAccount, bridgeCustomerId: undefined }), + }) + + const result = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + + const { BridgeCustomerNotFoundError } = jest.requireActual("@services/bridge/errors") + expect(result).toBeInstanceOf(BridgeCustomerNotFoundError) + }) + + it("returns an error when account has no Ethereum address", async () => { + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue({ + ...mockAccount, + bridgeEthereumAddress: undefined, + }), + }) + + const result = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + + expect(result).toBeInstanceOf(Error) + }) +}) + +// ───────────────────────────────────────────────────────────────────────────── +// initiateWithdrawal (refactored) +// Step 2A: fetches the pending record by ID, re-checks balance, calls Bridge. +// ───────────────────────────────────────────────────────────────────────────── + +describe("initiateWithdrawal — takes withdrawalId (step 2A)", () => { + beforeEach(() => { + jest.clearAllMocks() + setupGuards() + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID), + ) + }) + + it("fetches the pending withdrawal from MongoDB before calling Bridge", async () => { + await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + expect(BridgeAccountsRepo.findWithdrawalById).toHaveBeenCalledWith(WITHDRAWAL_ID) + }) + + it("never calls createWithdrawal — the row already exists from requestWithdrawal", async () => { + await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + }) + + it("uses the idempotency key derived from the withdrawalId", async () => { + await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + const expectedKey = deriveWithdrawalIdempotencyKey(WITHDRAWAL_ID) + expect(BridgeClient.createTransfer).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.any(Object), + expectedKey, + ) + }) + + it("calling twice with the same withdrawalId passes the same idempotency key to Bridge", async () => { + await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + const calls = (BridgeClient.createTransfer as jest.Mock).mock.calls + expect(calls[0][2]).toBe(calls[1][2]) + expect(calls[0][2]).toBe(deriveWithdrawalIdempotencyKey(WITHDRAWAL_ID)) + }) + + it("updates the withdrawal record with the Bridge transfer ID and transitions status to submitted", async () => { + const result = await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + expect(BridgeAccountsRepo.updateWithdrawalTransferId).toHaveBeenCalledWith( + WITHDRAWAL_ID, + TRANSFER_ID, + AMOUNT, + "usd", + ) + expect(expectSuccess(result)).toMatchObject({ + status: "submitted", + bridgeTransferId: TRANSFER_ID, + }) + }) + + it("returns BridgeWithdrawalNotFoundError when the withdrawal ID does not exist", async () => { + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + new RepositoryError("Withdrawal not found"), + ) + + const result = await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + const { BridgeWithdrawalNotFoundError } = jest.requireActual("@services/bridge/errors") + expect(result).toBeInstanceOf(BridgeWithdrawalNotFoundError) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("returns BridgeWithdrawalNotFoundError when the withdrawal belongs to a different account", async () => { + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { accountId: "different-account" }), + ) + + const result = await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + const { BridgeWithdrawalNotFoundError } = jest.requireActual("@services/bridge/errors") + expect(result).toBeInstanceOf(BridgeWithdrawalNotFoundError) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("returns BridgeWithdrawalAlreadyInitiatedError when bridgeTransferId is already set", async () => { + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { bridgeTransferId: "already-submitted" }), + ) + + const result = await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + const { BridgeWithdrawalAlreadyInitiatedError } = jest.requireActual( + "@services/bridge/errors", + ) + expect(result).toBeInstanceOf(BridgeWithdrawalAlreadyInitiatedError) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("returns BridgeWithdrawalAlreadyInitiatedError when status is not pending", async () => { + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { status: "cancelled" }), + ) + + const result = await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + const { BridgeWithdrawalAlreadyInitiatedError } = jest.requireActual( + "@services/bridge/errors", + ) + expect(result).toBeInstanceOf(BridgeWithdrawalAlreadyInitiatedError) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("returns BridgeInsufficientFundsError when balance dropped between request and initiate", async () => { + ;(getBalanceForWallet as jest.Mock).mockResolvedValue(getUSDTAmount(5)) // < AMOUNT=50 + + const result = await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + const { BridgeInsufficientFundsError } = jest.requireActual("@services/bridge/errors") + expect(result).toBeInstanceOf(BridgeInsufficientFundsError) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() }) +}) + +// ───────────────────────────────────────────────────────────────────────────── +// cancelWithdrawalRequest +// Step 2B: marks the pending record "cancelled" and sends a push notification. +// Only allowed before the Bridge API has been called (no bridgeTransferId). +// ───────────────────────────────────────────────────────────────────────────── - describe("fresh request (no in-flight row)", () => { - it("creates a pending withdrawal row before calling Bridge", async () => { - const row = makeRow("fresh-row-001") - ;( - BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock - ).mockResolvedValue(null) - ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) +describe("cancelWithdrawalRequest", () => { + beforeEach(() => { + jest.clearAllMocks() + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + }) + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID), + ) + ;(BridgeAccountsRepo.cancelWithdrawal as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { status: "cancelled" }), + ) + }) - await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + it("cancels the pending withdrawal and returns status cancelled", async () => { + const result = await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) - const createOrder = (BridgeAccountsRepo.createWithdrawal as jest.Mock).mock - .invocationCallOrder[0] - const transferOrder = (BridgeClient.createTransfer as jest.Mock).mock - .invocationCallOrder[0] - expect(createOrder).toBeLessThan(transferOrder) + expect(expectSuccess(result)).toMatchObject({ + status: "cancelled", + id: WITHDRAWAL_ID, + amount: AMOUNT, }) + }) + + it("calls cancelWithdrawal with the correct accountId and withdrawalId", async () => { + await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) - it("derives the idempotency key from the newly created row's id", async () => { - const rowId = "fresh-row-id-abc" - ;( - BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock - ).mockResolvedValue(null) - ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue( - makeRow(rowId), - ) - - await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) - - const expectedKey = deriveWithdrawalIdempotencyKey(rowId) - expect(BridgeClient.createTransfer).toHaveBeenCalledWith( - CUSTOMER_ID, - expect.any(Object), - expectedKey, - ) + expect(BridgeAccountsRepo.cancelWithdrawal).toHaveBeenCalledWith( + ACCOUNT_ID as string, + WITHDRAWAL_ID, + ) + }) + + it("never calls the Bridge API", async () => { + await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) + + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("sends a cancelled push notification after a successful cancel", async () => { + await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) + + expect(sendBridgeWithdrawalNotificationBestEffort).toHaveBeenCalledWith({ + accountId: ACCOUNT_ID as string, + amount: AMOUNT, + currency: "usdt", + outcome: "cancelled", }) }) - describe("retry — in-flight row already exists", () => { - it("does not create a second withdrawal row", async () => { - const existingRow = makeRow("existing-row-001") - ;( - BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock - ).mockResolvedValue(existingRow) + it("returns BridgeWithdrawalNotFoundError when the withdrawal ID does not exist", async () => { + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + new RepositoryError("Withdrawal not found"), + ) + + const result = await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) - await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + const { BridgeWithdrawalNotFoundError } = jest.requireActual("@services/bridge/errors") + expect(result).toBeInstanceOf(BridgeWithdrawalNotFoundError) + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) + + it("returns BridgeWithdrawalNotFoundError when the withdrawal belongs to a different account", async () => { + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { accountId: "different-account" }), + ) + + const result = await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) + + const { BridgeWithdrawalNotFoundError } = jest.requireActual("@services/bridge/errors") + expect(result).toBeInstanceOf(BridgeWithdrawalNotFoundError) + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) + + it("returns BridgeWithdrawalAlreadyInitiatedError when the transfer was already submitted to Bridge", async () => { + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { bridgeTransferId: "already-submitted-id" }), + ) - expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + const result = await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) + + const { BridgeWithdrawalAlreadyInitiatedError } = jest.requireActual( + "@services/bridge/errors", + ) + expect(result).toBeInstanceOf(BridgeWithdrawalAlreadyInitiatedError) + expect(BridgeAccountsRepo.cancelWithdrawal).not.toHaveBeenCalled() + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) + + it("does not send a notification when the repo cancelWithdrawal fails (e.g. race condition)", async () => { + ;(BridgeAccountsRepo.cancelWithdrawal as jest.Mock).mockResolvedValue( + new RepositoryError("Withdrawal not found or cannot be cancelled"), + ) + + const result = await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) + + const { BridgeWithdrawalNotFoundError } = jest.requireActual("@services/bridge/errors") + expect(result).toBeInstanceOf(BridgeWithdrawalNotFoundError) + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) +}) + +// ───────────────────────────────────────────────────────────────────────────── +// withdrawal request → confirm/cancel flow +// Chains the three service steps to pin the contract promised by the PR. +// ───────────────────────────────────────────────────────────────────────────── + +describe("withdrawal request → confirm/cancel flow", () => { + beforeEach(() => { + jest.clearAllMocks() + setupGuards() + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + null, + ) + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID), + ) + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID), + ) + ;(BridgeAccountsRepo.cancelWithdrawal as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { status: "cancelled" }), + ) + ;(BridgeAccountsRepo.updateWithdrawalTransferId as jest.Mock).mockResolvedValue({ + ...makeRow(WITHDRAWAL_ID), + bridgeTransferId: TRANSFER_ID, + status: "submitted" as const, }) + }) - it("derives the key from the existing row's id — identical to the first attempt's key", async () => { - const rowId = "existing-row-001" - ;( - BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock - ).mockResolvedValue(makeRow(rowId)) + it("request creates a pending row, then initiate submits it and records bridgeTransferId", async () => { + const requested = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + const pending = expectSuccess(requested) + expect(pending).toMatchObject({ status: "pending", id: WITHDRAWAL_ID }) - await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + const initiated = expectSuccess( + await BridgeService.initiateWithdrawal(ACCOUNT_ID, pending.id), + ) + expect(initiated).toMatchObject({ + status: "submitted", + bridgeTransferId: TRANSFER_ID, + }) + expect(BridgeClient.createTransfer).toHaveBeenCalledTimes(1) + expect(BridgeAccountsRepo.updateWithdrawalTransferId).toHaveBeenCalledWith( + WITHDRAWAL_ID, + TRANSFER_ID, + AMOUNT, + "usd", + ) + }) - const expectedKey = deriveWithdrawalIdempotencyKey(rowId) - expect(BridgeClient.createTransfer).toHaveBeenCalledWith( - CUSTOMER_ID, - expect.any(Object), - expectedKey, - ) + it("duplicate request reuses the pending row, then initiate still submits that row", async () => { + const existingRow = makeRow("deduped-withdrawal-001") + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + existingRow, + ) + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue(existingRow) + ;(BridgeAccountsRepo.updateWithdrawalTransferId as jest.Mock).mockResolvedValue({ + ...existingRow, + bridgeTransferId: TRANSFER_ID, + status: "submitted" as const, + }) + + const first = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + const second = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + + expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + const firstPending = expectSuccess(first) + const secondPending = expectSuccess(second) + expect(firstPending).toMatchObject({ id: "deduped-withdrawal-001" }) + expect(secondPending).toMatchObject({ id: "deduped-withdrawal-001" }) + + const initiated = expectSuccess( + await BridgeService.initiateWithdrawal(ACCOUNT_ID, firstPending.id), + ) + expect(initiated.bridgeTransferId).toBe(TRANSFER_ID) + expect(BridgeAccountsRepo.updateWithdrawalTransferId).toHaveBeenCalledWith( + "deduped-withdrawal-001", + TRANSFER_ID, + AMOUNT, + "usd", + ) + }) + + it("request then cancel marks the row cancelled and sends the cancelled notification", async () => { + const requested = await BridgeService.requestWithdrawal( + ACCOUNT_ID, + AMOUNT, + EXTERNAL_ACCOUNT_ID, + ) + const pending = expectSuccess(requested) + const cancelled = expectSuccess( + await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, pending.id), + ) + + expect(cancelled.status).toBe("cancelled") + expect(BridgeAccountsRepo.cancelWithdrawal).toHaveBeenCalledWith( + ACCOUNT_ID as string, + WITHDRAWAL_ID, + ) + expect(sendBridgeWithdrawalNotificationBestEffort).toHaveBeenCalledWith({ + accountId: ACCOUNT_ID as string, + amount: AMOUNT, + currency: "usdt", + outcome: "cancelled", + }) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("initiate returns BridgeWithdrawalNotFoundError for missing or wrong-owner withdrawalId", async () => { + const { BridgeWithdrawalNotFoundError } = jest.requireActual("@services/bridge/errors") + + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + new RepositoryError("Withdrawal not found"), + ) + expect( + await BridgeService.initiateWithdrawal(ACCOUNT_ID, "missing-withdrawal"), + ).toBeInstanceOf(BridgeWithdrawalNotFoundError) + + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { accountId: "other-account" }), + ) + expect( + await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID), + ).toBeInstanceOf(BridgeWithdrawalNotFoundError) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("initiate returns BridgeWithdrawalAlreadyInitiatedError when the row was already submitted", async () => { + const { BridgeWithdrawalAlreadyInitiatedError } = jest.requireActual( + "@services/bridge/errors", + ) + + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { bridgeTransferId: TRANSFER_ID, status: "submitted" }), + ) + + const result = await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + expect(result).toBeInstanceOf(BridgeWithdrawalAlreadyInitiatedError) + expect(BridgeClient.createTransfer).not.toHaveBeenCalled() + }) + + it("cancel returns BridgeWithdrawalNotFoundError for missing or wrong-owner withdrawalId", async () => { + const { BridgeWithdrawalNotFoundError } = jest.requireActual("@services/bridge/errors") + + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + new RepositoryError("Withdrawal not found"), + ) + expect( + await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, "missing-withdrawal"), + ).toBeInstanceOf(BridgeWithdrawalNotFoundError) + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { accountId: "other-account" }), + ) + expect( + await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID), + ).toBeInstanceOf(BridgeWithdrawalNotFoundError) + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) + + it("cancel returns BridgeWithdrawalAlreadyInitiatedError when bridgeTransferId is already set", async () => { + const { BridgeWithdrawalAlreadyInitiatedError } = jest.requireActual( + "@services/bridge/errors", + ) + + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue( + makeRow(WITHDRAWAL_ID, { bridgeTransferId: TRANSFER_ID, status: "submitted" }), + ) + + const result = await BridgeService.cancelWithdrawalRequest(ACCOUNT_ID, WITHDRAWAL_ID) + expect(result).toBeInstanceOf(BridgeWithdrawalAlreadyInitiatedError) + expect(BridgeAccountsRepo.cancelWithdrawal).not.toHaveBeenCalled() + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) +}) + +// ───────────────────────────────────────────────────────────────────────────── +// getWithdrawals +// Returns the account's withdrawal history mapped to the GQL-facing shape +// (id/status/bridgeTransferId — NOT the old transferId/state fields). +// ───────────────────────────────────────────────────────────────────────────── + +describe("getWithdrawals", () => { + beforeEach(() => { + jest.clearAllMocks() + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), }) }) - describe("two rapid calls for the same request", () => { - it("pass the same idempotency key to Bridge — collapsing into one transfer", async () => { - // Call 1: no in-flight row → creates row A - // Call 2: finds row A (created by call 1) → reuses its id - const rowId = "shared-row-concurrent" - const row = makeRow(rowId) + it("maps submitted rows to id/status — not transferId or state", async () => { + ;(BridgeAccountsRepo.findWithdrawalsByAccountId as jest.Mock).mockResolvedValue([ + makeRow(WITHDRAWAL_ID, { bridgeTransferId: TRANSFER_ID, status: "submitted" }), + ]) + + const result = expectSuccess(await BridgeService.getWithdrawals(ACCOUNT_ID)) - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock) - .mockResolvedValueOnce(null) // call 1: nothing in-flight yet - .mockResolvedValueOnce(row) // call 2: row A now visible - ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) + expect(result).toHaveLength(1) + expect(result[0]).toMatchObject({ id: WITHDRAWAL_ID, status: "submitted" }) + expect((result[0] as Record).transferId).toBeUndefined() + expect((result[0] as Record).state).toBeUndefined() + }) - await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) - await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + it("includes bridgeTransferId for submitted/completed rows", async () => { + ;(BridgeAccountsRepo.findWithdrawalsByAccountId as jest.Mock).mockResolvedValue([ + makeRow(WITHDRAWAL_ID, { bridgeTransferId: TRANSFER_ID, status: "completed" }), + ]) - const calls = (BridgeClient.createTransfer as jest.Mock).mock.calls - expect(calls).toHaveLength(2) + const result = expectSuccess(await BridgeService.getWithdrawals(ACCOUNT_ID)) - const key1 = calls[0][2] - const key2 = calls[1][2] - expect(key1).toBe(key2) - expect(key1).toBe(deriveWithdrawalIdempotencyKey(rowId)) + expect(result[0]).toMatchObject({ + bridgeTransferId: TRANSFER_ID, + status: "completed", }) }) + + it("excludes pending rows that have no bridgeTransferId (pre-initiation)", async () => { + ;(BridgeAccountsRepo.findWithdrawalsByAccountId as jest.Mock).mockResolvedValue([ + makeRow(WITHDRAWAL_ID), // bridgeTransferId: undefined — pre-approval + ]) + + const result = expectSuccess(await BridgeService.getWithdrawals(ACCOUNT_ID)) + + expect(result).toHaveLength(0) + }) + + it("excludes cancelled rows without a bridgeTransferId, includes submitted/completed/failed", async () => { + ;(BridgeAccountsRepo.findWithdrawalsByAccountId as jest.Mock).mockResolvedValue([ + makeRow("w-1", { status: "pending" }), // excluded + makeRow("w-2", { status: "cancelled" }), // excluded (no transferId) + makeRow("w-3", { status: "submitted", bridgeTransferId: TRANSFER_ID }), + makeRow("w-4", { status: "completed", bridgeTransferId: "t-completed" }), + makeRow("w-5", { status: "failed", bridgeTransferId: "t-failed" }), + ]) + + const result = expectSuccess(await BridgeService.getWithdrawals(ACCOUNT_ID)) + + expect(result).toHaveLength(3) + expect(result.map((r) => r.status)).toEqual(["submitted", "completed", "failed"]) + }) + + it("formats createdAt as an ISO string", async () => { + ;(BridgeAccountsRepo.findWithdrawalsByAccountId as jest.Mock).mockResolvedValue([ + makeRow(WITHDRAWAL_ID, { bridgeTransferId: TRANSFER_ID, status: "submitted" }), + ]) + + const result = expectSuccess(await BridgeService.getWithdrawals(ACCOUNT_ID)) + + expect(result[0].createdAt).toBe(CREATED_AT.toISOString()) + }) }) diff --git a/test/flash/unit/services/bridge/return-shapes.spec.ts b/test/flash/unit/services/bridge/return-shapes.spec.ts new file mode 100644 index 000000000..ceba1affa --- /dev/null +++ b/test/flash/unit/services/bridge/return-shapes.spec.ts @@ -0,0 +1,242 @@ +/** + * Bridge service return shapes must match the public BridgeWithdrawal GraphQL type. + * + * Withdrawal mutation/query resolvers return BridgeService results directly with no + * resolver-level field mapping, so the service is the source of truth for the GQL + * contract: NonNull `id`, `amount`, `currency`, `status`, `createdAt`; optional + * `externalAccountId`, `bridgeTransferId`, `failureReason`. + */ +jest.mock("@services/tracing", () => ({ + wrapAsyncFunctionsToRunInSpan: ({ + fns, + }: { + namespace: string + fns: Record unknown> + }) => fns, +})) + +jest.mock("@config", () => ({ + ...jest.requireActual("@config"), + BridgeConfig: { enabled: true, minWithdrawalAmount: 10 }, +})) + +jest.mock("@services/logger", () => { + const logger = { + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + child: jest.fn(), + } + logger.child.mockReturnValue(logger) + return { baseLogger: logger } +}) + +jest.mock("@app/bridge/send-withdrawal-notification", () => ({ + sendBridgeWithdrawalNotificationBestEffort: jest.fn().mockResolvedValue(undefined), +})) + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + getEthereumUsdtOption: jest.fn(), + createCryptoReceiveInfo: jest.fn(), + }, +})) + +jest.mock("@services/mongoose/bridge-accounts", () => ({ + createWithdrawal: jest.fn(), + findPendingWithdrawalWithoutTransfer: jest.fn(), + findExternalAccountsByAccountId: jest.fn(), + findWithdrawalsByAccountId: jest.fn(), + findWithdrawalById: jest.fn(), + updateWithdrawalTransferId: jest.fn(), +})) + +jest.mock("@services/bridge/client", () => ({ + __esModule: true, + default: { createTransfer: jest.fn() }, +})) + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/wallets", () => ({ + WalletsRepository: jest.fn(), +})) + +jest.mock("@app/wallets/get-balance-for-wallet", () => ({ + getBalanceForWallet: jest.fn(), +})) + +jest.mock("@services/kratos", () => ({ + IdentityRepository: jest.fn(), +})) + +jest.mock("@domain/primitives/bridge", () => ({ + toBridgeCustomerId: (id: string) => id, + toBridgeExternalAccountId: (id: string) => id, +})) + +jest.mock("@domain/shared", () => { + class USDTAmount { + private readonly ibexValue: number + + constructor(ibexValue: number) { + this.ibexValue = ibexValue + } + + toIbex() { + return this.ibexValue + } + } + return { ...jest.requireActual("@domain/shared"), USDTAmount } +}) + +import BridgeService from "@services/bridge" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import BridgeClient from "@services/bridge/client" +import { AccountsRepository } from "@services/mongoose/accounts" +import { WalletsRepository } from "@services/mongoose/wallets" +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" + +const ACCOUNT_ID = "account-001" as AccountId +const EXTERNAL_ACCOUNT_ID = "ext-account-001" +const AMOUNT = "50" +const CUSTOMER_ID = "cust-001" +const ETHEREUM_ADDRESS = "ETH_ADDR_001" +const TRANSFER_ID = "transfer-bridge-001" +const WITHDRAWAL_ID = "withdrawal-mongo-001" +const CREATED_AT = new Date("2026-06-05T00:00:00.000Z") + +const mockAccount = { + id: ACCOUNT_ID, + level: 2, + bridgeCustomerId: CUSTOMER_ID, + bridgeEthereumAddress: ETHEREUM_ADDRESS, + bridgeKycStatus: "approved", + kratosUserId: "kratos-001", +} + +const mockTransfer = { + id: TRANSFER_ID, + amount: AMOUNT, + currency: "usd", + state: "pending", +} + +const makeRow = (overrides: Record = {}) => ({ + id: WITHDRAWAL_ID, + accountId: ACCOUNT_ID as string, + amount: AMOUNT, + currency: "usdt", + externalAccountId: EXTERNAL_ACCOUNT_ID, + status: "pending" as const, + bridgeTransferId: undefined, + failureReason: undefined, + createdAt: CREATED_AT, + ...overrides, +}) + +const setupGuards = () => { + const { USDTAmount } = jest.requireMock("@domain/shared") as { + USDTAmount: new (ibexValue: number) => { toIbex: () => number } + } + + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + }) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest + .fn() + .mockResolvedValue([{ id: "wallet-001", currency: "USDT", type: "checking" }]), + }) + ;(getBalanceForWallet as jest.Mock).mockResolvedValue(new USDTAmount(1000)) + ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ + { bridgeExternalAccountId: EXTERNAL_ACCOUNT_ID, status: "verified" }, + ]) + ;(BridgeAccountsRepo.findWithdrawalById as jest.Mock).mockResolvedValue(makeRow()) + ;(BridgeAccountsRepo.updateWithdrawalTransferId as jest.Mock).mockResolvedValue({ + ...makeRow(), + bridgeTransferId: TRANSFER_ID, + status: "submitted" as const, + }) + ;(BridgeClient.createTransfer as jest.Mock).mockResolvedValue(mockTransfer) +} + +describe("initiateWithdrawal — BridgeWithdrawal GraphQL contract shape", () => { + beforeEach(() => { + jest.clearAllMocks() + setupGuards() + }) + + it("returns every NonNull field required by the BridgeWithdrawal GraphQL type", async () => { + const result = await BridgeService.initiateWithdrawal(ACCOUNT_ID, WITHDRAWAL_ID) + + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + + expect(result.id).toBe(WITHDRAWAL_ID) + expect(result.amount).toBe(AMOUNT) + expect(result.currency).toBe("usdt") + expect(result.status).toBe("submitted") + expect(result.createdAt).toBe(CREATED_AT.toISOString()) + expect(result.bridgeTransferId).toBe(TRANSFER_ID) + expect((result as Record).transferId).toBeUndefined() + expect((result as Record).state).toBeUndefined() + }) +}) + +describe("getWithdrawals — BridgeWithdrawal GraphQL contract shape", () => { + beforeEach(() => { + jest.clearAllMocks() + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + }) + }) + + it("maps Mongo rows to id/status (not legacy transferId/state)", async () => { + ;(BridgeAccountsRepo.findWithdrawalsByAccountId as jest.Mock).mockResolvedValue([ + makeRow({ bridgeTransferId: TRANSFER_ID, status: "submitted", failureReason: "ACH return" }), + ]) + + const result = await BridgeService.getWithdrawals(ACCOUNT_ID) + + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result).toHaveLength(1) + expect(result[0]).toEqual({ + id: WITHDRAWAL_ID, + amount: AMOUNT, + currency: "usdt", + externalAccountId: EXTERNAL_ACCOUNT_ID, + status: "submitted", + bridgeTransferId: TRANSFER_ID, + failureReason: "ACH return", + createdAt: CREATED_AT.toISOString(), + }) + expect((result[0] as Record).transferId).toBeUndefined() + expect((result[0] as Record).state).toBeUndefined() + }) + + it("excludes rows without bridgeTransferId so NonNull id/status never resolve undefined", async () => { + ;(BridgeAccountsRepo.findWithdrawalsByAccountId as jest.Mock).mockResolvedValue([ + makeRow({ status: "pending" }), + makeRow({ id: "w-cancelled", status: "cancelled" }), + makeRow({ + id: "w-submitted", + bridgeTransferId: TRANSFER_ID, + status: "submitted", + }), + ]) + + const result = await BridgeService.getWithdrawals(ACCOUNT_ID) + + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result).toHaveLength(1) + expect(result[0].id).toBe("w-submitted") + expect(result[0].status).toBe("submitted") + }) +}) From 7e321f26e8b3a1bc7da2c1d961223831a32dc1e1 Mon Sep 17 00:00:00 2001 From: Patoo Date: Mon, 8 Jun 2026 09:14:54 -0400 Subject: [PATCH 31/43] feat(bridge): add KYC tier ceiling error code (ENG-354) (#394) --- docs/bridge-integration/API.md | 1 + src/graphql/error-map.ts | 17 +++++-- src/services/bridge/errors.ts | 50 +++++++++++++++++-- .../unit/graphql/bridge-error-map.spec.ts | 41 +++++++++++++++ 4 files changed, 102 insertions(+), 7 deletions(-) diff --git a/docs/bridge-integration/API.md b/docs/bridge-integration/API.md index f43046226..6ead13714 100644 --- a/docs/bridge-integration/API.md +++ b/docs/bridge-integration/API.md @@ -295,6 +295,7 @@ query BridgeWithdrawals { | `BRIDGE_KYC_PENDING` | Operation requires approved KYC, but it is still pending. | | `BRIDGE_KYC_REJECTED` | KYC was rejected. | | `BRIDGE_KYC_OFFBOARDED` | Bridge offboarded the customer. | +| `BRIDGE_KYC_TIER_CEILING_EXCEEDED` | Withdrawal amount exceeds the KYC tier ceiling. | | `BRIDGE_CUSTOMER_NOT_FOUND` | Bridge customer record not found for the user. | | `BRIDGE_WITHDRAWAL_NOT_FOUND` | Withdrawal request not found or does not belong to the caller. | | `BRIDGE_WITHDRAWAL_ALREADY_INITIATED` | Withdrawal was already submitted to Bridge. | diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 9defa8f00..dcaaaee13 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -541,6 +541,13 @@ export const mapError = (error: ApplicationError): CustomApolloError => { message, }) + case "BridgeKycTierCeilingExceededError": + message = error.message || "Withdrawal amount exceeds the KYC tier ceiling" + return bridgeGqlError({ + code: "BRIDGE_KYC_TIER_CEILING_EXCEEDED", + message, + }) + case "BridgeCustomerNotFoundError": message = "Bridge customer not found" return bridgeGqlError({ @@ -810,8 +817,9 @@ export const mapError = (error: ApplicationError): CustomApolloError => { case "InvalidCarrierForPhoneMetadataError": case "InvalidCarrierTypeForPhoneMetadataError": case "InvalidCountryCodeForPhoneMetadataError": - message = `Unexpected error occurred, please try again or contact support if it persists (code: ${error.name - }${error.message ? ": " + error.message : ""})` + message = `Unexpected error occurred, please try again or contact support if it persists (code: ${ + error.name + }${error.message ? ": " + error.message : ""})` return new UnexpectedClientError({ message, logger: baseLogger }) case "MissingSessionIdError": @@ -907,8 +915,9 @@ export const mapError = (error: ApplicationError): CustomApolloError => { return new ValidationInternalError({ message, logger: baseLogger }) case "UnknownCaptchaError": - message = `Unknown error occurred (code: ${error.name}${error.message ? ": " + error.message : "" - })` + message = `Unknown error occurred (code: ${error.name}${ + error.message ? ": " + error.message : "" + })` return new UnknownClientError({ message, logger: baseLogger }) default: diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index 9eff26707..5e0e125e4 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -46,7 +46,9 @@ export class BridgeKycRejectedError extends BridgeError { } export class BridgeKycOffboardedError extends BridgeError { - constructor(message: string = "Your account has been offboarded from Bridge. Please contact support.") { + constructor( + message: string = "Your account has been offboarded from Bridge. Please contact support.", + ) { super(message) } } @@ -58,7 +60,9 @@ export class BridgeInsufficientFundsError extends BridgeError { } export class BridgeAccountLevelError extends BridgeError { - constructor(message: string = "Bridge requires at least a Personal account (Level 1+)") { + constructor( + message: string = "Bridge requires at least a Personal account (Level 1+)", + ) { super(message) } } @@ -95,6 +99,12 @@ export class BridgeWebhookValidationError extends BridgeError { } } +export class BridgeKycTierCeilingExceededError extends BridgeError { + constructor(message: string = "Withdrawal amount exceeds the KYC tier ceiling") { + super(message) + } +} + export class BridgeWithdrawalNotFoundError extends BridgeError { constructor(message: string = "Withdrawal request not found") { super(message) @@ -102,18 +112,52 @@ export class BridgeWithdrawalNotFoundError extends BridgeError { } export class BridgeWithdrawalAlreadyInitiatedError extends BridgeError { - constructor(message: string = "Withdrawal has already been submitted to Bridge and cannot be cancelled") { + constructor( + message: string = "Withdrawal has already been submitted to Bridge and cannot be cancelled", + ) { super(message) } } /** * Maps HTTP status codes from Bridge API to domain error types + * + * Checks the response body for specific Bridge error types when applicable. */ export const mapBridgeHttpError = ( statusCode: number, response?: unknown, ): BridgeError => { + // Bridge returns 422/400 with a specific error type for KYC tier ceiling violations. + if ( + (statusCode === 422 || statusCode === 400) && + typeof response === "object" && + response !== null + ) { + const resp = response as Record + const errorObj = (resp.error ?? resp) as Record | undefined + const errorType = String(errorObj?.type ?? "").toLowerCase() + const errorMessage = String(errorObj?.message ?? resp?.message ?? "").toLowerCase() + + if ( + errorType.includes("kyc_tier_limit") || + errorType.includes("kyc_limit") || + errorType.includes("tier_ceiling") || + (errorMessage.includes("kyc") && + (errorMessage.includes("limit") || + errorMessage.includes("ceiling") || + errorMessage.includes("tier"))) + ) { + const message = + typeof errorObj?.message === "string" + ? errorObj.message + : typeof resp.message === "string" + ? resp.message + : undefined + return new BridgeKycTierCeilingExceededError(message) + } + } + switch (statusCode) { case 404: return new BridgeCustomerNotFoundError() diff --git a/test/flash/unit/graphql/bridge-error-map.spec.ts b/test/flash/unit/graphql/bridge-error-map.spec.ts index 194a83b39..286d6b2a3 100644 --- a/test/flash/unit/graphql/bridge-error-map.spec.ts +++ b/test/flash/unit/graphql/bridge-error-map.spec.ts @@ -11,10 +11,12 @@ import { BridgeKycOffboardedError, BridgeKycPendingError, BridgeKycRejectedError, + BridgeKycTierCeilingExceededError, BridgeRateLimitError, BridgeTimeoutError, BridgeTransferFailedError, BridgeWebhookValidationError, + mapBridgeHttpError, } from "@services/bridge/errors" describe("error-map: Bridge errors", () => { @@ -32,6 +34,7 @@ describe("error-map: Bridge errors", () => { [new BridgeTimeoutError(), "BRIDGE_TIMEOUT"], [new BridgeTransferFailedError(), "BRIDGE_TRANSFER_FAILED"], [new BridgeWebhookValidationError(), "BRIDGE_WEBHOOK_VALIDATION"], + [new BridgeKycTierCeilingExceededError(), "BRIDGE_KYC_TIER_CEILING_EXCEEDED"], [new BridgeApiError("Bridge API error", 500), "BRIDGE_API_ERROR"], [new BridgeError("Bridge unavailable"), "BRIDGE_ERROR"], ] @@ -52,3 +55,41 @@ describe("error-map: Bridge errors", () => { expect(result.message).toBeTruthy() }) }) + +describe("error-map: mapBridgeHttpError KYC tier ceiling detection", () => { + it("detects KYC tier ceiling via error.type", () => { + const result = mapBridgeHttpError(422, { + error: { type: "kyc_tier_limit_exceeded", message: "KYC tier limit reached" }, + }) + expect(result).toBeInstanceOf(BridgeKycTierCeilingExceededError) + expect(result.message).toBe("KYC tier limit reached") + }) + + it("detects KYC tier ceiling via error.type kyc_limit", () => { + const result = mapBridgeHttpError(400, { + error: { type: "kyc_limit_exceeded", message: "KYC limit exceeded" }, + }) + expect(result).toBeInstanceOf(BridgeKycTierCeilingExceededError) + }) + + it("detects KYC tier ceiling via response.message", () => { + const result = mapBridgeHttpError(422, { + message: "exceeds kyc ceiling", + }) + expect(result).toBeInstanceOf(BridgeKycTierCeilingExceededError) + }) + + it("does not detect on unrelated 422 errors", () => { + const result = mapBridgeHttpError(422, { + error: { type: "validation_error", message: "Invalid amount" }, + }) + expect(result).not.toBeInstanceOf(BridgeKycTierCeilingExceededError) + }) + + it("does not detect on non-422/400 errors", () => { + const result = mapBridgeHttpError(500, { + error: { type: "kyc_tier_limit_exceeded", message: "KYC tier limit" }, + }) + expect(result).not.toBeInstanceOf(BridgeKycTierCeilingExceededError) + }) +}) From c6db7d6a2f737b08d2a1a7f2640f4ff5ecd21b29 Mon Sep 17 00:00:00 2001 From: Island Bitcoin <34528298+islandbitcoin@users.noreply.github.com> Date: Tue, 9 Jun 2026 19:07:21 -0700 Subject: [PATCH 32/43] feat(alerts): Bridge alerting to PagerDuty / Slack / Discord [ENG-361] (#391) * feat(alerts): add Bridge AlertService (PagerDuty/Slack/Discord) [ENG-361] Severity-routed best-effort alert fan-out: critical pages PagerDuty + informs Slack/Mattermost + Discord; warning informs only. Each sender no-ops when its env credential is unset. Config: ALERT_PAGERDUTY_ROUTING_KEY / ALERT_SLACK_WEBHOOK_URL / ALERT_DISCORD_WEBHOOK_URL. Co-Authored-By: Claude Opus 4.8 (1M context) * feat(alerts): wire Bridge alert sources to AlertService [ENG-361] Fire-and-forget alertBridge() at the Bridge failure points (alongside existing logging), all critical/page: - ERPNext audit-write failures (deposit + transfer completed/failed) - Bridge webhook processing exceptions (deposit + transfer catch) - Bridge API outage in client.request(): 5xx / timeout / network (4xx not alerted) IBEX-error source deferred: on-receive.ts is general LN/onchain receive handling, not the Bridge<->IBEX movement path; needs the exact call site (warning sev). Co-Authored-By: Claude Opus 4.8 (1M context) * docs(alerts): add Bridge alerting setup guide [ENG-361] Co-Authored-By: Claude Opus 4.8 (1M context) * chore(alerts): clean bridge alert lint and deposit test * feat(alerts): dedupe Bridge alerts and wire IBEX movement warnings [ENG-361] Group PagerDuty triggers with dedup_key, suppress duplicate Slack/Discord informs, and alert IBEX crypto-receive and reconciliation failures as warnings. * chore(alerts): format bridge alert updates * chore(alerts): trim dedup cleanup noise --------- Co-authored-by: Dread Co-authored-by: Claude Opus 4.8 (1M context) Co-authored-by: Vandana Co-authored-by: heyolaniran --- docs/bridge-integration/ALERTING.md | 91 +++++++++++++ src/config/env.ts | 8 ++ src/config/index.ts | 3 + src/services/alerts/dedup-key.ts | 34 +++++ src/services/alerts/discord.ts | 34 +++++ src/services/alerts/ibex-bridge-movement.ts | 89 +++++++++++++ src/services/alerts/index.ts | 46 +++++++ src/services/alerts/index.types.ts | 14 ++ src/services/alerts/inform-dedup.ts | 35 +++++ src/services/alerts/pagerduty.ts | 34 +++++ src/services/alerts/slack.ts | 31 +++++ src/services/bridge/client.ts | 72 +++++++--- src/services/bridge/reconciliation.ts | 61 ++++++++- .../bridge/webhook-server/routes/deposit.ts | 32 ++++- .../bridge/webhook-server/routes/transfer.ts | 25 ++++ .../webhook-server/routes/crypto-receive.ts | 64 ++++++++- .../unit/services/alerts/dedup-key.spec.ts | 54 ++++++++ .../alerts/ibex-bridge-movement.spec.ts | 59 +++++++++ test/flash/unit/services/alerts/index.spec.ts | 76 +++++++++++ .../unit/services/alerts/inform-dedup.spec.ts | 34 +++++ .../unit/services/alerts/pagerduty.spec.ts | 44 ++++++ .../services/bridge/reconciliation.spec.ts | 125 ++++++++++++------ .../bridge/webhook-server/deposit.spec.ts | 7 +- .../routes/crypto-receive.spec.ts | 17 +++ 24 files changed, 1021 insertions(+), 68 deletions(-) create mode 100644 docs/bridge-integration/ALERTING.md create mode 100644 src/services/alerts/dedup-key.ts create mode 100644 src/services/alerts/discord.ts create mode 100644 src/services/alerts/ibex-bridge-movement.ts create mode 100644 src/services/alerts/index.ts create mode 100644 src/services/alerts/index.types.ts create mode 100644 src/services/alerts/inform-dedup.ts create mode 100644 src/services/alerts/pagerduty.ts create mode 100644 src/services/alerts/slack.ts create mode 100644 test/flash/unit/services/alerts/dedup-key.spec.ts create mode 100644 test/flash/unit/services/alerts/ibex-bridge-movement.spec.ts create mode 100644 test/flash/unit/services/alerts/index.spec.ts create mode 100644 test/flash/unit/services/alerts/inform-dedup.spec.ts create mode 100644 test/flash/unit/services/alerts/pagerduty.spec.ts diff --git a/docs/bridge-integration/ALERTING.md b/docs/bridge-integration/ALERTING.md new file mode 100644 index 000000000..270621a8b --- /dev/null +++ b/docs/bridge-integration/ALERTING.md @@ -0,0 +1,91 @@ +# Bridge Alerting (ENG-361) + +Operational alerting for the Bridge integration. When a Bridge signal fails +(webhook processing, ERPNext audit write, or a Bridge API outage), the +`AlertService` (`src/services/alerts`) fans the alert out to the configured +destinations. + +## Routing + +| Severity | PagerDuty (page) | Slack / Mattermost (inform) | Discord (inform) | +| ------------ | :--------------: | :-------------------------: | :--------------: | +| **critical** | ✅ | ✅ | ✅ | +| **warning** | — | ✅ | ✅ | + +Delivery is best-effort and fire-and-forget — a failing or unconfigured +destination never blocks or fails the webhook/request path. **A destination +with no configured credential is silently skipped**, so channels can be enabled +incrementally. + +### Deduplication + +Alerts carry a stable `dedupKey` so repeated failures do not spam on-call or chat: + +| Destination | Behavior | +| ----------- | -------- | +| **PagerDuty** | Events API v2 `dedup_key` groups triggers into one incident | +| **Slack / Discord** | First message per `dedupKey` within TTL; duplicates are skipped | + +Key classes (see `src/services/alerts/dedup-key.ts`): + +- `bridge-api:5xx` / `bridge-api:timeout` / `bridge-api:network` — coarse outage keys (30 min inform TTL) +- `erpnext-audit:deposit:{transfer_id}` — per deposit audit failure (1 h inform TTL) +- `erpnext-audit:transfer-complete:{transfer_id}` / `transfer-failed:{transfer_id}` — per transfer audit failure +- `bridge-webhook:deposit:{event_id}` / `bridge-webhook:transfer:{transfer_id}:{event}` — per webhook processing error +- `ibex:crypto-receive:{tx_hash}` — per IBEX crypto receive webhook failure (1 h inform TTL) +- `ibex:reconcile:bridge-without-ibex:{tx_hash}` / `ibex:reconcile:ibex-without-bridge:{tx_hash}` — per reconciliation orphan +- `ibex:reconcile:failed:{tx_hash}` — reconciliation handler threw + +Inform dedup is in-process per pod; PagerDuty dedup is global to the service integration. + +## Alert sources + +| Source | Severity | Where | +| ------------------------------------------------- | -------- | ----------------------------------------------------------- | +| ERPNext audit-write failure (deposit + transfer) | critical | `services/bridge/webhook-server/routes/{deposit,transfer}.ts` | +| Bridge webhook processing exception | critical | same routes (catch block) | +| Bridge API outage — 5xx / timeout / network | critical | `services/bridge/client.ts` | +| IBEX crypto receive webhook failure | warning | `services/ibex/webhook-server/routes/crypto-receive.ts` | +| Bridge↔IBEX reconciliation orphan / failure | warning | `services/bridge/reconciliation.ts`, deposit/crypto catch | + +`4xx` responses from Bridge are normal API rejections and are **not** alerted. + +## Configuration + +Three optional env vars, each gating one destination: + +| Env var | Destination | Value | +| ----------------------------- | ------------------- | ------------------------------------------- | +| `ALERT_PAGERDUTY_ROUTING_KEY` | PagerDuty | Events API v2 **integration / routing key** | +| `ALERT_SLACK_WEBHOOK_URL` | Slack or Mattermost | Incoming-webhook URL | +| `ALERT_DISCORD_WEBHOOK_URL` | Discord | Channel webhook URL | + +### How to get each value + +**PagerDuty** — `ALERT_PAGERDUTY_ROUTING_KEY` +1. PagerDuty → **Services** → pick (or create) the service that should page for Bridge. +2. **Integrations** → **Add integration** → **Events API v2**. +3. Copy the **Integration Key** — that is the routing key. + +**Slack** — `ALERT_SLACK_WEBHOOK_URL` +1. Create/choose a Slack app → **Incoming Webhooks** → **Activate**. +2. **Add New Webhook to Workspace** → choose the target channel. +3. Copy the URL (`https://hooks.slack.com/services/...`). + _Mattermost works too_ — it accepts the same `{ text }` payload; use its incoming-webhook URL. + +**Discord** — `ALERT_DISCORD_WEBHOOK_URL` +1. Discord → target channel → **Edit Channel** → **Integrations** → **Webhooks**. +2. **New Webhook** → name it → **Copy Webhook URL**. + +### Where to set them + +- **Local dev:** add to `.env` (and `.env.ci` for CI). +- **Staging / production:** set as environment variables / secrets in the deployment — the same place `MATTERMOST_WEBHOOK_URL` is configured. Treat all three as **secrets**. + +> If none are set, alerting is a no-op (no errors, no delivery) — useful until the channels are provisioned. + +## Verifying in staging (ENG-361 acceptance) + +1. Set at least `ALERT_PAGERDUTY_ROUTING_KEY` and `ALERT_SLACK_WEBHOOK_URL` in staging. +2. Simulate a Bridge webhook failure (e.g. force an ERPNext audit-write error, or replay a malformed transfer webhook). +3. Confirm on-call is paged via PagerDuty **and** a message posts to Slack within ~1 minute. diff --git a/src/config/env.ts b/src/config/env.ts index 1216528c0..fe2397f8a 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -124,6 +124,10 @@ export const env = createEnv({ MATTERMOST_WEBHOOK_URL: z.string().min(1).optional(), + ALERT_PAGERDUTY_ROUTING_KEY: z.string().min(1).optional(), + ALERT_SLACK_WEBHOOK_URL: z.string().url().optional(), + ALERT_DISCORD_WEBHOOK_URL: z.string().url().optional(), + PROXY_CHECK_APIKEY: z.string().min(1).optional(), SVIX_SECRET: z.string().optional(), @@ -231,6 +235,10 @@ export const env = createEnv({ MATTERMOST_WEBHOOK_URL: process.env.MATTERMOST_WEBHOOK_URL, + ALERT_PAGERDUTY_ROUTING_KEY: process.env.ALERT_PAGERDUTY_ROUTING_KEY, + ALERT_SLACK_WEBHOOK_URL: process.env.ALERT_SLACK_WEBHOOK_URL, + ALERT_DISCORD_WEBHOOK_URL: process.env.ALERT_DISCORD_WEBHOOK_URL, + PROXY_CHECK_APIKEY: process.env.PROXY_CHECK_APIKEY, SVIX_SECRET: process.env.SVIX_SECRET, diff --git a/src/config/index.ts b/src/config/index.ts index 2b070e1a0..749a0933f 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -188,6 +188,9 @@ export const NEXTCLOUD_URL = env.NEXTCLOUD_URL export const NEXTCLOUD_USER = env.NEXTCLOUD_USER export const NEXTCLOUD_PASSWORD = env.NEXTCLOUD_PASSWORD export const MATTERMOST_WEBHOOK_URL = env.MATTERMOST_WEBHOOK_URL +export const ALERT_PAGERDUTY_ROUTING_KEY = env.ALERT_PAGERDUTY_ROUTING_KEY +export const ALERT_SLACK_WEBHOOK_URL = env.ALERT_SLACK_WEBHOOK_URL +export const ALERT_DISCORD_WEBHOOK_URL = env.ALERT_DISCORD_WEBHOOK_URL export const PROXY_CHECK_APIKEY = env.PROXY_CHECK_APIKEY export const NOSTR_PRIVATE_KEY = env.NOSTR_PRIVATE_KEY diff --git a/src/services/alerts/dedup-key.ts b/src/services/alerts/dedup-key.ts new file mode 100644 index 000000000..2a1cb2584 --- /dev/null +++ b/src/services/alerts/dedup-key.ts @@ -0,0 +1,34 @@ +export const PAGERDUTY_DEDUP_KEY_MAX = 255 + +const OUTAGE_TTL_MS = 30 * 60 * 1000 +const DEFAULT_TTL_MS = 60 * 60 * 1000 + +/** TTL for Slack/Discord first-alert suppression per dedup key class. */ +export const informDedupTtlMs = (dedupKey: string): number => + dedupKey.startsWith("bridge-api") ? OUTAGE_TTL_MS : DEFAULT_TTL_MS + +export const generateDedupKey = { + bridgeApi5xx: () => "bridge-api:5xx", + bridgeApiTimeout: () => "bridge-api:timeout", + bridgeApiNetwork: () => "bridge-api:network", + erpnextDepositAudit: (transferId: string) => `erpnext-audit:deposit:${transferId}`, + erpnextTransferCompletedAudit: (transferId: string) => + `erpnext-audit:transfer-complete:${transferId}`, + erpnextTransferFailedAudit: (transferId: string) => + `erpnext-audit:transfer-failed:${transferId}`, + bridgeWebhookDeposit: (eventId: string) => `bridge-webhook:deposit:${eventId}`, + bridgeWebhookTransfer: (transferId: string, event: string) => + `bridge-webhook:transfer:${transferId}:${event}`, + ibexCryptoReceive: (txHash: string) => `ibex:crypto-receive:${txHash.toLowerCase()}`, + ibexReconcileBridgeWithoutIbex: (txHash: string) => + `ibex:reconcile:bridge-without-ibex:${txHash.toLowerCase()}`, + ibexReconcileBridgeWithoutIbexTransfer: (transferId: string) => + `ibex:reconcile:bridge-without-ibex:transfer:${transferId}`, + ibexReconcileIbexWithoutBridge: (txHash: string) => + `ibex:reconcile:ibex-without-bridge:${txHash.toLowerCase()}`, + ibexReconcileFailed: (txHash: string) => + `ibex:reconcile:failed:${txHash.toLowerCase()}`, +} + +export const normalizeDedupKey = (key: string): string => + key.length <= PAGERDUTY_DEDUP_KEY_MAX ? key : key.slice(0, PAGERDUTY_DEDUP_KEY_MAX) diff --git a/src/services/alerts/discord.ts b/src/services/alerts/discord.ts new file mode 100644 index 000000000..df9ba0bac --- /dev/null +++ b/src/services/alerts/discord.ts @@ -0,0 +1,34 @@ +import { ALERT_DISCORD_WEBHOOK_URL } from "@config" +import { ErrorLevel } from "@domain/shared" +import { recordExceptionInCurrentSpan } from "@services/tracing" +import axios from "axios" + +import { BridgeAlert } from "./index.types" + +// Discord caps message content at 2000 chars; leave headroom. +const DISCORD_CONTENT_MAX = 1900 + +// Discord incoming webhook ({ content }). +export const sendDiscord = async (alert: BridgeAlert): Promise => { + if (!ALERT_DISCORD_WEBHOOK_URL) return + + const label = alert.severity === "critical" ? "[CRITICAL]" : "[WARNING]" + let content = `${label} **Bridge alert** - ${alert.title}\nsource: \`${alert.source}\` | severity: \`${alert.severity}\`` + if (alert.detail) content += `\n${alert.detail}` + if (alert.context) { + content += "\n```json\n" + JSON.stringify(alert.context, null, 2) + "\n```" + } + if (content.length > DISCORD_CONTENT_MAX) { + content = content.slice(0, DISCORD_CONTENT_MAX) + "..." + } + + try { + await axios.post( + ALERT_DISCORD_WEBHOOK_URL, + { content }, + { timeout: 5000, headers: { "Content-Type": "application/json" } }, + ) + } catch (error) { + recordExceptionInCurrentSpan({ error, level: ErrorLevel.Warn }) + } +} diff --git a/src/services/alerts/ibex-bridge-movement.ts b/src/services/alerts/ibex-bridge-movement.ts new file mode 100644 index 000000000..a8bfd8ab8 --- /dev/null +++ b/src/services/alerts/ibex-bridge-movement.ts @@ -0,0 +1,89 @@ +import { generateDedupKey } from "./dedup-key" + +import { alertBridge } from "./index" + +type IbexMovementAlert = { + title: string + detail?: string + context?: Record +} + +const alertIbexMovement = (dedupKey: string, alert: IbexMovementAlert): void => { + alertBridge({ + dedupKey, + source: "ibex", + severity: "warning", + ...alert, + }) +} + +export const alertIbexCryptoReceiveFailure = ({ + txHash, + code, + title, + detail, + context, +}: { + txHash: string + code: string + title: string + detail?: string + context?: Record +}): void => { + alertIbexMovement(generateDedupKey.ibexCryptoReceive(txHash), { + title, + detail, + context: { tx_hash: txHash, code, ...context }, + }) +} + +export const alertIbexReconciliationOrphan = ({ + orphanType, + txHash, + transferId, + reason, + context, +}: { + orphanType: "bridge_without_ibex" | "ibex_without_bridge" + txHash?: string + transferId?: string + reason: string + context?: Record +}): void => { + const dedupKey = + orphanType === "ibex_without_bridge" && txHash + ? generateDedupKey.ibexReconcileIbexWithoutBridge(txHash) + : txHash + ? generateDedupKey.ibexReconcileBridgeWithoutIbex(txHash) + : generateDedupKey.ibexReconcileBridgeWithoutIbexTransfer(transferId ?? "unknown") + + const title = + orphanType === "ibex_without_bridge" + ? "IBEX crypto receive without matching Bridge deposit" + : "Bridge deposit without matching IBEX crypto receive" + + alertIbexMovement(dedupKey, { + title, + detail: reason, + context: { + orphan_type: orphanType, + tx_hash: txHash, + transfer_id: transferId, + ...context, + }, + }) +} + +export const alertIbexReconciliationFailed = ({ + txHash, + detail, +}: { + txHash: string + detail: string +}): void => { + alertIbexMovement(generateDedupKey.ibexReconcileFailed(txHash), { + title: "Bridge↔IBEX reconciliation failed", + detail, + context: { tx_hash: txHash }, + }) +} diff --git a/src/services/alerts/index.ts b/src/services/alerts/index.ts new file mode 100644 index 000000000..95f7a8255 --- /dev/null +++ b/src/services/alerts/index.ts @@ -0,0 +1,46 @@ +import { sendPagerDuty } from "./pagerduty" +import { sendSlack } from "./slack" +import { sendDiscord } from "./discord" +import { normalizeDedupKey } from "./dedup-key" +import { claimInformSlot } from "./inform-dedup" +import { BridgeAlert } from "./index.types" + +export * from "./index.types" +export { generateDedupKey } from "./dedup-key" + +/** + * Fire-and-forget fan-out of a Bridge alert to the configured destinations + * (ENG-361). Returns immediately; delivery is best-effort: each sender catches + * its own errors and no-ops when its credential/URL is unset, so it never throws + * or rejects into the caller (no need to await or handle it). + * + * Routing: + * - critical: page on-call (PagerDuty) + inform (Slack/Mattermost, Discord) + * - warning: inform (Slack/Mattermost, Discord) only + * + * Dedup: + * - PagerDuty: Events API v2 dedup_key groups triggers into one incident. + * - Slack / Discord: first alert per dedup key within TTL only. + */ +export const alertBridge = (alert: BridgeAlert): void => { + const dedupKey = normalizeDedupKey(alert.dedupKey) + const alertWithKey: BridgeAlert = { ...alert, dedupKey } + + const deliver = async () => { + const senders: Promise[] = [] + + if (claimInformSlot(dedupKey)) { + senders.push(sendSlack(alertWithKey), sendDiscord(alertWithKey)) + } + + if (alert.severity === "critical") { + senders.push(sendPagerDuty(alertWithKey)) + } + + if (senders.length > 0) { + await Promise.allSettled(senders) + } + } + + deliver().catch(() => undefined) +} diff --git a/src/services/alerts/index.types.ts b/src/services/alerts/index.types.ts new file mode 100644 index 000000000..b93b6c91c --- /dev/null +++ b/src/services/alerts/index.types.ts @@ -0,0 +1,14 @@ +// Ops alerting for Bridge integration signals (ENG-361). + +export type AlertSeverity = "critical" | "warning" + +export type AlertSource = "bridge-webhook" | "bridge-api" | "ibex" | "erpnext-audit" + +export interface BridgeAlert { + dedupKey: string + source: AlertSource + severity: AlertSeverity + title: string + detail?: string + context?: Record +} diff --git a/src/services/alerts/inform-dedup.ts b/src/services/alerts/inform-dedup.ts new file mode 100644 index 000000000..7e0bc7884 --- /dev/null +++ b/src/services/alerts/inform-dedup.ts @@ -0,0 +1,35 @@ +import { informDedupTtlMs } from "./dedup-key" + +const seenAt = new Map() + +/** + * Returns true when Slack/Discord should fire for this dedup key (first within TTL). + * Subsequent duplicates within the TTL are suppressed. + */ +export const claimInformSlot = (dedupKey: string, nowMs = Date.now()): boolean => { + const ttlMs = informDedupTtlMs(dedupKey) + const lastSentAt = seenAt.get(dedupKey) + + if (lastSentAt !== undefined && nowMs - lastSentAt < ttlMs) { + return false + } + + seenAt.set(dedupKey, nowMs) + pruneExpired(nowMs) + return true +} + +const pruneExpired = (nowMs: number): void => { + if (seenAt.size < 500) return + + for (const [key, sentAt] of seenAt) { + if (nowMs - sentAt >= informDedupTtlMs(key)) { + seenAt.delete(key) + } + } +} + +/** Test helper — clears the in-process inform dedup cache. */ +export const resetInformDedup = (): void => { + seenAt.clear() +} diff --git a/src/services/alerts/pagerduty.ts b/src/services/alerts/pagerduty.ts new file mode 100644 index 000000000..46d8317e5 --- /dev/null +++ b/src/services/alerts/pagerduty.ts @@ -0,0 +1,34 @@ +import { ALERT_PAGERDUTY_ROUTING_KEY } from "@config" +import { ErrorLevel } from "@domain/shared" +import { recordExceptionInCurrentSpan } from "@services/tracing" +import axios from "axios" + +import { BridgeAlert } from "./index.types" + +const PAGERDUTY_EVENTS_URL = "https://events.pagerduty.com/v2/enqueue" + +// PagerDuty Events API v2 triggers a paging incident. "critical" and +// "warning" are both valid PD payload severities, so we pass them through. +export const sendPagerDuty = async (alert: BridgeAlert): Promise => { + if (!ALERT_PAGERDUTY_ROUTING_KEY) return + + try { + await axios.post( + PAGERDUTY_EVENTS_URL, + { + routing_key: ALERT_PAGERDUTY_ROUTING_KEY, + event_action: "trigger", + dedup_key: alert.dedupKey, + payload: { + summary: `[bridge:${alert.source}] ${alert.title}`, + severity: alert.severity, + source: "flash-bridge", + custom_details: { ...alert.context, detail: alert.detail }, + }, + }, + { timeout: 5000, headers: { "Content-Type": "application/json" } }, + ) + } catch (error) { + recordExceptionInCurrentSpan({ error, level: ErrorLevel.Warn }) + } +} diff --git a/src/services/alerts/slack.ts b/src/services/alerts/slack.ts new file mode 100644 index 000000000..10a260088 --- /dev/null +++ b/src/services/alerts/slack.ts @@ -0,0 +1,31 @@ +import { ALERT_SLACK_WEBHOOK_URL } from "@config" +import { ErrorLevel } from "@domain/shared" +import { recordExceptionInCurrentSpan } from "@services/tracing" +import axios from "axios" + +import { BridgeAlert } from "./index.types" + +// Slack / Mattermost-compatible incoming webhook ({ text }). +export const sendSlack = async (alert: BridgeAlert): Promise => { + if (!ALERT_SLACK_WEBHOOK_URL) return + + const icon = alert.severity === "critical" ? ":rotating_light:" : ":warning:" + const lines = [ + `${icon} *Bridge alert* - ${alert.title}`, + `*source:* \`${alert.source}\` *severity:* \`${alert.severity}\``, + ] + if (alert.detail) lines.push(alert.detail) + if (alert.context) { + lines.push("```" + JSON.stringify(alert.context, null, 2) + "```") + } + + try { + await axios.post( + ALERT_SLACK_WEBHOOK_URL, + { text: lines.join("\n") }, + { timeout: 5000, headers: { "Content-Type": "application/json" } }, + ) + } catch (error) { + recordExceptionInCurrentSpan({ error, level: ErrorLevel.Warn }) + } +} diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index 363e535bf..d6a8c9310 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -7,7 +7,13 @@ import crypto from "crypto" import { BridgeConfig } from "@config" -import { BridgeCustomerId, BridgeTransferId, BridgeVirtualAccountId } from "@domain/primitives/bridge" +import { + BridgeCustomerId, + BridgeTransferId, + BridgeVirtualAccountId, +} from "@domain/primitives/bridge" +import { alertBridge, generateDedupKey } from "@services/alerts" + import { BridgeTimeoutError } from "./errors" // ============ Error Handling ============ @@ -67,15 +73,15 @@ export interface Customer { id: string type: "individual" | "business" status?: - | "active" - | "awaiting_questionnaire" - | "rejected" - | "paused" - | "under_review" - | "offboarded" - | "awaiting_ubo" - | "incomplete" - | "not_started" + | "active" + | "awaiting_questionnaire" + | "rejected" + | "paused" + | "under_review" + | "offboarded" + | "awaiting_ubo" + | "incomplete" + | "not_started" has_accepted_terms_of_service?: string created_at: string updated_at: string @@ -379,6 +385,17 @@ export class BridgeClient { const responseData = await response.json().catch(() => null) if (!response.ok) { + // Only 5xx indicates a Bridge-side outage; 4xx are normal API rejections. + if (response.status >= 500) { + alertBridge({ + dedupKey: generateDedupKey.bridgeApi5xx(), + source: "bridge-api", + severity: "critical", + title: `Bridge API ${response.status} on ${method} ${path}`, + detail: response.statusText, + context: { method, path, status: response.status }, + }) + } throw new BridgeApiError( `Bridge API error: ${response.status} ${response.statusText}`, response.status, @@ -389,8 +406,26 @@ export class BridgeClient { return responseData as T } catch (err) { if (err instanceof Error && err.name === "AbortError") { + alertBridge({ + dedupKey: generateDedupKey.bridgeApiTimeout(), + source: "bridge-api", + severity: "critical", + title: `Bridge API timeout on ${method} ${path}`, + context: { method, path, timeoutMs }, + }) throw new BridgeTimeoutError() } + // Network/connectivity failures (5xx already alerted above). + if (!(err instanceof BridgeApiError)) { + alertBridge({ + dedupKey: generateDedupKey.bridgeApiNetwork(), + source: "bridge-api", + severity: "critical", + title: `Bridge API request failed on ${method} ${path}`, + detail: err instanceof Error ? err.message : String(err), + context: { method, path }, + }) + } throw err } finally { clearTimeout(timeoutId) @@ -439,7 +474,9 @@ export class BridgeClient { } async getVirtualAccount( - customerId: BridgeCustomerId, virtualAccountId: BridgeVirtualAccountId, idempotencyKey?: string, + customerId: BridgeCustomerId, + virtualAccountId: BridgeVirtualAccountId, + idempotencyKey?: string, ): Promise { return this.request( "GET", @@ -449,16 +486,15 @@ export class BridgeClient { ) } - - async getVirtualAccountByCustomerId(customerId: BridgeCustomerId): Promise { + async getVirtualAccountByCustomerId( + customerId: BridgeCustomerId, + ): Promise { const response = await this.request<{ data: VirtualAccount[] }>( "GET", `/customers/${customerId}/virtual_accounts`, ) return response.data as VirtualAccount[] - - } // ============ External Accounts ============ @@ -567,8 +603,10 @@ export async function* listAllEvents( const startMs = params?.start ? new Date(params.start).getTime() : -Infinity const endMs = params?.end ? new Date(params.end).getTime() : Infinity - // Strip start/end — Bridge /webhook_events only supports cursor params; filter locally. - const { start: _s, end: _e, ...apiParams } = params ?? {} + // Strip start/end: Bridge /webhook_events only supports cursor params; filter locally. + const apiParams = { ...(params ?? {}) } + delete apiParams.start + delete apiParams.end let cursor: string | undefined do { diff --git a/src/services/bridge/reconciliation.ts b/src/services/bridge/reconciliation.ts index 851ebce90..26ef10ada 100644 --- a/src/services/bridge/reconciliation.ts +++ b/src/services/bridge/reconciliation.ts @@ -1,3 +1,4 @@ +import { alertIbexReconciliationOrphan } from "@services/alerts/ibex-bridge-movement" import { baseLogger } from "@services/logger" import { findIbexCryptoReceivesSince } from "@services/mongoose/ibex-crypto-receive-log" import { @@ -78,6 +79,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ for (const deposit of bridgeDeposits) { if (!deposit.destinationTxHash) { bridgeWithoutIbex++ + const reason = "Bridge payment_processed has no destinationTxHash" await upsertBridgeReconciliationOrphan({ orphanKey: toOrphanKey("bridge-no-tx", deposit.transferId), orphanType: "bridge_without_ibex", @@ -87,13 +89,24 @@ export const reconcileBridgeAndIbexDeposits = async ({ amount: deposit.amount, currency: deposit.currency, triageContext: { - reason: "Bridge payment_processed has no destinationTxHash", + reason, windowStart: since.toISOString(), windowEnd: now.toISOString(), depositState: deposit.state, createdAt: deposit.createdAt.toISOString(), }, }) + alertIbexReconciliationOrphan({ + orphanType: "bridge_without_ibex", + transferId: deposit.transferId, + reason, + context: { + bridge_event_id: deposit.eventId, + customer_id: deposit.customerId, + amount: deposit.amount, + currency: deposit.currency, + }, + }) continue } @@ -101,6 +114,8 @@ export const reconcileBridgeAndIbexDeposits = async ({ if (matchedIbex) continue bridgeWithoutIbex++ + const reason = + "No IBEX crypto.receive found for Bridge destinationTxHash within window" await upsertBridgeReconciliationOrphan({ orphanKey: toOrphanKey("bridge", deposit.destinationTxHash), orphanType: "bridge_without_ibex", @@ -111,14 +126,25 @@ export const reconcileBridgeAndIbexDeposits = async ({ amount: deposit.amount, currency: deposit.currency, triageContext: { - reason: - "No IBEX crypto.receive found for Bridge destinationTxHash within window", + reason, windowStart: since.toISOString(), windowEnd: now.toISOString(), depositState: deposit.state, createdAt: deposit.createdAt.toISOString(), }, }) + alertIbexReconciliationOrphan({ + orphanType: "bridge_without_ibex", + txHash: deposit.destinationTxHash, + transferId: deposit.transferId, + reason, + context: { + bridge_event_id: deposit.eventId, + customer_id: deposit.customerId, + amount: deposit.amount, + currency: deposit.currency, + }, + }) } for (const receive of ibexReceives) { @@ -126,6 +152,8 @@ export const reconcileBridgeAndIbexDeposits = async ({ if (matchedBridge) continue ibexWithoutBridge++ + const reason = + "No Bridge deposit payment_processed found for IBEX tx hash within window" await upsertBridgeReconciliationOrphan({ orphanKey: toOrphanKey("ibex", receive.txHash), orphanType: "ibex_without_bridge", @@ -133,8 +161,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ amount: receive.amount, currency: receive.currency, triageContext: { - reason: - "No Bridge deposit payment_processed found for IBEX tx hash within window", + reason, windowStart: since.toISOString(), windowEnd: now.toISOString(), address: receive.address, @@ -143,6 +170,18 @@ export const reconcileBridgeAndIbexDeposits = async ({ receivedAt: receive.receivedAt.toISOString(), }, }) + alertIbexReconciliationOrphan({ + orphanType: "ibex_without_bridge", + txHash: receive.txHash, + reason, + context: { + amount: receive.amount, + currency: receive.currency, + address: receive.address, + network: receive.network, + account_id: receive.accountId, + }, + }) } const summary = { @@ -263,6 +302,18 @@ export const reconcileByTxHash = async ({ triageContext, }) + alertIbexReconciliationOrphan({ + orphanType, + txHash: normalizedHash, + transferId, + reason: String(triageContext.reason), + context: { + customer_id: customerId, + amount, + currency, + }, + }) + const event: ReconcileByTxHashResult = { txHash: normalizedHash, status: "unmatched", diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index 4fe09f432..33e163147 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -12,6 +12,8 @@ import { baseLogger } from "@services/logger" import { createBridgeDeposit } from "@services/mongoose/bridge-deposit-log" import { reconcileByTxHash } from "@services/bridge/reconciliation" import { writeBridgeDepositRequest } from "@services/frappe/BridgeTransferRequestWriter" +import { alertBridge, generateDedupKey } from "@services/alerts" +import { alertIbexReconciliationFailed } from "@services/alerts/ibex-bridge-movement" export const depositHandler = async (req: Request, res: Response) => { const { event_id, event_object } = req.body @@ -79,9 +81,13 @@ export const depositHandler = async (req: Request, res: Response) => { } if (state === "payment_processed" && receipt?.destination_tx_hash) { - reconcileByTxHash({ txHash: receipt.destination_tx_hash }).catch((err) => - baseLogger.error({ err, event_id, id }, "Real-time reconciliation failed"), - ) + reconcileByTxHash({ txHash: receipt.destination_tx_hash }).catch((err) => { + baseLogger.error({ err, event_id, id }, "Real-time reconciliation failed") + alertIbexReconciliationFailed({ + txHash: receipt.destination_tx_hash, + detail: err instanceof Error ? err.message : String(err), + }) + }) } const auditResult = await writeBridgeDepositRequest({ @@ -94,13 +100,23 @@ export const depositHandler = async (req: Request, res: Response) => { { error: auditResult, event_id, id }, "Failed to persist Bridge deposit ERPNext audit row", ) + alertBridge({ + dedupKey: generateDedupKey.erpnextDepositAudit(id), + source: "erpnext-audit", + severity: "critical", + title: "Bridge deposit ERPNext audit write failed", + detail: auditResult.message, + context: { event_id, transfer_id: id }, + }) return res.status(500).json({ error: "Failed to persist ERPNext audit row" }) } // Idempotency: mark processed only after local and ERPNext writes succeed, so // provider retries can recover audit gaps after transient ERPNext failures. const auditLockKey = `bridge-deposit:${event_id}` - const auditLockResult = await LockService().lockIdempotencyKey(auditLockKey as IdempotencyKey) + const auditLockResult = await LockService().lockIdempotencyKey( + auditLockKey as IdempotencyKey, + ) if (auditLockResult instanceof Error) { baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") return res.status(200).json({ status: "already_processed" }) @@ -109,6 +125,14 @@ export const depositHandler = async (req: Request, res: Response) => { return res.status(200).json({ status: "success" }) } catch (error) { baseLogger.error({ error, id, event_id }, "Error processing Bridge deposit webhook") + alertBridge({ + dedupKey: generateDedupKey.bridgeWebhookDeposit(event_id), + source: "bridge-webhook", + severity: "critical", + title: "Bridge deposit webhook processing error", + detail: error instanceof Error ? error.message : String(error), + context: { event_id, transfer_id: id }, + }) return res.status(500).json({ error: "Internal server error" }) } } diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts index 88f00fad8..fd6410e87 100644 --- a/src/services/bridge/webhook-server/routes/transfer.ts +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -13,6 +13,7 @@ import { writeBridgeCashoutCompleted, writeBridgeCashoutFailed, } from "@services/frappe/BridgeTransferRequestWriter" +import { alertBridge, generateDedupKey } from "@services/alerts" const TERMINAL_FAILURE_STATES = new Set([ "undeliverable", @@ -121,6 +122,14 @@ export const transferHandler = async (req: Request, res: Response) => { { transfer_id, error: auditResult }, "Failed to persist Bridge transfer ERPNext audit row", ) + alertBridge({ + dedupKey: generateDedupKey.erpnextTransferCompletedAudit(transfer_id), + source: "erpnext-audit", + severity: "critical", + title: "Bridge transfer ERPNext audit write failed", + detail: auditResult.message, + context: { transfer_id, event }, + }) return res.status(500).json({ error: "Failed to persist ERPNext audit row" }) } @@ -196,6 +205,14 @@ export const transferHandler = async (req: Request, res: Response) => { { transfer_id, error: auditResult }, "Failed to persist Bridge transfer failure ERPNext audit row", ) + alertBridge({ + dedupKey: generateDedupKey.erpnextTransferFailedAudit(transfer_id), + source: "erpnext-audit", + severity: "critical", + title: "Bridge transfer-failure ERPNext audit write failed", + detail: auditResult.message, + context: { transfer_id, event }, + }) return res.status(500).json({ error: "Failed to persist ERPNext audit row" }) } @@ -216,6 +233,14 @@ export const transferHandler = async (req: Request, res: Response) => { return res.status(200).json({ status: "success" }) } catch (error) { baseLogger.error({ error, transfer_id }, "Error processing Bridge transfer webhook") + alertBridge({ + dedupKey: generateDedupKey.bridgeWebhookTransfer(transfer_id, event), + source: "bridge-webhook", + severity: "critical", + title: "Bridge transfer webhook processing error", + detail: error instanceof Error ? error.message : String(error), + context: { transfer_id, event }, + }) return res.status(500).json({ error: "Internal server error" }) } } diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index 7a45d129c..939ffd0dc 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -7,6 +7,10 @@ import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" import { LockService } from "@services/lock" import { reconcileByTxHash } from "@services/bridge/reconciliation" +import { + alertIbexCryptoReceiveFailure, + alertIbexReconciliationFailed, +} from "@services/alerts/ibex-bridge-movement" import { writeIbexCryptoReceiveRequest } from "@services/frappe/BridgeTransferRequestWriter" import { authenticate, logRequest } from "../middleware" @@ -48,6 +52,12 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { const account = await AccountsRepository().findByBridgeEthereumAddress(address) if (account instanceof Error) { baseLogger.error({ address, tx_hash }, "Account not found for Ethereum address") + alertIbexCryptoReceiveFailure({ + txHash: String(tx_hash), + code: "account_not_found", + title: "IBEX crypto receive: account not found for Bridge Ethereum address", + context: { address }, + }) return { status: "error", code: "account_not_found" } as CryptoReceiveResult } @@ -64,12 +74,23 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { { error: ibexLog, tx_hash }, "Failed to persist IBEX crypto receive log", ) + alertIbexCryptoReceiveFailure({ + txHash: String(tx_hash), + code: "persist_failed", + title: "IBEX crypto receive log persistence failed", + detail: ibexLog.message, + context: { address }, + }) return { status: "error", code: "internal_error" } as CryptoReceiveResult } - reconcileByTxHash({ txHash: String(tx_hash) }).catch((err) => - baseLogger.error({ err, tx_hash }, "Real-time reconciliation failed"), - ) + reconcileByTxHash({ txHash: String(tx_hash) }).catch((err) => { + baseLogger.error({ err, tx_hash }, "Real-time reconciliation failed") + alertIbexReconciliationFailed({ + txHash: String(tx_hash), + detail: err instanceof Error ? err.message : String(err), + }) + }) const wallets = await listWalletsByAccountId(account.id) if (wallets instanceof Error) { @@ -77,18 +98,38 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { { accountId: account.id, error: wallets }, "Failed to list wallets", ) + alertIbexCryptoReceiveFailure({ + txHash: String(tx_hash), + code: "wallet_list_failed", + title: "IBEX crypto receive: wallet list failed", + detail: wallets.message, + context: { accountId: account.id, address }, + }) return { status: "error", code: "wallet_list_failed" } as CryptoReceiveResult } const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) if (!usdtWallet) { baseLogger.error({ accountId: account.id }, "USDT wallet not found") + alertIbexCryptoReceiveFailure({ + txHash: String(tx_hash), + code: "usdt_wallet_not_found", + title: "IBEX crypto receive: USDT wallet not found", + context: { accountId: account.id, address }, + }) return { status: "error", code: "usdt_wallet_not_found" } as CryptoReceiveResult } const usdtAmount = USDTAmount.fromNumber(amount) if (usdtAmount instanceof Error) { baseLogger.error({ amount, error: usdtAmount }, "Invalid USDT amount") + alertIbexCryptoReceiveFailure({ + txHash: String(tx_hash), + code: "invalid_amount", + title: "IBEX crypto receive: invalid USDT amount", + detail: usdtAmount.message, + context: { accountId: account.id, address, amount }, + }) return { status: "error", code: "invalid_amount" } as CryptoReceiveResult } @@ -123,6 +164,17 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { }, "Failed to persist IBEX crypto receive ERPNext audit row", ) + alertIbexCryptoReceiveFailure({ + txHash: String(tx_hash), + code: "erpnext_audit_failed", + title: "IBEX crypto receive ERPNext audit write failed", + detail: auditResult.message, + context: { + accountId: account.id, + walletId: usdtWallet.id, + address, + }, + }) return { status: "error", code: "erpnext_audit_failed" } as CryptoReceiveResult } @@ -135,6 +187,12 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return { status: "success" } as CryptoReceiveResult } catch (error) { baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") + alertIbexCryptoReceiveFailure({ + txHash: String(tx_hash), + code: "internal_error", + title: "IBEX crypto receive webhook processing error", + detail: error instanceof Error ? error.message : String(error), + }) return { status: "error", code: "internal_error" } as CryptoReceiveResult } }, diff --git a/test/flash/unit/services/alerts/dedup-key.spec.ts b/test/flash/unit/services/alerts/dedup-key.spec.ts new file mode 100644 index 000000000..f06f18c1d --- /dev/null +++ b/test/flash/unit/services/alerts/dedup-key.spec.ts @@ -0,0 +1,54 @@ +import { + generateDedupKey, + informDedupTtlMs, + normalizeDedupKey, +} from "@services/alerts/dedup-key" + +describe("generateDedupKey", () => { + it("uses coarse keys for Bridge API outage classes", () => { + expect(generateDedupKey.bridgeApi5xx()).toBe("bridge-api:5xx") + expect(generateDedupKey.bridgeApiTimeout()).toBe("bridge-api:timeout") + expect(generateDedupKey.bridgeApiNetwork()).toBe("bridge-api:network") + }) + + it("scopes ERPNext and webhook keys per resource", () => { + expect(generateDedupKey.erpnextDepositAudit("tr_1")).toBe( + "erpnext-audit:deposit:tr_1", + ) + expect(generateDedupKey.erpnextTransferCompletedAudit("tr_2")).toBe( + "erpnext-audit:transfer-complete:tr_2", + ) + expect(generateDedupKey.bridgeWebhookDeposit("wh_1")).toBe( + "bridge-webhook:deposit:wh_1", + ) + expect(generateDedupKey.bridgeWebhookTransfer("tr_3", "transfer.completed")).toBe( + "bridge-webhook:transfer:tr_3:transfer.completed", + ) + }) + + it("scopes IBEX Bridge movement keys per tx hash or transfer", () => { + expect(generateDedupKey.ibexCryptoReceive("0XABC")).toBe("ibex:crypto-receive:0xabc") + expect(generateDedupKey.ibexReconcileBridgeWithoutIbex("0xabc")).toBe( + "ibex:reconcile:bridge-without-ibex:0xabc", + ) + expect(generateDedupKey.ibexReconcileIbexWithoutBridge("0xabc")).toBe( + "ibex:reconcile:ibex-without-bridge:0xabc", + ) + expect(generateDedupKey.ibexReconcileBridgeWithoutIbexTransfer("tr_1")).toBe( + "ibex:reconcile:bridge-without-ibex:transfer:tr_1", + ) + }) +}) + +describe("informDedupTtlMs", () => { + it("uses a shorter TTL for Bridge API outage keys", () => { + expect(informDedupTtlMs("bridge-api:5xx")).toBe(30 * 60 * 1000) + expect(informDedupTtlMs("erpnext-audit:deposit:tr_1")).toBe(60 * 60 * 1000) + }) +}) + +describe("normalizeDedupKey", () => { + it("truncates keys to PagerDuty's maximum dedup_key length", () => { + expect(normalizeDedupKey("a".repeat(300))).toHaveLength(255) + }) +}) diff --git a/test/flash/unit/services/alerts/ibex-bridge-movement.spec.ts b/test/flash/unit/services/alerts/ibex-bridge-movement.spec.ts new file mode 100644 index 000000000..aaa1ff20d --- /dev/null +++ b/test/flash/unit/services/alerts/ibex-bridge-movement.spec.ts @@ -0,0 +1,59 @@ +jest.mock("@services/alerts", () => ({ + alertBridge: jest.fn(), +})) + +import { alertBridge } from "@services/alerts" +import { + alertIbexCryptoReceiveFailure, + alertIbexReconciliationOrphan, +} from "@services/alerts/ibex-bridge-movement" + +describe("ibex bridge movement alerts", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("routes crypto receive failures as IBEX warnings", () => { + alertIbexCryptoReceiveFailure({ + txHash: "0xabc", + code: "erpnext_audit_failed", + title: "IBEX crypto receive ERPNext audit write failed", + detail: "timeout", + context: { accountId: "acc_1" }, + }) + + expect(alertBridge).toHaveBeenCalledWith({ + dedupKey: "ibex:crypto-receive:0xabc", + source: "ibex", + severity: "warning", + title: "IBEX crypto receive ERPNext audit write failed", + detail: "timeout", + context: { + tx_hash: "0xabc", + code: "erpnext_audit_failed", + accountId: "acc_1", + }, + }) + }) + + it("routes reconciliation orphans as IBEX warnings", () => { + alertIbexReconciliationOrphan({ + orphanType: "ibex_without_bridge", + txHash: "0xdef", + reason: "No Bridge deposit payment_processed found for IBEX tx hash within window", + }) + + expect(alertBridge).toHaveBeenCalledWith({ + dedupKey: "ibex:reconcile:ibex-without-bridge:0xdef", + source: "ibex", + severity: "warning", + title: "IBEX crypto receive without matching Bridge deposit", + detail: "No Bridge deposit payment_processed found for IBEX tx hash within window", + context: { + orphan_type: "ibex_without_bridge", + tx_hash: "0xdef", + transfer_id: undefined, + }, + }) + }) +}) diff --git a/test/flash/unit/services/alerts/index.spec.ts b/test/flash/unit/services/alerts/index.spec.ts new file mode 100644 index 000000000..675185119 --- /dev/null +++ b/test/flash/unit/services/alerts/index.spec.ts @@ -0,0 +1,76 @@ +jest.mock("@services/alerts/slack", () => ({ + sendSlack: jest.fn().mockResolvedValue(undefined), +})) + +jest.mock("@services/alerts/discord", () => ({ + sendDiscord: jest.fn().mockResolvedValue(undefined), +})) + +jest.mock("@services/alerts/pagerduty", () => ({ + sendPagerDuty: jest.fn().mockResolvedValue(undefined), +})) + +import { alertBridge } from "@services/alerts" +import { resetInformDedup } from "@services/alerts/inform-dedup" +import { sendDiscord } from "@services/alerts/discord" +import { sendPagerDuty } from "@services/alerts/pagerduty" +import { sendSlack } from "@services/alerts/slack" + +describe("alertBridge", () => { + beforeEach(() => { + jest.clearAllMocks() + resetInformDedup() + }) + + it("fans out critical alerts to inform channels and PagerDuty", async () => { + alertBridge({ + dedupKey: "bridge-api:5xx", + source: "bridge-api", + severity: "critical", + title: "Bridge API 502 on GET /transfers", + }) + + await Promise.resolve() + + expect(sendSlack).toHaveBeenCalledTimes(1) + expect(sendDiscord).toHaveBeenCalledTimes(1) + expect(sendPagerDuty).toHaveBeenCalledTimes(1) + expect(sendPagerDuty).toHaveBeenCalledWith( + expect.objectContaining({ dedupKey: "bridge-api:5xx" }), + ) + }) + + it("does not page PagerDuty for warning alerts", async () => { + alertBridge({ + dedupKey: "ibex:warning:tx_1", + source: "ibex", + severity: "warning", + title: "IBEX movement failed", + }) + + await Promise.resolve() + + expect(sendSlack).toHaveBeenCalledTimes(1) + expect(sendDiscord).toHaveBeenCalledTimes(1) + expect(sendPagerDuty).not.toHaveBeenCalled() + }) + + it("suppresses duplicate Slack and Discord alerts for the same dedup key", async () => { + const alert = { + dedupKey: "bridge-api:5xx", + source: "bridge-api" as const, + severity: "critical" as const, + title: "Bridge API 502 on GET /transfers", + } + + alertBridge(alert) + alertBridge(alert) + alertBridge(alert) + + await Promise.resolve() + + expect(sendSlack).toHaveBeenCalledTimes(1) + expect(sendDiscord).toHaveBeenCalledTimes(1) + expect(sendPagerDuty).toHaveBeenCalledTimes(3) + }) +}) diff --git a/test/flash/unit/services/alerts/inform-dedup.spec.ts b/test/flash/unit/services/alerts/inform-dedup.spec.ts new file mode 100644 index 000000000..c1c0fcc16 --- /dev/null +++ b/test/flash/unit/services/alerts/inform-dedup.spec.ts @@ -0,0 +1,34 @@ +import { claimInformSlot, resetInformDedup } from "@services/alerts/inform-dedup" + +describe("claimInformSlot", () => { + beforeEach(() => { + resetInformDedup() + }) + + it("allows the first inform for a dedup key", () => { + expect(claimInformSlot("erpnext-audit:deposit:tr_1", 1_000)).toBe(true) + }) + + it("suppresses duplicate informs within the TTL", () => { + const key = "bridge-api:5xx" + const start = 10_000 + + expect(claimInformSlot(key, start)).toBe(true) + expect(claimInformSlot(key, start + 1_000)).toBe(false) + expect(claimInformSlot(key, start + 29 * 60 * 1000)).toBe(false) + }) + + it("allows a new inform after the TTL expires", () => { + const key = "bridge-api:5xx" + const start = 10_000 + + expect(claimInformSlot(key, start)).toBe(true) + expect(claimInformSlot(key, start + 30 * 60 * 1000)).toBe(true) + }) + + it("tracks different dedup keys independently", () => { + expect(claimInformSlot("erpnext-audit:deposit:tr_a", 1_000)).toBe(true) + expect(claimInformSlot("erpnext-audit:deposit:tr_b", 1_000)).toBe(true) + expect(claimInformSlot("erpnext-audit:deposit:tr_a", 2_000)).toBe(false) + }) +}) diff --git a/test/flash/unit/services/alerts/pagerduty.spec.ts b/test/flash/unit/services/alerts/pagerduty.spec.ts new file mode 100644 index 000000000..d95b45199 --- /dev/null +++ b/test/flash/unit/services/alerts/pagerduty.spec.ts @@ -0,0 +1,44 @@ +jest.mock("@config", () => ({ + ALERT_PAGERDUTY_ROUTING_KEY: "test-routing-key", +})) + +jest.mock("@services/tracing", () => ({ + recordExceptionInCurrentSpan: jest.fn(), +})) + +jest.mock("axios", () => ({ + post: jest.fn().mockResolvedValue({ status: 202 }), +})) + +import axios from "axios" +import { sendPagerDuty } from "@services/alerts/pagerduty" + +describe("sendPagerDuty", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("includes dedup_key in the Events API v2 payload", async () => { + await sendPagerDuty({ + dedupKey: "bridge-api:5xx", + source: "bridge-api", + severity: "critical", + title: "Bridge API 502 on GET /transfers", + context: { method: "GET", path: "/transfers" }, + }) + + expect(axios.post).toHaveBeenCalledWith( + "https://events.pagerduty.com/v2/enqueue", + expect.objectContaining({ + routing_key: "test-routing-key", + event_action: "trigger", + dedup_key: "bridge-api:5xx", + payload: expect.objectContaining({ + summary: "[bridge:bridge-api] Bridge API 502 on GET /transfers", + severity: "critical", + }), + }), + expect.any(Object), + ) + }) +}) diff --git a/test/flash/unit/services/bridge/reconciliation.spec.ts b/test/flash/unit/services/bridge/reconciliation.spec.ts index 2c4c3a6d6..9e8a91092 100644 --- a/test/flash/unit/services/bridge/reconciliation.spec.ts +++ b/test/flash/unit/services/bridge/reconciliation.spec.ts @@ -10,12 +10,12 @@ jest.mock("@services/logger", () => ({ })) jest.mock("@services/mongoose/schema", () => ({ - BridgeDepositLog: { findOne: jest.fn(), find: jest.fn() }, - IbexCryptoReceiveLog: { findOne: jest.fn() }, + BridgeDeposits: { findOne: jest.fn(), find: jest.fn() }, + IbexCryptoReceive: { findOne: jest.fn() }, })) jest.mock("@services/mongoose/ibex-crypto-receive-log", () => ({ - findIbexCryptoReceiveLogsSince: jest.fn(), + findIbexCryptoReceivesSince: jest.fn(), })) jest.mock("@services/mongoose/bridge-reconciliation-orphan", () => ({ @@ -33,13 +33,19 @@ jest.mock("@domain/pubsub", () => ({ }, })) -import { BridgeDepositLog, IbexCryptoReceiveLog } from "@services/mongoose/schema" -import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" +jest.mock("@services/alerts/ibex-bridge-movement", () => ({ + alertIbexReconciliationOrphan: jest.fn(), + alertIbexReconciliationFailed: jest.fn(), +})) + +import { BridgeDeposits, IbexCryptoReceive } from "@services/mongoose/schema" +import { findIbexCryptoReceivesSince } from "@services/mongoose/ibex-crypto-receive-log" import { upsertBridgeReconciliationOrphan, resolveOrphansByTxHash, } from "@services/mongoose/bridge-reconciliation-orphan" import { PubSubService } from "@services/pubsub" +import { alertIbexReconciliationOrphan } from "@services/alerts/ibex-bridge-movement" import { reconcileByTxHash, reconcileBridgeAndIbexDeposits, @@ -91,8 +97,12 @@ beforeEach(() => { describe("reconcileByTxHash", () => { describe("both sides found → matched", () => { beforeEach(() => { - ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) - ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + ;(BridgeDeposits.findOne as jest.Mock).mockReturnValue( + makeLeanQuery(BRIDGE_DEPOSIT), + ) + ;(IbexCryptoReceive.findOne as jest.Mock).mockReturnValue( + makeLeanQuery(IBEX_RECEIVE), + ) }) it("returns status matched", async () => { @@ -134,15 +144,17 @@ describe("reconcileByTxHash", () => { expect(result).not.toBeInstanceOf(Error) if (result instanceof Error) return expect(result.txHash).toBe(NORM_HASH) - const [bridgeCall] = (BridgeDepositLog.findOne as jest.Mock).mock.calls + const [bridgeCall] = (BridgeDeposits.findOne as jest.Mock).mock.calls expect(bridgeCall[0].destinationTxHash.$regex.flags).toContain("i") }) }) describe("only Bridge found → bridge_without_ibex", () => { beforeEach(() => { - ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) - ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + ;(BridgeDeposits.findOne as jest.Mock).mockReturnValue( + makeLeanQuery(BRIDGE_DEPOSIT), + ) + ;(IbexCryptoReceive.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) }) it("returns status unmatched with correct orphanType", async () => { @@ -182,12 +194,25 @@ describe("reconcileByTxHash", () => { }), ) }) + + it("alerts ops when Bridge has no matching IBEX receive", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(alertIbexReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanType: "bridge_without_ibex", + txHash: NORM_HASH, + transferId: BRIDGE_DEPOSIT.transferId, + }), + ) + }) }) describe("only IBEX found → ibex_without_bridge", () => { beforeEach(() => { - ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) - ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + ;(BridgeDeposits.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + ;(IbexCryptoReceive.findOne as jest.Mock).mockReturnValue( + makeLeanQuery(IBEX_RECEIVE), + ) }) it("returns status unmatched with correct orphanType", async () => { @@ -208,13 +233,25 @@ describe("reconcileByTxHash", () => { }), ) }) + + it("alerts ops when IBEX has no matching Bridge deposit", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(alertIbexReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanType: "ibex_without_bridge", + txHash: NORM_HASH, + }), + ) + }) }) describe("self-healing: second call with both sides resolves orphan", () => { it("resolves orphan when called again after missing side arrives", async () => { // First call: only Bridge - ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) - ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + ;(BridgeDeposits.findOne as jest.Mock).mockReturnValue( + makeLeanQuery(BRIDGE_DEPOSIT), + ) + ;(IbexCryptoReceive.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) await reconcileByTxHash({ txHash: TX_HASH }) expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledTimes(1) @@ -223,8 +260,12 @@ describe("reconcileByTxHash", () => { ;(resolveOrphansByTxHash as jest.Mock).mockResolvedValue({ resolvedCount: 1 }) // Second call: both sides present (IBEX webhook arrived) - ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) - ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + ;(BridgeDeposits.findOne as jest.Mock).mockReturnValue( + makeLeanQuery(BRIDGE_DEPOSIT), + ) + ;(IbexCryptoReceive.findOne as jest.Mock).mockReturnValue( + makeLeanQuery(IBEX_RECEIVE), + ) const result = await reconcileByTxHash({ txHash: TX_HASH }) expect(result).not.toBeInstanceOf(Error) @@ -236,11 +277,11 @@ describe("reconcileByTxHash", () => { }) describe("Bridge query uses payment_processed state filter", () => { - it("passes state: payment_processed to BridgeDepositLog.findOne", async () => { - ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) - ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + it("passes state: payment_processed to BridgeDeposits.findOne", async () => { + ;(BridgeDeposits.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + ;(IbexCryptoReceive.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) await reconcileByTxHash({ txHash: TX_HASH }) - expect(BridgeDepositLog.findOne).toHaveBeenCalledWith( + expect(BridgeDeposits.findOne).toHaveBeenCalledWith( expect.objectContaining({ state: "payment_processed" }), ) }) @@ -256,8 +297,10 @@ describe("reconcileBridgeAndIbexDeposits", () => { describe("all deposits matched", () => { it("returns zero orphans when every Bridge deposit has a matching IBEX receive", async () => { - ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([BRIDGE_DEPOSIT])) - ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE]) + ;(BridgeDeposits.find as jest.Mock).mockReturnValue( + makeBridgeFind([BRIDGE_DEPOSIT]), + ) + ;(findIbexCryptoReceivesSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE]) const result = await reconcileBridgeAndIbexDeposits() expect(result).not.toBeInstanceOf(Error) @@ -272,8 +315,10 @@ describe("reconcileBridgeAndIbexDeposits", () => { describe("Bridge deposit with no matching IBEX receive", () => { it("flags as bridge_without_ibex orphan", async () => { - ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([BRIDGE_DEPOSIT])) - ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + ;(BridgeDeposits.find as jest.Mock).mockReturnValue( + makeBridgeFind([BRIDGE_DEPOSIT]), + ) + ;(findIbexCryptoReceivesSince as jest.Mock).mockResolvedValue([]) const result = await reconcileBridgeAndIbexDeposits() expect(result).not.toBeInstanceOf(Error) @@ -293,8 +338,8 @@ describe("reconcileBridgeAndIbexDeposits", () => { describe("Bridge deposit with no destinationTxHash", () => { it("flags as bridge-no-tx:{transferId} orphan", async () => { const depositNoHash = { ...BRIDGE_DEPOSIT, destinationTxHash: undefined } - ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([depositNoHash])) - ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + ;(BridgeDeposits.find as jest.Mock).mockReturnValue(makeBridgeFind([depositNoHash])) + ;(findIbexCryptoReceivesSince as jest.Mock).mockResolvedValue([]) const result = await reconcileBridgeAndIbexDeposits() expect(result).not.toBeInstanceOf(Error) @@ -311,8 +356,8 @@ describe("reconcileBridgeAndIbexDeposits", () => { describe("IBEX receive with no matching Bridge deposit", () => { it("flags as ibex_without_bridge orphan", async () => { - ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) - ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE]) + ;(BridgeDeposits.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceivesSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE]) const result = await reconcileBridgeAndIbexDeposits() expect(result).not.toBeInstanceOf(Error) @@ -329,12 +374,12 @@ describe("reconcileBridgeAndIbexDeposits", () => { }) describe("batch uses payment_processed state filter", () => { - it("passes state: payment_processed to BridgeDepositLog.find", async () => { - ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) - ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + it("passes state: payment_processed to BridgeDeposits.find", async () => { + ;(BridgeDeposits.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceivesSince as jest.Mock).mockResolvedValue([]) await reconcileBridgeAndIbexDeposits() - expect(BridgeDepositLog.find).toHaveBeenCalledWith( + expect(BridgeDeposits.find).toHaveBeenCalledWith( expect.objectContaining({ state: "payment_processed" }), ) }) @@ -342,13 +387,17 @@ describe("reconcileBridgeAndIbexDeposits", () => { describe("mixed scenario", () => { it("counts matched and unmatched independently", async () => { - const deposit2 = { ...BRIDGE_DEPOSIT, transferId: "tr_002", destinationTxHash: "0xother" } + const deposit2 = { + ...BRIDGE_DEPOSIT, + transferId: "tr_002", + destinationTxHash: "0xother", + } const ibex2 = { ...IBEX_RECEIVE, txHash: "0xorphan_ibex" } - ;(BridgeDepositLog.find as jest.Mock).mockReturnValue( + ;(BridgeDeposits.find as jest.Mock).mockReturnValue( makeBridgeFind([BRIDGE_DEPOSIT, deposit2]), ) - ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE, ibex2]) + ;(findIbexCryptoReceivesSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE, ibex2]) const result = await reconcileBridgeAndIbexDeposits() expect(result).not.toBeInstanceOf(Error) @@ -365,9 +414,9 @@ describe("reconcileBridgeAndIbexDeposits", () => { }) describe("error handling", () => { - it("returns an Error when findIbexCryptoReceiveLogsSince fails", async () => { - ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) - ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue( + it("returns an Error when findIbexCryptoReceivesSince fails", async () => { + ;(BridgeDeposits.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceivesSince as jest.Mock).mockResolvedValue( new Error("mongo connection lost"), ) diff --git a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts index 44788f076..54094f81d 100644 --- a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts @@ -115,7 +115,11 @@ describe("depositHandler — invalid payload", () => { describe("depositHandler — idempotency", () => { it("returns already_processed after idempotent local and audit writes on duplicate delivery", async () => { - mockLockService(false) + const lockFn = jest + .fn() + .mockResolvedValueOnce({}) + .mockResolvedValueOnce(new Error("already locked")) + ;(LockService as jest.Mock).mockReturnValue({ lockIdempotencyKey: lockFn }) ;(DepositLog.createBridgeDeposit as jest.Mock).mockResolvedValue({ id: "log-dup" }) const res = makeRes() @@ -124,6 +128,7 @@ describe("depositHandler — idempotency", () => { expect(res.json as jest.Mock).toHaveBeenCalledWith({ status: "already_processed" }) expect(DepositLog.createBridgeDeposit).toHaveBeenCalledTimes(1) expect(writeBridgeDepositRequest).toHaveBeenCalledTimes(1) + expect(lockFn).toHaveBeenCalledTimes(2) }) it("locks on the event id after writing the audit row", async () => { diff --git a/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts b/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts index e4e42d929..af7830a4c 100644 --- a/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts +++ b/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts @@ -27,10 +27,19 @@ jest.mock("@services/bridge/reconciliation", () => ({ reconcileByTxHash: jest.fn().mockResolvedValue({ status: "matched" }), })) +jest.mock("@app/bridge/send-deposit-notification", () => ({ + sendBridgeDepositNotificationBestEffort: jest.fn().mockResolvedValue(undefined), +})) + jest.mock("@services/frappe/BridgeTransferRequestWriter", () => ({ writeIbexCryptoReceiveRequest: jest.fn(), })) +jest.mock("@services/alerts/ibex-bridge-movement", () => ({ + alertIbexCryptoReceiveFailure: jest.fn(), + alertIbexReconciliationFailed: jest.fn(), +})) + import { cryptoReceiveHandler } from "@services/ibex/webhook-server/routes/crypto-receive" import { AccountsRepository } from "@services/mongoose/accounts" import { createIbexCryptoReceive } from "@services/mongoose/ibex-crypto-receive-log" @@ -38,6 +47,7 @@ import { listWalletsByAccountId } from "@app/wallets" import { LockService } from "@services/lock" import { WalletCurrency } from "@domain/shared" import { writeIbexCryptoReceiveRequest } from "@services/frappe/BridgeTransferRequestWriter" +import { alertIbexCryptoReceiveFailure } from "@services/alerts/ibex-bridge-movement" const ACCOUNT_ID = "account-001" as AccountId const WALLET_ID = "wallet-usdt-001" as WalletId @@ -155,6 +165,13 @@ describe("cryptoReceiveHandler", () => { expect(res.status).toHaveBeenCalledWith(500) expect(res.json).toHaveBeenCalledWith({ error: "erpnext_audit_failed" }) + expect(alertIbexCryptoReceiveFailure).toHaveBeenCalledWith( + expect.objectContaining({ + txHash: TX_HASH, + code: "erpnext_audit_failed", + title: "IBEX crypto receive ERPNext audit write failed", + }), + ) }) it("rejects legacy Tron USDT receive webhooks for the ETH-USDT Cash Wallet path", async () => { From a23c9fdeed9a90f5b80b733077aff6b801861739 Mon Sep 17 00:00:00 2001 From: Island Bitcoin <34528298+islandbitcoin@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:46:28 -0700 Subject: [PATCH 33/43] feat(cashout): route Cashout V1 wallets via cutover guard (ENG-357) (#395) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(cashout): route Cashout V1 wallets via cutover guard (ENG-357) Cashout V1 always debited the legacy USD wallet and credited the bank-owner USD wallet. Post-cutover the user's funds live in an ETH-USDT cash wallet, so the offer must debit USDT and credit the bank-owner's USDT wallet. The Flash bank-owner account holds both a USD and a USDT wallet, so the route simply selects the matching pair on both sides — no cross-currency swap. - Add resolveCashoutWalletSelection: reads the cutover config + per-account migration and runs evaluateCashWalletCutoverGuard to pick the route. Source and destination wallets are resolved server-side from the guard, NOT from the client-supplied walletId (trusted only for wallet-level auth). The guard blocks the cashout while a migration is in-flight or has failed. - CashoutManager.createOffer builds a USD or USDT invoice per route; the USD/JMD payout math is unchanged (1 USDT = 1 USD). - executeCashout authorizes by account, since an old client may still present the zeroed legacy USD walletId while the offer settles in USDT, instead of an exact wallet-id match. - CashoutValidator and CashoutDetails.payment.amount are currency-aware; the USD path stays byte-identical. ErpNext.draftCashout records the USDT amount. Adds unit coverage for the routing decision tree. Co-Authored-By: Claude Opus 4.8 (1M context) * test(cashout): add ENG-357 Bruno smoke coverage * fix(cashout): harden offer redis deserialization --- .../01 discover legacy cashout wallet.bru | 68 +++++++ .../02 discover USDT cashout inputs.bru | 80 ++++++++ .../03 request cashout offer.bru | 78 ++++++++ .../04 initiate cashout offer.bru | 61 ++++++ .../token/ENG-357 cashout cutover/folder.bru | 33 ++++ .../cash-wallet-cutover/cashout-routing.ts | 103 +++++++++++ src/app/offers/CashoutManager.ts | 59 ++++-- src/app/offers/Validator.ts | 43 ++++- src/app/offers/storage/OffersSerde.ts | 41 +++++ src/app/offers/storage/Redis.ts | 54 ++---- src/app/offers/types.ts | 5 +- src/services/frappe/ErpNext.ts | 9 +- .../cashout-routing.spec.ts | 174 ++++++++++++++++++ .../app/offers/storage/redis-serde.spec.ts | 73 ++++++++ 14 files changed, 818 insertions(+), 63 deletions(-) create mode 100644 dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/01 discover legacy cashout wallet.bru create mode 100644 dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/02 discover USDT cashout inputs.bru create mode 100644 dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/03 request cashout offer.bru create mode 100644 dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/04 initiate cashout offer.bru create mode 100644 dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/folder.bru create mode 100644 src/app/cash-wallet-cutover/cashout-routing.ts create mode 100644 src/app/offers/storage/OffersSerde.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/cashout-routing.spec.ts create mode 100644 test/flash/unit/app/offers/storage/redis-serde.spec.ts diff --git a/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/01 discover legacy cashout wallet.bru b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/01 discover legacy cashout wallet.bru new file mode 100644 index 000000000..64a600c2d --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/01 discover legacy cashout wallet.bru @@ -0,0 +1,68 @@ +meta { + name: 01 discover legacy cashout wallet + type: graphql + seq: 1 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + query Eng357CashoutInputs { + me { + id + defaultAccount { + id + defaultWalletId + wallets { + id + walletCurrency + balance + } + } + bankAccounts { + id + accountName + bankName + currency + } + } + } +} + +script:post-response { + test("discovers the legacy USD cashout wallet without the cash-wallet capability header", function () { + const body = res.getBody(); + const errors = body.errors ?? []; + expect(errors, JSON.stringify(errors)).to.be.empty; + + const me = body.data?.me; + expect(me, "me").to.exist; + + const wallets = me.defaultAccount?.wallets ?? []; + const cashoutAccountId = me.defaultAccount?.id; + const legacyUsdWallet = wallets.find((wallet) => wallet.walletCurrency === "USD"); + const legacyUsdWalletId = legacyUsdWallet?.id ?? me.defaultAccount?.defaultWalletId; + expect(cashoutAccountId, "cashout account id").to.exist; + expect(legacyUsdWallet?.id, "legacy USD wallet id").to.exist; + expect(legacyUsdWalletId, "legacy USD wallet id or defaultWalletId").to.exist; + + bru.setEnvVar("cashoutAccountId", cashoutAccountId); + bru.setEnvVar("legacyUsdWalletId", legacyUsdWalletId); + + console.log("cashoutAccountId:", cashoutAccountId); + console.log("legacyUsdWalletId:", legacyUsdWalletId); + }); +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/02 discover USDT cashout inputs.bru b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/02 discover USDT cashout inputs.bru new file mode 100644 index 000000000..8a414d3d6 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/02 discover USDT cashout inputs.bru @@ -0,0 +1,80 @@ +meta { + name: 02 discover USDT cashout inputs + type: graphql + seq: 2 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json + X-Flash-Client-Capabilities: cash-wallet-usdt-v1 +} + +body:graphql { + query Eng357CashoutInputs { + me { + id + defaultAccount { + id + defaultWalletId + wallets { + id + walletCurrency + balance + } + } + bankAccounts { + id + accountName + bankName + currency + } + } + } +} + +script:post-response { + test("discovers the post-cutover USDT wallet and cashout bank account with the cash-wallet capability header", function () { + const body = res.getBody(); + const errors = body.errors ?? []; + expect(errors, JSON.stringify(errors)).to.be.empty; + + const me = body.data?.me; + expect(me, "me").to.exist; + + const wallets = me.defaultAccount?.wallets ?? []; + const cashoutAccountId = me.defaultAccount?.id; + const usdtWallet = wallets.find((wallet) => wallet.walletCurrency === "USDT"); + expect(cashoutAccountId, "cashout account id").to.exist; + expect(bru.getEnvVar("legacyUsdWalletId"), "legacy USD wallet id from step 01").to.exist; + expect(usdtWallet?.id, "USDT wallet id").to.exist; + expect(usdtWallet.id, "USDT wallet should be the post-cutover default wallet").to.eql( + me.defaultAccount?.defaultWalletId, + ); + + const bankAccounts = me.bankAccounts ?? []; + const bankAccount = + bankAccounts.find((account) => account.currency === "JMD") ?? bankAccounts[0]; + const cashoutBankAccountId = + bankAccount?.id ?? bru.getEnvVar("cashoutBankAccountId") ?? "12345 - First Global"; + expect(cashoutBankAccountId, "cashout bank account id").to.exist; + + bru.setEnvVar("cashoutAccountId", cashoutAccountId); + bru.setEnvVar("usdtWalletId", usdtWallet.id); + bru.setEnvVar("cashoutBankAccountId", cashoutBankAccountId); + + console.log("cashoutAccountId:", cashoutAccountId); + console.log("usdtWalletId:", usdtWallet.id); + console.log("cashoutBankAccountId:", cashoutBankAccountId); + }); +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/03 request cashout offer.bru b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/03 request cashout offer.bru new file mode 100644 index 000000000..8b1bb4cd9 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/03 request cashout offer.bru @@ -0,0 +1,78 @@ +meta { + name: 03 request cashout offer + type: graphql + seq: 3 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json + ~X-Flash-Client-Capabilities: cash-wallet-usdt-v1 +} + +body:graphql { + mutation Eng357RequestCashout($input: RequestCashoutInput!) { + requestCashout(input: $input) { + offer { + offerId + walletId + send + receiveUsd + receiveJmd + flashFee + expiresAt + } + errors { + ... on GraphQLApplicationError { + code + message + path + } + } + } + } +} + +body:graphql:vars { + { + "input": { + "walletId": "{{legacyUsdWalletId}}", + "amount": 100, + "bankAccountId": "{{cashoutBankAccountId}}" + } + } +} + +script:post-response { + test("creates a post-cutover USDT-settled cashout offer from the legacy USD wallet input", function () { + const body = res.getBody(); + const graphqlErrors = body.errors ?? []; + expect(graphqlErrors, JSON.stringify(graphqlErrors)).to.be.empty; + + const payload = body.data?.requestCashout; + const appErrors = payload?.errors ?? []; + expect(appErrors, JSON.stringify(appErrors)).to.be.empty; + + const offer = payload?.offer; + expect(offer?.offerId, "offerId").to.exist; + expect(offer.walletId, "offer walletId should be the resolved USDT wallet").to.eql( + bru.getEnvVar("usdtWalletId"), + ); + expect(offer.walletId, "offer walletId should not stay on legacy USD").to.not.eql( + bru.getEnvVar("legacyUsdWalletId"), + ); + + bru.setEnvVar("cashoutOfferId", offer.offerId); + console.log("cashoutOfferId:", offer.offerId); + }); +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/04 initiate cashout offer.bru b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/04 initiate cashout offer.bru new file mode 100644 index 000000000..d251d4227 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/04 initiate cashout offer.bru @@ -0,0 +1,61 @@ +meta { + name: 04 initiate cashout offer + type: graphql + seq: 4 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json + X-Flash-Client-Capabilities: cash-wallet-usdt-v1 +} + +body:graphql { + mutation Eng357InitiateCashout($input: InitiateCashoutInput!) { + initiateCashout(input: $input) { + id + errors { + ... on GraphQLApplicationError { + code + message + path + } + } + } + } +} + +body:graphql:vars { + { + "input": { + "offerId": "{{cashoutOfferId}}", + "walletId": "{{legacyUsdWalletId}}" + } + } +} + +script:post-response { + test("initiates the Redis-loaded USDT cashout offer using the legacy USD wallet for auth", function () { + const body = res.getBody(); + const graphqlErrors = body.errors ?? []; + expect(graphqlErrors, JSON.stringify(graphqlErrors)).to.be.empty; + + const payload = body.data?.initiateCashout; + const appErrors = payload?.errors ?? []; + expect(appErrors, JSON.stringify(appErrors)).to.be.empty; + expect(payload?.id, "cashout id").to.exist; + + bru.setEnvVar("cashoutId", payload.id); + console.log("cashoutId:", payload.id); + }); +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/folder.bru b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/folder.bru new file mode 100644 index 000000000..5e8d71621 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/ENG-357 cashout cutover/folder.bru @@ -0,0 +1,33 @@ +meta { + name: ENG-357 cashout cutover + seq: 4 +} + +auth { + mode: inherit +} + +docs { + # ENG-357 Cashout V1 cutover smoke + + Focused local Bruno flow for PR #395. + + Run order: + 1. `login flow/userLogin` + 2. `token/ENG-357 cashout cutover/01 discover legacy cashout wallet` + 3. `token/ENG-357 cashout cutover/02 discover USDT cashout inputs` + 4. `token/ENG-357 cashout cutover/03 request cashout offer` + 5. `token/ENG-357 cashout cutover/04 initiate cashout offer` + + This intentionally requests cashout with the legacy USD wallet from the old-client + view, then verifies the offer settles against the post-cutover USDT cash wallet. + Step 01 omits `X-Flash-Client-Capabilities` to discover the legacy USD wallet. + Step 02 sends `X-Flash-Client-Capabilities: cash-wallet-usdt-v1` to discover the + USDT wallet and bank account. PR #395 should return an offer whose `walletId` is + the account's USDT wallet, then `initiateCashout` should successfully reload that + offer from Redis while authenticating with the legacy wallet id. + + If step 03 returns the USD wallet instead of USDT, the local account/config is not + exercising the post-cutover route. If step 04 returns a server error after step 03 + succeeds, that strongly points at the USDT offer Redis serialization round-trip. +} diff --git a/src/app/cash-wallet-cutover/cashout-routing.ts b/src/app/cash-wallet-cutover/cashout-routing.ts new file mode 100644 index 000000000..84e939166 --- /dev/null +++ b/src/app/cash-wallet-cutover/cashout-routing.ts @@ -0,0 +1,103 @@ +import { WalletCurrency } from "@domain/shared" +import { WalletsRepository, CashWalletCutoverRepository } from "@services/mongoose" + +import { + CashWalletMissingUsdtWalletError, + CashWalletCutoverPreflightError, +} from "./errors" +import { CashWalletCutoverRoute, evaluateCashWalletCutoverGuard } from "./guard" + +type CashoutRoutingMigrationsRepository = { + getConfig: () => Promise + findMigrationByAccountId: (args: { + accountId: AccountId + cutoverVersion: number + runId: string + }) => Promise +} + +type CashoutRoutingWalletsRepository = { + findById: (walletId: WalletId) => Promise + listByAccountId: (accountId: AccountId) => Promise +} + +export type CashoutWalletSelection = { + route: CashWalletCutoverRoute + userWalletId: WalletId + flashWalletId: WalletId +} + +// Resolves the source (user) and destination (Flash bank-owner) wallets for a +// Cashout V1 offer from the cutover guard — NOT from the client-supplied walletId. +// Pre-cutover this returns the legacy USD wallets unchanged; post-cutover it returns +// the account's USDT wallet and the bank-owner's USDT wallet so the debit settles in +// ETH-USDT. The guard blocks the cashout (returns an error) while a migration is +// in-flight or has failed. +export const resolveCashoutWalletSelection = async ({ + accountId, + requestedUserWalletId, + bankOwnerUsdWalletId, + migrationsRepo = CashWalletCutoverRepository(), + walletsRepo = WalletsRepository(), +}: { + accountId: AccountId + requestedUserWalletId: WalletId + bankOwnerUsdWalletId: WalletId + migrationsRepo?: CashoutRoutingMigrationsRepository + walletsRepo?: CashoutRoutingWalletsRepository +}): Promise => { + const cutover = await migrationsRepo.getConfig() + if (cutover instanceof Error) return cutover + + let migration: CashWalletMigration | null | undefined + if (cutover.state === "in_progress") { + if (!cutover.runId) return new CashWalletCutoverPreflightError() + + const foundMigration = await migrationsRepo.findMigrationByAccountId({ + accountId, + cutoverVersion: cutover.cutoverVersion, + runId: cutover.runId, + }) + if (foundMigration instanceof Error) return foundMigration + migration = foundMigration + } + + const decision = evaluateCashWalletCutoverGuard({ cutover, migration }) + if (decision instanceof Error) return decision + + if (decision.route === "legacy_usd") { + return { + route: "legacy_usd", + userWalletId: requestedUserWalletId, + flashWalletId: bankOwnerUsdWalletId, + } + } + + const userWallets = await walletsRepo.listByAccountId(accountId) + if (userWallets instanceof Error) return userWallets + const userUsdtWallet = userWallets.find((w) => w.currency === WalletCurrency.Usdt) + if (!userUsdtWallet) { + return new CashWalletMissingUsdtWalletError( + `No USDT wallet found for account ${accountId}`, + ) + } + + const bankOwnerWallet = await walletsRepo.findById(bankOwnerUsdWalletId) + if (bankOwnerWallet instanceof Error) return bankOwnerWallet + const bankOwnerWallets = await walletsRepo.listByAccountId(bankOwnerWallet.accountId) + if (bankOwnerWallets instanceof Error) return bankOwnerWallets + const bankOwnerUsdtWallet = bankOwnerWallets.find( + (w) => w.currency === WalletCurrency.Usdt, + ) + if (!bankOwnerUsdtWallet) { + return new CashWalletMissingUsdtWalletError( + "No USDT wallet found for the Flash bank-owner account", + ) + } + + return { + route: "usdt", + userWalletId: userUsdtWallet.id, + flashWalletId: bankOwnerUsdtWallet.id, + } +} diff --git a/src/app/offers/CashoutManager.ts b/src/app/offers/CashoutManager.ts index 60770ef74..946d16efc 100644 --- a/src/app/offers/CashoutManager.ts +++ b/src/app/offers/CashoutManager.ts @@ -1,7 +1,8 @@ import Storage from "./storage/Redis" import ValidOffer, { InitiatedCashout } from "./ValidOffer" -import { USDAmount, ValidationError } from "@domain/shared" +import { USDAmount, USDTAmount, ValidationError } from "@domain/shared" import { CacheServiceError } from "@domain/cache" +import { resolveCashoutWalletSelection } from "@app/cash-wallet-cutover/cashout-routing" import { getBankOwnerIbexAccount } from "@services/ledger/caching" import Ibex from "@services/ibex/client" import { UnexpectedIbexResponse } from "@services/ibex/errors" @@ -25,12 +26,36 @@ const CashoutManager = { userPayment: USDAmount, bankAccountId: string, ): Promise => { - const flashWallet = await getBankOwnerIbexAccount() + const bankOwnerUsdWalletId = await getBankOwnerIbexAccount() - const invoiceResp = await Ibex.addInvoice({ - accountId: flashWallet, + const wallet = await WalletsRepository().findById(walletId) + if (wallet instanceof RepositoryError) return new ValidationError(wallet) + const account = await AccountsRepository().findById(wallet.accountId) + if (account instanceof RepositoryError) return new ValidationError(account) + if (!account.erpParty) return new Error("Could not find erpParty for account") + + // Source (user) and destination (Flash bank-owner) wallets are resolved from the + // cutover guard — not from the client-supplied walletId, which is trusted only for + // wallet-level auth. Post-cutover this routes the debit to the account's USDT wallet + // and the bank-owner's USDT wallet; pre-cutover it stays on the legacy USD wallets. + const selection = await resolveCashoutWalletSelection({ + accountId: account.id, + requestedUserWalletId: walletId, + bankOwnerUsdWalletId, + }) + if (selection instanceof Error) return selection + + // 1 USDT = 1 USD; the JMD/USD payout math below stays USD-denominated regardless. + const paymentAmount = + selection.route === "usdt" + ? USDTAmount.usdCents(userPayment.asCents()) + : userPayment + if (paymentAmount instanceof Error) return paymentAmount + + const invoiceResp = await Ibex.addInvoice({ + accountId: selection.flashWalletId, memo: "User withdraw to bank", - amount: userPayment, + amount: paymentAmount, expiration: config.duration, }) if (invoiceResp instanceof Error) return invoiceResp @@ -43,12 +68,6 @@ const CashoutManager = { const exchangeRate = config.jmd.sell // todo: get from price server const jmdPayout = usdPayout.convertAtRate(exchangeRate) - const wallet = await WalletsRepository().findById(walletId) - if (wallet instanceof RepositoryError) return new ValidationError(wallet) - const account = await AccountsRepository().findById(wallet.accountId) - if (account instanceof RepositoryError) return new ValidationError(account) - if (!account.erpParty) return new Error("Could not find erpParty for account") - const bankAccounts = await ErpNext.getBankAccountsByCustomer(account.erpParty!) if (bankAccounts instanceof BankAccountQueryError) return bankAccounts const bankAccount = bankAccounts.find(b => b.name === bankAccountId) @@ -61,10 +80,10 @@ const CashoutManager = { const validated = await ValidOffer.from({ payment: { - userAcct: walletId, - flashAcct: flashWallet, + userAcct: selection.userWalletId, + flashAcct: selection.flashWalletId, invoice, - amount: userPayment, + amount: paymentAmount, }, payout, }) @@ -78,8 +97,16 @@ const CashoutManager = { executeCashout: async (id: OfferId, walletId: WalletId): Promise => { const offer = await Storage.get(id) if (offer instanceof Error) return offer - - if (walletId !== offer.details.payment.userAcct) return new ValidationError("Offer is not good for provided wallet.") + + // walletId authenticates the caller at the wallet level; the offer's settlement wallet + // may differ from it (e.g. a USDT cash wallet post-cutover while an older client still + // presents the legacy USD walletId). Authorize when both belong to the same account. + const providedWallet = await WalletsRepository().findById(walletId) + if (providedWallet instanceof RepositoryError) return new ValidationError(providedWallet) + const settlementWallet = await WalletsRepository().findById(offer.details.payment.userAcct) + if (settlementWallet instanceof RepositoryError) return new ValidationError(settlementWallet) + if (providedWallet.accountId !== settlementWallet.accountId) + return new ValidationError("Offer is not good for provided wallet.") const validOffer = await ValidOffer.from(offer.details) if (validOffer instanceof Error) return validOffer diff --git a/src/app/offers/Validator.ts b/src/app/offers/Validator.ts index 3b113edc9..a41974ece 100644 --- a/src/app/offers/Validator.ts +++ b/src/app/offers/Validator.ts @@ -27,6 +27,13 @@ const isBeforeExpiry = async (o: ValidationInputs): Promise => { + if (o.payment.amount instanceof USDTAmount) { + const min = USDTAmount.usdCents(config.minimum.amount) + if (min instanceof Error) return new ValidationError(min) + if (o.payment.amount.isLesserThan(min)) + return new ValidationError(`Minimum cashout is $${min.asNumber(2)}`) + return true + } const min = USDAmount.cents(config.minimum.amount) if (min instanceof Error) return new ValidationError(min) if (o.payment.amount.isLesserThan(min)) @@ -37,6 +44,13 @@ const cashoutMin = async (o: ValidationInputs): Promise const cashoutMax: ValidationFn = async ( o: ValidationInputs, ): Promise => { + if (o.payment.amount instanceof USDTAmount) { + const max = USDTAmount.usdCents(config.maximum.amount) + if (max instanceof Error) return new ValidationError(max) + if (o.payment.amount.isGreaterThan(max)) + return new ValidationError(`Maximum cashout is $${max.asNumber(2)}`) + return true + } const max = USDAmount.cents(config.maximum.amount) if (max instanceof Error) return new ValidationError(max) if (o.payment.amount.isGreaterThan(max)) @@ -44,9 +58,17 @@ const cashoutMax: ValidationFn = async ( else return true } -const isUsd = async (o: ValidationInputs) => { - if (o.wallet.currency !== "USD") - return new ValidationError("Cash out only supports withdrawals from USD wallets") +// Cash out supports a USD source wallet (pre-cutover) or a USDT source wallet +// (post-cutover). The amount currency must match the resolved source wallet. +const isSupportedCashoutWallet = async (o: ValidationInputs) => { + const { currency } = o.wallet + if (currency !== "USD" && currency !== "USDT") + return new ValidationError( + "Cash out only supports withdrawals from USD or USDT wallets", + ) + const amountIsUsdt = o.payment.amount instanceof USDTAmount + if (amountIsUsdt !== (currency === "USDT")) + return new ValidationError("Cashout amount currency does not match the source wallet") return true } @@ -58,8 +80,17 @@ const hasSufficientBalance = async ( currency: o.wallet.currency, }) if (balance instanceof Error) return new ValidationError(balance) - if (balance instanceof USDTAmount) - return new ValidationError("Cash out only supports withdrawals from USD wallets") + if (balance instanceof USDTAmount) { + if (!(o.payment.amount instanceof USDTAmount)) + return new ValidationError( + "Cashout amount currency does not match the source wallet", + ) + if (o.payment.amount.isGreaterThan(balance)) + return new ValidationError("Transfer amount is greater than wallet balance.") + return true + } + if (o.payment.amount instanceof USDTAmount) + return new ValidationError("Cashout amount currency does not match the source wallet") else if (o.payment.amount.isGreaterThan(balance)) return new ValidationError("Transfer amount is greater than wallet balance.") else return true @@ -90,7 +121,7 @@ const verifyBankAccount = async ( } export const CashoutValidator = validator([ - isUsd, + isSupportedCashoutWallet, cashoutMin, cashoutMax, isActiveAccount, diff --git a/src/app/offers/storage/OffersSerde.ts b/src/app/offers/storage/OffersSerde.ts new file mode 100644 index 000000000..cf3ff040e --- /dev/null +++ b/src/app/offers/storage/OffersSerde.ts @@ -0,0 +1,41 @@ +import { + MoneyAmount, + USDTAmount, + WalletCurrency, + toMoneyAmountFromJSON, +} from "@domain/shared" + +import { CashoutDetails } from "../types" + +/** + * Custom SerDe for CashoutDetails + */ +export const OffersSerde = { + serialize: (data: CashoutDetails): string => { + return JSON.stringify(data, (_, value) => { + if (value instanceof MoneyAmount || value instanceof USDTAmount) + return value.toJson() + else if (typeof value === "bigint") return value.toString() + else return value + }) + }, + + deserialize: (json: string): CashoutDetails => { + return JSON.parse(json, (key: string, value: unknown) => { + if (key === "expiresAt" && typeof value === "string") return new Date(value) + + if ( + ["amount", "servicefee", "exchangerate"].includes(key.toLowerCase()) && + Array.isArray(value) + ) { + const amount = + value[1] === WalletCurrency.Usdt + ? USDTAmount.smallestUnits(value[0] as string) + : toMoneyAmountFromJSON(value as [string, string]) + if (amount instanceof Error) throw amount + return amount + } + return value + }) + }, +} diff --git a/src/app/offers/storage/Redis.ts b/src/app/offers/storage/Redis.ts index 13dedb5f6..cd401da3d 100644 --- a/src/app/offers/storage/Redis.ts +++ b/src/app/offers/storage/Redis.ts @@ -1,59 +1,39 @@ -import { parseRepositoryError } from "../../../services/mongoose/utils" -import PersistedOffer from "./PersistedOffer" -import ValidOffer from "../ValidOffer" -import { RedisCacheService } from "@services/cache" -import { CacheServiceError, CacheUndefinedError, OfferNotFound } from "@domain/cache" -import { baseLogger } from "@services/logger" import { randomUUID } from "crypto" -import { JMDAmount, MoneyAmount, USDAmount, toMoneyAmountFromJSON } from "@domain/shared" -import { CashoutDetails } from "../types" -/** - * Custom SerDe for CashoutDetails - */ -const OffersSerde = { - serialize: (data: CashoutDetails): string => { - return JSON.stringify(data, (_, value) => { - if (value instanceof MoneyAmount) return value.toJson() - else if (typeof value === "bigint") return value.toString(); - else return value - }); - }, +import { CacheServiceError, CacheUndefinedError, OfferNotFound } from "@domain/cache" +import { RedisCacheService } from "@services/cache" - deserialize: (json: string) => { - return JSON.parse( - json, - (key: string, value: any) => { - if (['amount', 'servicefee', 'exchangerate'].includes(key.toLowerCase()) && Array.isArray(value)) { - return toMoneyAmountFromJSON(value as [string, string]) - } - return value; - }) - } -} +import ValidOffer from "../ValidOffer" + +import { parseRepositoryError } from "../../../services/mongoose/utils" + +import { OffersSerde } from "./OffersSerde" +import PersistedOffer from "./PersistedOffer" const Redis = { add: async (o: ValidOffer): Promise => { const id = randomUUID() as OfferId // could use hash of offer details with getOrSet - const result= await RedisCacheService().set({ + const result = await RedisCacheService().set({ key: `offers:${id}`, value: OffersSerde.serialize(o.details), - ttlSecs: 3600 as Seconds - }); + ttlSecs: 3600 as Seconds, + }) if (result instanceof CacheServiceError) return result return new PersistedOffer(id, o.details) }, - + get: async (id: OfferId): Promise => { try { - const result: string | CacheServiceError = await RedisCacheService().get({ key: `offers:${id}`}) + const result: string | CacheServiceError = await RedisCacheService().get({ + key: `offers:${id}`, + }) if (result instanceof CacheUndefinedError) return new OfferNotFound() if (result instanceof CacheServiceError) return result else return new PersistedOffer(id, OffersSerde.deserialize(result)) } catch (err) { return parseRepositoryError(err) } - } + }, } -export default Redis \ No newline at end of file +export default Redis diff --git a/src/app/offers/types.ts b/src/app/offers/types.ts index 107df62ed..e39af9926 100644 --- a/src/app/offers/types.ts +++ b/src/app/offers/types.ts @@ -1,4 +1,4 @@ -import { USDAmount, JMDAmount } from "@domain/shared" +import { USDAmount, USDTAmount, JMDAmount } from "@domain/shared" // Full details in a cashout transaction export type CashoutDetails = { @@ -6,7 +6,8 @@ export type CashoutDetails = { readonly userAcct: WalletId, readonly flashAcct: WalletId, readonly invoice: LnInvoice, - readonly amount: USDAmount, + // USD pre-cutover; USDT once the source account has migrated to the cash wallet. + readonly amount: USDAmount | USDTAmount, }, readonly payout: { readonly bankAccountId: string, diff --git a/src/services/frappe/ErpNext.ts b/src/services/frappe/ErpNext.ts index e0d42cb05..33ef9e2a2 100644 --- a/src/services/frappe/ErpNext.ts +++ b/src/services/frappe/ErpNext.ts @@ -1,6 +1,6 @@ import ValidOffer from "@app/offers/ValidOffer" import { FrappeConfig } from "@config" -import { JMDAmount, USDAmount, Validated } from "@domain/shared" +import { JMDAmount, USDAmount, USDTAmount, Validated } from "@domain/shared" import { baseLogger } from "@services/logger" import { recordExceptionInCurrentSpan } from "@services/tracing" import axios, { isAxiosError } from "axios" @@ -68,7 +68,12 @@ export class ErpNext { wallet_id: payment.userAcct, flash_wallet: payment.flashAcct, user_receives: Number(payout.amount.asDollars()), - user_pays: Number(payment.amount.asDollars()), + // 1 USDT = 1 USD; USDTAmount exposes major units via asNumber (no asDollars). + user_pays: Number( + payment.amount instanceof USDTAmount + ? payment.amount.asNumber(2) + : payment.amount.asDollars(), + ), currency: payout.amount.currencyCode, exchange_rate: payout.exchangeRate ? Number(payout.exchangeRate.asDollars()) : undefined, flash_fee: Number(payout.serviceFee.asDollars()), diff --git a/test/flash/unit/app/cash-wallet-cutover/cashout-routing.spec.ts b/test/flash/unit/app/cash-wallet-cutover/cashout-routing.spec.ts new file mode 100644 index 000000000..0defc38ad --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/cashout-routing.spec.ts @@ -0,0 +1,174 @@ +import { resolveCashoutWalletSelection } from "@app/cash-wallet-cutover/cashout-routing" +import { + CashWalletMigrationFailedError, + CashWalletMissingUsdtWalletError, +} from "@app/cash-wallet-cutover/errors" +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const accountId = "user-account-id" as AccountId +const legacyUsdWalletId = "11111111-1111-4111-8111-111111111111" as WalletId +const userUsdtWalletId = "22222222-2222-4222-8222-222222222222" as WalletId + +const bankOwnerAccountId = "bank-owner-account-id" as AccountId +const bankOwnerUsdWalletId = "33333333-3333-4333-8333-333333333333" as WalletId +const bankOwnerUsdtWalletId = "44444444-4444-4444-8444-444444444444" as WalletId + +const asWallet = (id: WalletId, acctId: AccountId, currency: WalletCurrency): Wallet => + ({ + id, + accountId: acctId, + currency, + type: WalletType.Checking, + }) as Wallet + +const userUsdtWallet = asWallet(userUsdtWalletId, accountId, WalletCurrency.Usdt) +const bankOwnerUsdWallet = asWallet( + bankOwnerUsdWalletId, + bankOwnerAccountId, + WalletCurrency.Usd, +) +const bankOwnerUsdtWallet = asWallet( + bankOwnerUsdtWalletId, + bankOwnerAccountId, + WalletCurrency.Usdt, +) + +const config = (overrides: Record) => + ({ + cutoverVersion: 1, + updatedAt: new Date(), + ...overrides, + }) as unknown as CashWalletCutoverConfig + +// Resolves the bank-owner USDT wallet by account, and the user USDT wallet by account. +const usdtWalletsRepo = () => ({ + findById: jest.fn().mockResolvedValue(bankOwnerUsdWallet), + listByAccountId: jest + .fn() + .mockImplementation(async (id: AccountId) => + id === bankOwnerAccountId + ? [bankOwnerUsdWallet, bankOwnerUsdtWallet] + : [userUsdtWallet], + ), +}) + +describe("resolveCashoutWalletSelection", () => { + it("routes to the legacy USD wallets pre-cutover, trusting the client walletId", async () => { + const migrationsRepo = { + getConfig: jest.fn().mockResolvedValue(config({ state: "pre" })), + findMigrationByAccountId: jest.fn(), + } + const walletsRepo = { + findById: jest.fn(), + listByAccountId: jest.fn(), + } + + const result = await resolveCashoutWalletSelection({ + accountId, + requestedUserWalletId: legacyUsdWalletId, + bankOwnerUsdWalletId, + migrationsRepo, + walletsRepo, + }) + + expect(result).toEqual({ + route: "legacy_usd", + userWalletId: legacyUsdWalletId, + flashWalletId: bankOwnerUsdWalletId, + }) + expect(migrationsRepo.findMigrationByAccountId).not.toHaveBeenCalled() + expect(walletsRepo.listByAccountId).not.toHaveBeenCalled() + }) + + it("routes to USDT wallets once the cutover is complete", async () => { + const migrationsRepo = { + getConfig: jest.fn().mockResolvedValue(config({ state: "complete" })), + findMigrationByAccountId: jest.fn(), + } + const walletsRepo = usdtWalletsRepo() + + const result = await resolveCashoutWalletSelection({ + accountId, + requestedUserWalletId: legacyUsdWalletId, + bankOwnerUsdWalletId, + migrationsRepo, + walletsRepo, + }) + + expect(result).toEqual({ + route: "usdt", + userWalletId: userUsdtWalletId, + flashWalletId: bankOwnerUsdtWalletId, + }) + }) + + it("stays on legacy USD mid-cutover for an account that has not started migrating", async () => { + const migrationsRepo = { + getConfig: jest + .fn() + .mockResolvedValue(config({ state: "in_progress", runId: "run-1" })), + findMigrationByAccountId: jest.fn().mockResolvedValue(null), + } + const walletsRepo = { findById: jest.fn(), listByAccountId: jest.fn() } + + const result = await resolveCashoutWalletSelection({ + accountId, + requestedUserWalletId: legacyUsdWalletId, + bankOwnerUsdWalletId, + migrationsRepo, + walletsRepo, + }) + + expect(result).toEqual({ + route: "legacy_usd", + userWalletId: legacyUsdWalletId, + flashWalletId: bankOwnerUsdWalletId, + }) + }) + + it("blocks the cashout when the account migration has failed", async () => { + const migrationsRepo = { + getConfig: jest + .fn() + .mockResolvedValue(config({ state: "in_progress", runId: "run-1" })), + findMigrationByAccountId: jest + .fn() + .mockResolvedValue({ status: "failed" } as unknown as CashWalletMigration), + } + const walletsRepo = { findById: jest.fn(), listByAccountId: jest.fn() } + + const result = await resolveCashoutWalletSelection({ + accountId, + requestedUserWalletId: legacyUsdWalletId, + bankOwnerUsdWalletId, + migrationsRepo, + walletsRepo, + }) + + expect(result).toBeInstanceOf(CashWalletMigrationFailedError) + }) + + it("errors when the USDT route is selected but the account has no USDT wallet", async () => { + const migrationsRepo = { + getConfig: jest.fn().mockResolvedValue(config({ state: "complete" })), + findMigrationByAccountId: jest.fn(), + } + const walletsRepo = { + findById: jest.fn().mockResolvedValue(bankOwnerUsdWallet), + listByAccountId: jest + .fn() + .mockResolvedValue([asWallet(legacyUsdWalletId, accountId, WalletCurrency.Usd)]), + } + + const result = await resolveCashoutWalletSelection({ + accountId, + requestedUserWalletId: legacyUsdWalletId, + bankOwnerUsdWalletId, + migrationsRepo, + walletsRepo, + }) + + expect(result).toBeInstanceOf(CashWalletMissingUsdtWalletError) + }) +}) diff --git a/test/flash/unit/app/offers/storage/redis-serde.spec.ts b/test/flash/unit/app/offers/storage/redis-serde.spec.ts new file mode 100644 index 000000000..3394755f4 --- /dev/null +++ b/test/flash/unit/app/offers/storage/redis-serde.spec.ts @@ -0,0 +1,73 @@ +import { OffersSerde } from "@app/offers/storage/OffersSerde" +import { CashoutDetails } from "@app/offers/types" +import { JMDAmount, USDAmount, USDTAmount } from "@domain/shared" + +const usd = (cents: string) => { + const amount = USDAmount.cents(cents) + if (amount instanceof Error) throw amount + return amount +} + +const jmd = (cents: string) => { + const amount = JMDAmount.cents(cents) + if (amount instanceof Error) throw amount + return amount +} + +const usdt = (cents: string) => { + const amount = USDTAmount.usdCents(cents) + if (amount instanceof Error) throw amount + return amount +} + +describe("OffersSerde", () => { + it("round-trips USDT cashout payment amounts as domain amount instances", () => { + const details: CashoutDetails = { + payment: { + userAcct: "22222222-2222-4222-8222-222222222222" as WalletId, + flashAcct: "44444444-4444-4444-8444-444444444444" as WalletId, + invoice: { + paymentRequest: "lnbc1test" as Bolt11, + expiresAt: new Date(Date.now() + 60_000), + } as LnInvoice, + amount: usdt("100"), + }, + payout: { + bankAccountId: "12345 - First Global", + amount: jmd("15500"), + serviceFee: usd("0"), + exchangeRate: jmd("15500"), + }, + } + + const parsed = OffersSerde.deserialize(OffersSerde.serialize(details)) + + expect(parsed.payment.amount).toBeInstanceOf(USDTAmount) + expect(parsed.payment.amount.isLesserThan(usdt("101"))).toBe(true) + expect(parsed.payment.invoice.expiresAt).toBeInstanceOf(Date) + expect(parsed.payout.amount).toBeInstanceOf(JMDAmount) + expect(parsed.payout.serviceFee).toBeInstanceOf(USDAmount) + }) + + it("throws instead of hydrating invalid amount tuples into Error objects", () => { + const invalidSerializedOffer = JSON.stringify({ + payment: { + userAcct: "22222222-2222-4222-8222-222222222222", + flashAcct: "44444444-4444-4444-8444-444444444444", + invoice: { + paymentRequest: "lnbc1test", + expiresAt: new Date(Date.now() + 60_000).toISOString(), + }, + amount: ["not-a-number", "USDT"], + }, + payout: { + bankAccountId: "12345 - First Global", + amount: ["15500", "JMD"], + serviceFee: ["0", "USD"], + exchangeRate: ["15500", "JMD"], + }, + }) + + expect(() => OffersSerde.deserialize(invalidSerializedOffer)).toThrow() + }) +}) From 8532284d2a60a7c6821548b9ddece91fc381a717 Mon Sep 17 00:00:00 2001 From: Patoo Date: Wed, 10 Jun 2026 21:01:29 -0400 Subject: [PATCH 34/43] chore(bridge): add sandbox e2e test suite (#388) * feat(bridge): add opt-in sandbox e2e test suite ENG-274. Covers KYC, virtual account, external account, deposit, withdrawal, post-cutover state, ETH-USDT LN parity, and ERPNext audit-row verification. Guarded by RUN_BRIDGE_SANDBOX_E2E=true. Includes preflight check for Level 1 service guard and documentation drift cleanup (Level 2->Level 1, Tron->ETH-USDT). * chore(bridge): automate sandbox webhook setup * chore(bridge): add ERPNext audit snapshot * test(bridge): gate hosted sandbox success paths * test(bridge): quiet sandbox e2e service warnings * chore(bridge): polish sandbox e2e PR cleanup * chore(bridge): refresh sandbox e2e backup * test(bridge): address sandbox e2e review feedback --- .gitignore | 5 + dev/erpnext/backup.sh | 23 +- .../20260608_113333-frontend-database.sql.gz | Bin 0 -> 857154 bytes dev/erpnext/backups/clean-snapshot.sql.gz | Bin 836707 -> 0 bytes dev/setup-bridge-webhooks.js | 359 +++++++++++++++++ dev/setup.sh | 52 ++- docs/bridge-integration/API.md | 4 +- docs/bridge-integration/ARCHITECTURE.md | 14 +- docs/bridge-integration/FLOWS.md | 8 +- docs/bridge-integration/WEBHOOKS.md | 2 +- package.json | 4 +- src/services/bridge/index.ts | 91 +++-- .../frappe/models/BridgeTransferRequest.ts | 19 +- test/flash/bridge-sandbox-e2e/README.md | 208 ++++++++++ .../bridge-sandbox-e2e/config-overrides.yaml | 2 + .../bridge-sandbox-e2e/cutover-state.spec.ts | 102 +++++ .../deposit-withdrawal.spec.ts | 194 +++++++++ .../external-account.spec.ts | 156 ++++++++ test/flash/bridge-sandbox-e2e/helpers.ts | 374 ++++++++++++++++++ .../bridge-sandbox-e2e/helpers/http-utils.ts | 68 ++++ test/flash/bridge-sandbox-e2e/jest.config.js | 27 ++ test/flash/bridge-sandbox-e2e/jest.setup.ts | 85 ++++ .../kyc-virtual-account.spec.ts | 103 +++++ .../bridge-sandbox-e2e/ln-parity.spec.ts | 59 +++ test/flash/bridge-sandbox-e2e/preflight.ts | 64 +++ .../unit/dev/setup-bridge-webhooks.spec.ts | 126 ++++++ .../models/BridgeTransferRequest.spec.ts | 19 + 27 files changed, 2110 insertions(+), 58 deletions(-) create mode 100644 dev/erpnext/backups/20260608_113333-frontend-database.sql.gz delete mode 100644 dev/erpnext/backups/clean-snapshot.sql.gz create mode 100644 dev/setup-bridge-webhooks.js create mode 100644 test/flash/bridge-sandbox-e2e/README.md create mode 100644 test/flash/bridge-sandbox-e2e/config-overrides.yaml create mode 100644 test/flash/bridge-sandbox-e2e/cutover-state.spec.ts create mode 100644 test/flash/bridge-sandbox-e2e/deposit-withdrawal.spec.ts create mode 100644 test/flash/bridge-sandbox-e2e/external-account.spec.ts create mode 100644 test/flash/bridge-sandbox-e2e/helpers.ts create mode 100644 test/flash/bridge-sandbox-e2e/helpers/http-utils.ts create mode 100644 test/flash/bridge-sandbox-e2e/jest.config.js create mode 100644 test/flash/bridge-sandbox-e2e/jest.setup.ts create mode 100644 test/flash/bridge-sandbox-e2e/kyc-virtual-account.spec.ts create mode 100644 test/flash/bridge-sandbox-e2e/ln-parity.spec.ts create mode 100644 test/flash/bridge-sandbox-e2e/preflight.ts create mode 100644 test/flash/unit/dev/setup-bridge-webhooks.spec.ts diff --git a/.gitignore b/.gitignore index cdcb8c41c..4407023fa 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,8 @@ junit.xml .yarnrc.yml docker-compose.local.yml + +# Planning scratch files +findings.md +progress.md +task_plan.md diff --git a/dev/erpnext/backup.sh b/dev/erpnext/backup.sh index 230d68296..7fce5091e 100755 --- a/dev/erpnext/backup.sh +++ b/dev/erpnext/backup.sh @@ -1,17 +1,26 @@ #!/bin/bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd) # Create backups directory on host if it doesn't exist -BACKUP_DIR="$(dirname "$0")/backups" +BACKUP_DIR="$SCRIPT_DIR/backups" mkdir -p "$BACKUP_DIR" -docker exec -it flash-frappe-frontend-1 mkdir -p /tmp/backups +cd "$REPO_ROOT" + +FRAPPE_FRONTEND_SERVICE="frappe-frontend" +BACKUP_DIR_IN_CONTAINER="/tmp/backups" + +docker compose exec -T "$FRAPPE_FRONTEND_SERVICE" mkdir -p "$BACKUP_DIR_IN_CONTAINER" # Run the backup inside the container and capture output -BACKUP_OUTPUT=$(docker exec flash-frappe-frontend-1 bench --site frontend backup --backup-path /tmp/backups) +BACKUP_OUTPUT=$(docker compose exec -T "$FRAPPE_FRONTEND_SERVICE" bench --site frontend backup --backup-path "$BACKUP_DIR_IN_CONTAINER") # Extract the database path from the output line containing "Database:" -BACKUP_FILE=$(echo "$BACKUP_OUTPUT" | grep "Database:" | awk '{print $2}') -echo $BACKUP_FILE -docker cp flash-frappe-frontend-1:$BACKUP_FILE "$BACKUP_DIR/" +BACKUP_FILE=$(echo "$BACKUP_OUTPUT" | grep "Database:" | awk '{print $2}') +echo "$BACKUP_FILE" +docker compose cp "$FRAPPE_FRONTEND_SERVICE:$BACKUP_FILE" "$BACKUP_DIR/" -echo "Backups saved to: $BACKUP_DIR" +echo "Backups saved to: $BACKUP_DIR" diff --git a/dev/erpnext/backups/20260608_113333-frontend-database.sql.gz b/dev/erpnext/backups/20260608_113333-frontend-database.sql.gz new file mode 100644 index 0000000000000000000000000000000000000000..0dc5979e0c35c0b2880c6fc89091c4e733f6a866 GIT binary patch literal 857154 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

>k1`ud0c5uVk=V=BA)1C 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

rMRD;ZIJy`PfwiY$?Ij^%d+j$`*RyWLa0f#3E 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 =*e`G-!Zr8-HQ@NZ$G%X@U zv*Zm}`DIcle|1L>MZDhh#bLgr!Mg<2CfFglLWf0_@zPIVaqq#5oC`tM>^ zdEd?|U)tG2_`kb++Eh=aKtIQ8soP<`ShT|5o>@1N-ptu_?5F-ZaOJWUZhtNOWPdDr z1>{Z}?uy$Vipm$bF9uvkl0FUtU$FlZYsE%?3VAj5nw)P2EYWUfXOUdc9p((}C@_b5 z%1AEJDd?W2F#W*NA)pKG2PN;ZBjI3ADs8y0Nxp!;AB~$_9M(!zoo(c%tux2quxeMPezml}@62I!A&k>teSQ0ZRY6P;Va!+9 zsmD)9f=V0$@0*W7j7q{d%4a^L{Iwq%kqq~fPk{INEW1)mRgK|h8hA4e9ny0fcohWS zm<@#8D1Y*snt8L4<_3zJX;ZYGINB=TP6T0QWi3#+^@LmOGrLx}bF6lhJyjmdp#l2= zRII1b7M698mihO*&lvj@YqvQ?%|gm|eO(RL=eZZ5=#=M#^fl^(%8+U;G6Y?iA!+vO?} zO1>AiOQuca%cZm3!FXasuGb#Ph*!g8bKI-;Y}_@KwKhz$1k-p0Sa?Y=f9bihHo0ac z!L(?Z*|5VHL}db+irMTzh(V;h?uL(mivz9ot}h6*?OQm>%xMA5`au(a1&Vu*3nNSr z;kU|XaVwBM*$^^Rgdg*)6x=RE5qQ=A38U~ktI{_$G!5pViD)SLm(NIAEQVFn;1%WB&+hN9gF-ybcfsmK|y9U>XSP z4`#;NC_r*#3Q$(ST)}BEW7Ywa$E8wPPFU}5#hua#cb}vbeH`eO7Fk;4ObckuWd+~( z$`rs`mvB4Q=WX-FQdW{Plf3HOU|a&d1EklFm}MubLT8n`&aBvnnW;nDY-_KysX2XF#S;1=h|FK14t=0s znU-mt8TotZX9avpz38GWCX=lIj_px+x6I6`GiB#TFVrbN+e( zO1-v>fO1W^gY5M_sONEb%5hWo1F1U$L6+$g3F@(i?y=`k#jQfd7RAJ!7D62Qk%NdNDh6;LM<%Q!rnE%1^E$6uwI9zpGcf zPQ8Nuos@t5OZmG08DCh_A$mN2T^uGvKGHEth+nzOiFufH{m^?tO1`8qx`TZ|Yi9qgVDx|FUFmM)#+H5+-T-qo1GGJ? zB8!WG`=i&S2kB1VP6xpN280q>A}Mhbx9a5>ka}Ot3wTqSM=ivq#wqTV3CHp%?l&)GE{rVU_N0B~wXu#PUaE z&Sh||SMbtp>OMoT7m6&$KNii;3t69zEW-?{+qPOTLcwFg9l(YObFQs zrKi(7j_*ihKbOS3z5Q5mMB7?N+V2>#B>-QDu?z(Z874 z{#2Hp<3JCfMocUK=%tU;h8oj;tns)l@~G&e7(1n6KntaY0KhA9If!%Rg=ER7G;29$ zU73;Zv4&*nu(G)(I#`Ojse4@59{@k{0@i<-&L6d|L_vq=mbDFQ5y(T>wtv;#86 zVwUZ-Qww~-Hdwn<7mSIs;TS177_e{U{?#(Q)1@G*cxMRVKFA|s=a}9d^0MvGXlU3e z*%rxxd&&xA8`s#a1XuwDa8`I+nXG*p#jY(oHJM^-SfyJpUg83KKJ!?Xy^q5-db-8` zLjuFq-CL%`pDqa{EXDM*gqhTQ5y?v>%ca{__)5xLBJttZk`;vvom46xetHLegi{GS z*bAwku^Z`!h%67Y?4IR%zTLiMI{r0JmIowTTCkG^kOz&O^fT^NeyCra@URVPvO4zL za_OahWMO}?Ck-R^-LqNL2x2z{B0Q5U(VUi6TZKawbsAbocWgs>7k4vRq3DPYbfp>+ zsl?4zg#fFeYQru%boB-Oj;!f6&YWr-AS}6&RSD>KvO-b3RTR|##vy!F4FPZzzMbg{ z5=Zku8Se2(w%-Hn`w~yCO`K!fLwr(aQ8&T}$EgU2Q_q8LV@chk_?bE!ztCf;>S63& z8|`15T)r1ENhJ1XCuO^*nA_`Jh(!n;vP3=G*amT1=If2cZm-R|(;buSTtx)oRyzV* z@#yhD_~LDIAia&!Y;pL;`Gx+RwbGG5V6drcRQ4>9HNK3c-k{S^krg@;R@ zu_^{c?%EMypG8VjZNWlC!(t15?s{u2j67FzPZnXHHAeclI?Cfp{Z3QE0gy#Vdn(jQ zx1;YNl~23P4=n4B=kiv5wGB^yYSReQ*^zu7_}d_K^IYrUI6J)>HZ!xp>s@=%$E(tE zTl|TgQs<9cdL}cn`~=d4j7jVT=}HwL#R#$YG*#%5+eyPu_Ke2T zYF&iD+X}FFYCnbsUO)tGm0$x~BDh)L)gr|77oBBFC9Tv(pUY$E_v*NQ2)dO3pFaG2 z^X1FU?SIpMZf_2kON)WFWyOwDvGmU!uk4MYt-EAKezkj7MX}2nb-Tp6Q(2ADWp}pnj+} zt~FS{4w4w_p#Xp@sLF~AOFo+1$iZsrX={7yezUr&w?4l0N$6zm%*DP=kvpc53$S2} zy{BX=u!-*#B>AhJ!c{UBysJ)1odH~z8OC)ns9oJk!h9_N)QELKrWrHqD~!MJ!zZA7 zvK7Dh7TLItR!nM9y4cUtl_LPaDn;0H!YCn2^26gu_eV8+EU!wh>(aD-TlFQ2J{KR? z5((Mgw0lKs-#y=#_~>$}-le2!mr5m4kWlj0{1?iqw(^Umtb~=FyFX+<+%HDw%%8K& z-g=EQT?BuPP2iwfY3UMNUa075=!3+vz=x{aMngto>n=}qa~ZiJV9xTBow}W6hFM|V z*pLXkx-O143Uh4v#V!y)gQ4LJD&^w6+s}aSM%M#Bj|9M1=`@~I#i*rK%IhsZlqYKC z(QmTpCdc<0WkfG?*lJ0JRS|k**z}~KGnS4-`;Y5+J$@a;Xnfu74B1hIF#9!@Ro^rE zzeO*>^?t~I0= zs2kJNg(&COx}wcGu~^ueHDv9{eRc7=1wMrSDt9vh1YTinLyxr5vAu-e(hX_M!dJoK z8<|@+=Fva0ihi&ry>7rG>_t7I@c;hje={k``RPwzzm!jH*`(Ke6Z&Ou>Mtwt=N zz0R{R5uzyxc6ylel@*2b4N6|pA@|Pj-K*EkzY}d0qCyeCM~rkaAfZj+uDrt6{IKT} z4$83mZ`*$Dv#LxVTDvpS%rOiI5rX^$vFIIr&XcOts|?_=r{cmg5KP+gq<|{1h%v!-mL>2iC`NI<8?2glQabzJp&>nbQFESW#DyoI}Y)~oM=N+ zNb~?v3gu01icp9-Y zh%O`WY7r!jt=)?!jE15gqV*PB$HXAAfe^()dvoX<)r*A{>?dHHZHmk+L{J?NwG5Y| z?3SHC^whrOL8irW97#92JTi#nGE7-n8WIqR!1-EobeF@T7tQ$SQI5s>(wHteOX4R@ zF=lB-%tQpsMKKPx+0|rZHQ`Q&c;`z(3Xgn$p!4oZ{n1>>wA2?t-2=VPa@_WMjS*(d z*x)r!-m5TdKZ``Dng%LQ4!1est4#$LHdy6Ih>cj^v*FQ8Fm(#sQVTJX?MhwB+^t}~ zUeVpTg|X2E09IAXvAU_pN*LH11bNV#L;tqAD1GvJ1L<2*hO6RJ1&Z$AXj?_VD7xki z)Fc_+0rL)mTTO+sacDA>iI9olQP8uAMZa4Kj;9>-+W(${S&2Y3LkjLj0AT5DE1Z&= z73~d9J*%Wklm5N3xTQLG{U0sec`un=o91_w9e-z&HO~EPX*Ks9UWv&x%;`xXp!%L+ z>tO!XP}?EtOB~wuqWrTuR~9~f?I`l=+$w>QSauolB z^7J#{dWIZZenBF`njcp~+l@(Q&wp*hPxb?=B!oQIHmLoayEpz@AF$Vjd_5$|3f)J1w#r)u++E{Dm z0UyD1I3}_)D-Z~6!_NGw%}!~Rja6cBU5}0NmlQWO!2)1hOQ~&HBl5? z02FZCkYz?tn0Wz_-iKN8*Fn#^rwq*NR|XI{CIYcNKf2s=il(Xm4Bk5~(#33Gb z5WWAwhJSelYcRK5FCOi*U$CUR#vYQrVn(Ld_b9qSC9?!eI9tEqBY=9H{^TRsi z5firL_zB>yTwB#6XMS*Nx}HjE%N*Br%dPDOb`+ZPL#y+;kqX&&%}WmKBgXc50=xJa zvfSC|6tdGDL`eL^-|b?NEN)PaHBGWwfXgwZJqiAHRnqIN9S6CtND!;csj+K---q@U z!y8%pTD1f)yp>mr+R$Cpj6>`t2eq3pc^|}n^cUGHh8GRmoh>_#YlwhS(f*F^uT3u0lcJp%&bF`wK-*}IiON~;6q`1`KXlx$F|7^L zJkb&WMN%Y?vl6g#zwn>WV8k7G!!zu+EYOS$8uLr@X@vCezKPC4ud0fXud1n;z_^k1 z8-Q4m9cv|3Z-EIkpd3ILsv1D(D1^U8dB*2okBX}8rF9JYUGuWq^QP*;IB(j^;y{(% zBj}n?hw+Bj4nV+iC_t5D`A(r28n;MM3`0GHu8DwQ9)<3IMJwqoPV6J9j2@UuS_yDx z7^zrVqAdix?R|@J;5R{T6K_VbX5mVHxvnax?mnTg;EdS`6ACj{$TskaAi|9PB}(Sh zm75m^NCW|W=$|Op@1Lj^@)dXO3xe>V;JLl~n#-P5D>OHjV+n$>_Cg8@RCkLBs)Iwb z!)jb(QEkVn=zRE5w1aK7i4d64rf6btbR%OISnLI^r zeV5@4kR=)2LH!PBx3U6M`H;OvcRo!%jwO#-SbvgLO5y8TTI1?B>fEqrxOLUQY!SbMI6D$U(aPHQ#6Xf-H z7XuMHv!S~aMhQPu1n|Vv|AnwWQ5A1CZnh>XVSuLgaOUR0D&!1D-`dwR%JqrZ#-{{8R2$Lj8%hZw;g|=76+`h5X zWp?Rp=QPLrs@f1xO0sb6hGGD&Q{%Wdsq!~|B%SO|pG-EoZ)=?4gcKXyT45rL>YzsQ zcYfp23$Rm~9Q-GT_jQ(G%e@lCtSJ%L?KGYp1qwr#0w@E9u4>-Osi!xSfviO-DN#FPp$9dR_eU@>t08oy9ESjG!o>R)Q=3@k~7Yl}V zf5OQwo%@hQ`Xq`J-3T1U^Dvp9FRDLr0h8R2*9z9~NyeZCO#`VX z?TwEHv-c|udgm((y!vr=0prjTffq8p4d~?=RJzR)vdQLdQA}0!R#$Xrn1|3d=t>+{ z0oF+Tg9KnNjbg@tJJ;4zVMH02`-9pS`(tm$_R8a@vQLQo8A?R4Ex6f+q9AV$i$2&h z)vN0t>sz_XQV>H)i!AemfnMvE@Rn7&y)*sT$w(FN(-gPC^)~;}mEcJ?Nph$&wRaY}KC#3L^l7=xmO$-%{fnPWqPZXRPy7*zWAO(oL~rO7CO&TzzooK)F+qcUr#jBKzNP#stt`K=8v!tgTy{&jNs~GDCvh z+_))Us>72KFi-SRE@Mo%$PKVKVGcwg=Dfo-!W9c~Wykfd9c98jF=*O1;U4zB&k$`F z%T|=hqQE{F!daVd!!f{*TW%Ng!q36l)dHoiY!?7)#5kMRgo!Na(0_fc&s}-+Zes%_ z*a_nrQe$Hk1_u~?pLBV1Zs=~HJ}#DX@qOs7>kiuaF*aJw%xPS!GNxL2 zb2``I(9H`G5-+|*Hm>)AX;I&;WxudQK^Y!J`hK?sp}nDer}T``4Rin>9+YmZsv#c3)1`QLisLDWy56#w19R%%cQVJ zk>O;fF}q8y2}o1(D6GfR)EMzf*9j6wyp;!82M5z< zA|`j^C^rbmXII@!mtfzIzAv$#ktD>KS2TFPW*4N_?S&rXS2cg#haP~v{NV@oJ}#BrpQFf5FP}&3 zUc6Y8Bv~P(ILhqeY?droolsv#L0oB&FGa4EVsCb6b@7No&d|S^2h7NsU;nal8dkC4 zwR_vw>hO76jV4tGg^3-~;Zm3jUE>JGc@eqhA3!#Cyoe~G>}{~_xJcTR3Rew#IhL!c zK{DwxsV7NAO)4{-WheppPAPd4G$b1UT0o`02gxaP2!VbY&cn+6u5~aTy6ajN&uU6s zN_Sux=e8`T99#;!asxCr&4z-jpj!xJ&SX4>ya5@X#Zkz!{L~*YOP)x-Mh7M+Peh(NaDJrWk@j`cON$W zjQ8OO?3JTioa_>oZzZ^zeCBQXm`N8)se2CL#oBlsM^twhP|d_c_?Gs-yciW zePEMp;-b&GSDxBZjuqVnj=Rr%GXu=%C6V4>6cFs7)%3i6vN4e^Ay4;TTV{U0CYCb3a&dHKHlY3f(?v{%o5;nFw4im zqM}bd7P=#*{9)iH7%0sn@oN-1qeJ$Db;$8HHK9TD`BA3(Vn_1mmFj+ewN^P@x1}qV zVMk4f?uiGPFQ>2&0CJhvk*;uNxy`Us4({H#q3XTfuzN*!1tS8jFtvmc`F_PNN)(!F1#0tSs!>}YNup%wmp8m;ulddU zxOOgztr4I%Ksw2^I|v47Kq!*MLn2ueFUO1ds`%~GX$M8GO5N_#^b(m+Ddqf|JMtAC0W^(T+umAJd%;RB>Jj4eGag< z;c_E0&4AzW_kVeD+7Lx9-S8V)R)+7jVZgAZ)@zzDK86!=y#p;6=Pk*OH#`S#!{Ag)p$2apr%r+A z4{g8OtmrT#phKy#tI30lbGWsdsalGLDyHfvx~4c2(J)dXtR-Vh&7yFfhN+c*=&a1r zYi#w*Z_S0;ovhFsQ@i7x6ok)j&HAeaZIJot%53ku!sBwr$|#&HX~?8yWpG+}$PdSJ zuR$wmU=twIrv>I?2~~&usord6c(;VVNImgmD%FePZzyq&Vu2R?^}U0tM%RYDy_XLU zEJY4r7=fQF88vo}HUPE>F!t~qL>YI?39Ij+TiaM0A@;J27T|Gb2TTg11~g3B_2i{; zzx~);Db1jI_)@pw-l)Qd=0;G0keVe>CR4>KqjP-r@?0bnMZ

=6qWFWYBE`cuBCP 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 `%b6p{oSKtnx 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 z
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 z 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�@p)bduMffKJQM>M|*qTPB%+!b+}Iv6}k6A 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 FdH_$^YFz*T literal 0 HcmV?d00001 diff --git a/dev/erpnext/backups/clean-snapshot.sql.gz b/dev/erpnext/backups/clean-snapshot.sql.gz deleted file mode 100644 index 8905a1563460d8e9831388c0b394c657066a1ffb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 836707 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%0DW>a5Bcj$0RE0{x zMNpgf-eH7&$NH4ug!C!C!u(dpv(z$kGW4#}nDG6N-(>Ve;&Jx?uhRdOKGgmN|G$hVhYm{8&!5rd%qwj& zn^m2&1Q5P`!wHW=${H^sI8%BbQPW}ze`b+EoP9s>JYL2vYW@c9WjtIbUW5;Y9hkL| zEg5Kclt7nPsSI02Mb{`VRx-entyDg%&W1^2xiWHw+Nh z!2iJ0zE*a@l*z->7btJEK%XtwfxZo-PppH8W^!g{o%sNXY3R@qp)VRhmX@eOD)0By zgjI1!K7z#p(DNzE%)s@@-R)5h0qF+ICd*De zrfYu1m3mOQpI+UvPnI!@q7C_wV%uFI@4(O>O`E>%u%G?EbP(cLP-JK0bs*Pd8HamfV$A&ybFXj6xUwouEx^_TOSK5Y!U zi)GyU1=KabLu8{ts+*2TvZ_|U- zN(7^r?O1CM*~(WI!)P_|zSX3<{d*Wwi;%wJ7Bej0d_v9zk!D;)+xkWLR=i}PbEUkR zjl*%9kM81oZ}+LtuWXFaQvokOnNO;+fCGzajLoXlOWKFpIg9!$Mj)?`5%Wp#t zk@7^)kO`i@YK=oGpV6Yd+;CtH40;Z-Oo|k!k)WusidN5CaBAsCxU4`qD9s1>=dq8-GTv@hbP2qjFhIB zhhsXhON^9G0%A9jPNK(&XuhyalF7w5JwCPOF$y$}Q|VYCwj`#Q#G0Z^5iC}ILK^D_ zu=HJOUQ?l7xKC6z{F)|1^W(?@`W{#l8L^e$X$1DbupR@zZ0N=S3nfY~FH|&+2)Ati zMox!6+it{nRMp+UU7q}gp8tmb|#*`Z+l

qs$nA=Kf(~4#( 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�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

jIpS=0c)2Ai19?sCt}e@BK?;>m*@nE&XDL7 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�-D}~hwjXVnr4q%0(L8>Dsh=m{MDb`*#n%V~Q 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'ky&`#=->EGc%XM| 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»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

(Q3?W6M( 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 z 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 new Promise((resolve) => setTimeout(resolve, ms)) + +const trimTrailingSlash = (value) => value.replace(/\/+$/, "") + +const buildWebhookDefinitions = (baseUrl) => { + const normalizedBaseUrl = trimTrailingSlash(baseUrl) + return Object.fromEntries( + Object.entries(ROUTES).map(([key, route]) => [ + key, + { + url: `${normalizedBaseUrl}${route.path}`, + eventCategories: route.eventCategories, + }, + ]), + ) +} + +const extractNgrokHttpsUrl = (response) => { + const tunnel = response?.tunnels?.find( + (candidate) => + candidate?.proto === "https" && typeof candidate.public_url === "string", + ) + if (!tunnel) { + throw new Error("No HTTPS ngrok tunnel found on local ngrok API") + } + return tunnel.public_url +} + +const isObject = (value) => + value !== null && typeof value === "object" && !Array.isArray(value) + +const mergeDeep = (base, override) => { + const merged = { ...(isObject(base) ? base : {}) } + for (const [key, value] of Object.entries(override)) { + if (isObject(value) && isObject(merged[key])) { + merged[key] = mergeDeep(merged[key], value) + } else { + merged[key] = value + } + } + return merged +} + +const mergeDevOverrides = (existing, generated) => + mergeDeep(existing, { + bridge: { + apiKey: generated.apiKey, + baseUrl: generated.baseUrl, + webhook: { + uri: generated.webhookBaseUrl, + publicKeys: generated.publicKeys, + }, + }, + }) + +const reconcileBridgeWebhooks = async (api, definitions) => { + const existingWebhooks = await api.listWebhooks() + const webhooksToDelete = existingWebhooks.filter( + (webhook) => webhook.status !== "deleted", + ) + + for (const webhook of webhooksToDelete) { + await api.deleteWebhook(webhook.id) + } + + const publicKeys = {} + const created = {} + + for (const [key, definition] of Object.entries(definitions)) { + const webhook = await api.createWebhook({ key, ...definition }) + created[key] = webhook + publicKeys[key] = webhook.public_key + await api.enableWebhook(webhook.id, definition) + } + + return { + publicKeys, + created, + existingWebhookCount: existingWebhooks.length, + deletedWebhookCount: webhooksToDelete.length, + } +} + +const readYamlFile = (filePath) => { + if (!fs.existsSync(filePath)) return {} + return yaml.load(fs.readFileSync(filePath, "utf8")) ?? {} +} + +const writeYamlFile = (filePath, data) => { + fs.mkdirSync(path.dirname(filePath), { recursive: true }) + fs.writeFileSync(filePath, yaml.dump(data, { lineWidth: -1 }), "utf8") +} + +const defaultOverridesPath = () => { + const configDir = + process.env.CONFIG_PATH || path.join(process.env.HOME ?? ".", ".config/flash") + return path.join(configDir, "dev-overrides.yaml") +} + +const loadMergedConfig = ({ baseConfigPath, overridesPath }) => + mergeDeep(readYamlFile(baseConfigPath), readYamlFile(overridesPath)) + +const fetchJson = async ({ method, url, apiKey, body, idempotencyKey }) => { + const headers = { + "Api-Key": apiKey, + "Content-Type": "application/json", + } + if (idempotencyKey) { + headers["Idempotency-Key"] = idempotencyKey + } + + const response = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + }) + + const text = await response.text() + const parsed = text ? JSON.parse(text) : {} + if (!response.ok) { + throw new Error( + `Bridge ${method} ${url} failed (${response.status}): ${JSON.stringify(parsed)}`, + ) + } + return parsed +} + +const createBridgeApi = ({ apiKey, baseUrl }) => { + const normalizedBaseUrl = trimTrailingSlash(baseUrl) + return { + listWebhooks: async () => { + const response = await fetchJson({ + method: "GET", + url: `${normalizedBaseUrl}/webhooks`, + apiKey, + }) + return response.data ?? [] + }, + deleteWebhook: async (id) => + fetchJson({ + method: "DELETE", + url: `${normalizedBaseUrl}/webhooks/${id}`, + apiKey, + }), + createWebhook: async ({ key, url, eventCategories }) => + fetchJson({ + method: "POST", + url: `${normalizedBaseUrl}/webhooks`, + apiKey, + idempotencyKey: `flash-dev-${key}-${Date.now()}`, + body: { + url, + event_epoch: "webhook_creation", + event_categories: eventCategories, + }, + }), + enableWebhook: async (id, definition) => + fetchJson({ + method: "PUT", + url: `${normalizedBaseUrl}/webhooks/${id}`, + apiKey, + body: { + url: definition.url, + status: "active", + event_categories: definition.eventCategories, + }, + }), + } +} + +const getNgrokTunnels = async () => { + const response = await fetch("http://127.0.0.1:4040/api/tunnels") + if (!response.ok) { + throw new Error(`ngrok API returned ${response.status}`) + } + return response.json() +} + +const hasNgrok = () => { + const paths = (process.env.PATH ?? "").split(path.delimiter) + return paths.some((candidate) => fs.existsSync(path.join(candidate, "ngrok"))) +} + +const startNgrok = ({ port }) => { + if (!hasNgrok()) { + throw new Error("ngrok is not installed or not on PATH") + } + + const logPath = path.join( + process.env.TMPDIR ?? "/tmp", + `flash-bridge-ngrok-${port}.log`, + ) + const logFd = fs.openSync(logPath, "a") + const child = spawn("ngrok", ["http", String(port), "--log", "stdout"], { + detached: true, + stdio: ["ignore", logFd, logFd], + }) + child.unref() + return { pid: child.pid, logPath } +} + +const ensureNgrokTunnel = async ({ port, retries = 20, intervalMs = 500 }) => { + try { + return extractNgrokHttpsUrl(await getNgrokTunnels()) + } catch { + startNgrok({ port }) + } + + for (let attempt = 0; attempt < retries; attempt += 1) { + await sleep(intervalMs) + try { + return extractNgrokHttpsUrl(await getNgrokTunnels()) + } catch { + // ngrok is still starting; keep polling until retries are exhausted. + } + } + + throw new Error("ngrok did not expose an HTTPS tunnel before timeout") +} + +const parseArgs = (argv) => { + const args = { + baseConfigPath: "dev/config/base-config.yaml", + overridesPath: defaultOverridesPath(), + port: DEFAULT_PORT, + help: false, + } + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index] + if (arg === "--help" || arg === "-h") args.help = true + else if (arg === "--base-config") args.baseConfigPath = argv[++index] + else if (arg === "--overrides") args.overridesPath = argv[++index] + else if (arg === "--port") args.port = Number(argv[++index]) + else if (arg === "--api-key") args.apiKey = argv[++index] + else if (arg === "--base-url") args.baseUrl = argv[++index] + else throw new Error(`Unknown argument: ${arg}`) + } + + return args +} + +const usage = () => `Usage: node dev/setup-bridge-webhooks.js [options] + +Options: + --base-config Base YAML config path (default: dev/config/base-config.yaml) + --overrides Local override YAML path (default: $CONFIG_PATH/dev-overrides.yaml or ~/.config/flash/dev-overrides.yaml) + --port Local Bridge webhook port (default: 4009) + --api-key Bridge sandbox API key (default: existing config/env) + --base-url Bridge API base URL (default: existing config/env or sandbox) + --help Show this help message +` + +const run = async (argv = process.argv.slice(2)) => { + const args = parseArgs(argv) + if (args.help) { + console.log(usage()) + return + } + + const config = loadMergedConfig({ + baseConfigPath: args.baseConfigPath, + overridesPath: args.overridesPath, + }) + const apiKey = args.apiKey || process.env.BRIDGE_API_KEY || config.bridge?.apiKey + if (!apiKey) { + throw new Error( + "Bridge API key is required. Set bridge.apiKey in dev-overrides.yaml or pass --api-key.", + ) + } + + const baseUrl = + args.baseUrl || + process.env.BRIDGE_BASE_URL || + config.bridge?.baseUrl || + DEFAULT_BRIDGE_BASE_URL + + console.log(`Starting/using ngrok tunnel for localhost:${args.port}...`) + const webhookBaseUrl = await ensureNgrokTunnel({ port: args.port }) + console.log(`ngrok HTTPS URL: ${webhookBaseUrl}`) + + const definitions = buildWebhookDefinitions(webhookBaseUrl) + const bridgeApi = createBridgeApi({ apiKey, baseUrl }) + + console.log("Fetching and removing old Bridge sandbox webhooks...") + const { publicKeys, created, existingWebhookCount, deletedWebhookCount } = + await reconcileBridgeWebhooks(bridgeApi, definitions) + console.log(`Bridge reported ${existingWebhookCount} existing webhooks.`) + console.log(`Deleted ${deletedWebhookCount} old active/disabled webhooks.`) + + const existingOverrides = readYamlFile(args.overridesPath) + const merged = mergeDevOverrides(existingOverrides, { + apiKey, + baseUrl, + webhookBaseUrl, + publicKeys, + }) + writeYamlFile(args.overridesPath, merged) + + const activeCount = Object.keys(created).length + console.log(`Created and enabled ${activeCount} Bridge sandbox webhooks.`) + console.log("Smoke check passed: webhook public keys were returned and saved locally.") + console.log(`Updated local overrides: ${args.overridesPath}`) + console.log("") + console.log("Next steps:") + console.log(" 1. Start the Bridge webhook server:") + console.log( + ` yarn bridge-webhook --configPath ${args.baseConfigPath} --configPath ${args.overridesPath}`, + ) + console.log(" 2. Run the sandbox E2E suite:") + console.log(" IBEX_ENVIRONMENT=sandbox yarn test:bridge-sandbox-e2e:ci") +} + +if (require.main === module) { + run().catch((error) => { + console.error(`Bridge webhook setup failed: ${error.message}`) + process.exit(1) + }) +} + +module.exports = { + buildWebhookDefinitions, + createBridgeApi, + extractNgrokHttpsUrl, + mergeDevOverrides, + reconcileBridgeWebhooks, + run, +} diff --git a/dev/setup.sh b/dev/setup.sh index 3b001bef7..6ee3011e8 100755 --- a/dev/setup.sh +++ b/dev/setup.sh @@ -1,6 +1,6 @@ #!/bin/bash # Flash Dev Environment Setup -# Usage: ./dev/setup.sh +# Usage: ./dev/setup.sh [--dev|--webhook] # # This script validates your environment, installs dependencies, # configures credentials, and starts the development server. @@ -16,6 +16,52 @@ info() { echo -e "${GREEN}✓${NC} $1"; } warn() { echo -e "${YELLOW}⚠${NC} $1"; } fail() { echo -e "${RED}✗${NC} $1"; exit 1; } +RUN_WEBHOOK_SETUP=false +WEBHOOK_ONLY=false + +usage() { + cat << EOF +Usage: ./dev/setup.sh [--dev|--webhook] + +Options: + --dev Run normal dev setup, then configure Bridge sandbox webhooks. + --webhook Only configure Bridge sandbox webhooks and local dev overrides. + --help Show this help message. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --dev) + RUN_WEBHOOK_SETUP=true + shift + ;; + --webhook) + RUN_WEBHOOK_SETUP=true + WEBHOOK_ONLY=true + shift + ;; + --help|-h) + usage + exit 0 + ;; + *) + fail "Unknown argument: $1" + ;; + esac +done + +run_bridge_webhook_setup() { + echo "" + echo "Configuring Bridge sandbox webhooks..." + node dev/setup-bridge-webhooks.js +} + +if [ "$WEBHOOK_ONLY" = true ]; then + run_bridge_webhook_setup + exit 0 +fi + echo "" echo "═══════════════════════════════════════════════════" echo " Flash Backend — Development Environment Setup" @@ -135,6 +181,10 @@ echo "Starting Docker dependencies..." docker compose up bats-deps -d 2>&1 | grep -E '(Created|Started|Running)' || true info "Docker dependencies running" +if [ "$RUN_WEBHOOK_SETUP" = true ]; then + run_bridge_webhook_setup +fi + echo "" echo "═══════════════════════════════════════════════════" echo " ✅ Setup complete!" diff --git a/docs/bridge-integration/API.md b/docs/bridge-integration/API.md index 6ead13714..65211ab37 100644 --- a/docs/bridge-integration/API.md +++ b/docs/bridge-integration/API.md @@ -1,6 +1,6 @@ # Bridge.xyz GraphQL API Reference -All Bridge-related operations require the user to be authenticated and have an **Account Level 2** or higher. +All Bridge-related operations require the user to be authenticated and have an **Account Level 1** or higher. ## Mutations @@ -289,7 +289,7 @@ query BridgeWithdrawals { | Code | Description | | --- | --- | | `BRIDGE_DISABLED` | Bridge integration is disabled in configuration. | -| `BRIDGE_ACCOUNT_LEVEL_ERROR` | User account level is below the required Bridge access level. | +| `BRIDGE_ACCOUNT_LEVEL_ERROR` | User account level is below 1. | | `BRIDGE_INVALID_AMOUNT` | Withdrawal amount is malformed or not positive. | | `BRIDGE_BELOW_MINIMUM_WITHDRAWAL` | Withdrawal amount is below the configured minimum. | | `BRIDGE_KYC_PENDING` | Operation requires approved KYC, but it is still pending. | diff --git a/docs/bridge-integration/ARCHITECTURE.md b/docs/bridge-integration/ARCHITECTURE.md index 0ec665227..5a927736c 100644 --- a/docs/bridge-integration/ARCHITECTURE.md +++ b/docs/bridge-integration/ARCHITECTURE.md @@ -2,7 +2,7 @@ ## System Overview -The Bridge.xyz integration enables USD on-ramp and off-ramp functionality for Flash users. It allows users to convert between USD (via bank transfers) and USDT (on the Tron network), which is then integrated into the Flash ecosystem via IBEX. +The Bridge.xyz integration enables USD on-ramp and off-ramp functionality for Flash users. It allows users to convert between USD (via bank transfers) and USDT (Ethereum), which is then integrated into the Flash ecosystem via IBEX. ## Component Architecture @@ -10,7 +10,7 @@ The integration consists of three main components: 1. **Flash Backend**: The core service that orchestrates the flow between users, Bridge.xyz, and IBEX. It exposes a GraphQL API for the mobile app and handles webhooks from Bridge.xyz. 2. **Bridge.xyz API**: An external service that provides virtual bank accounts, KYC processing, and USD/USDT conversion. -3. **IBEX**: An external service used by Flash to manage Bitcoin and Lightning wallets, and in this context, to provide Tron USDT receive addresses and handle USDT deposits. +3. **IBEX**: An external service used by Flash to manage Bitcoin and Lightning wallets, and in this context, to provide USDT receive addresses and handle USDT deposits. ### Component Diagram @@ -29,7 +29,7 @@ The integration consists of three main components: | USD/USDT | USDT v v +----------------------------+ - | Tron Network | + | Ethereum Network | +----------------------------+ ``` @@ -38,16 +38,16 @@ The integration consists of three main components: ### On-Ramp (USD -> USDT) 1. **KYC**: User initiates KYC via Flash, which creates a Bridge customer and returns a KYC link (Persona). -2. **Virtual Account**: Once KYC is approved, Flash creates a Tron USDT address via IBEX and a Bridge virtual account pointing to that address. +2. **Virtual Account**: Once KYC is approved, Flash creates a Bridge virtual account for receiving USD deposits. 3. **Deposit**: User sends USD to the virtual account. -4. **Conversion**: Bridge converts USD to USDT and sends it to the Tron address. +4. **Conversion**: Bridge converts USD to USDT and sends it to the user's on-chain address. 5. **Credit**: IBEX detects the USDT deposit and notifies Flash via webhook, which credits the user's wallet. ### Off-Ramp (USDT -> USD) 1. **Link Bank**: User links an external bank account via Bridge's hosted UI. 2. **Withdrawal**: User initiates a withdrawal in Flash. -3. **Transfer**: Flash creates a Bridge transfer from the user's Tron address to the linked bank account. +3. **Transfer**: Flash initiates a Bridge transfer from the user's Bridge balance to the linked external bank account. 4. **Conversion**: Bridge converts USDT to USD and sends it to the bank via ACH. ## Technology Stack @@ -60,7 +60,7 @@ The integration consists of three main components: ## Security Model -- **Account Level**: Bridge functionality is restricted to users with Account Level 2 or higher. +- **Account Level**: Bridge functionality is restricted to users with Account Level 1 or higher. - **KYC**: All users must pass Bridge's KYC process (powered by Persona). - **Webhook Verification**: All incoming webhooks from Bridge.xyz are verified using asymmetric RSA-SHA256 signatures. - **Idempotency**: All critical API calls to Bridge include an `Idempotency-Key` to prevent duplicate transactions. diff --git a/docs/bridge-integration/FLOWS.md b/docs/bridge-integration/FLOWS.md index f147d3c4b..b1b8e7a40 100644 --- a/docs/bridge-integration/FLOWS.md +++ b/docs/bridge-integration/FLOWS.md @@ -24,7 +24,7 @@ User Flash App Flash Backend Bridge.xyz | (Persona Flow) | | | | | | | 7. kyc.approved | | | | |<--------------------| | - | | | 8. Create Tron Addr | | + | | | 8. Create USDT Addr | | | | |---------------------------------------->| | | | 9. Create Virt Acc | | | | |-------------------->| | @@ -51,12 +51,12 @@ User Flash App Flash Backend Bridge.xyz 5. **Redirect**: App opens the KYC link (Persona). 6. **Verification**: User completes identity verification. 7. **KYC Webhook**: Bridge sends `kyc.approved` webhook to Flash. -8. **Tron Address**: Flash requests a unique Tron USDT receive address from IBEX. -9. **Virtual Account**: Flash creates a Bridge virtual account linked to the Tron address. +8. **USDT Address**: Flash requests a unique USDT receive address from IBEX. +9. **Virtual Account**: Flash creates a Bridge virtual account linked to the receive address. 10. **Display Details**: User sees bank name, routing number, and account number in the app. 11. **Bank Transfer**: User initiates a transfer from their banking app. 12. **Conversion**: Bridge receives USD and converts it to USDT. -13. **Settlement**: Bridge sends USDT to the user's Tron address. +13. **Settlement**: Bridge sends USDT to the user's on-chain address. 14. **IBEX Webhook**: IBEX detects the incoming USDT and notifies Flash. 15. **Credit**: Flash credits the user's USDT wallet and sends a push notification. diff --git a/docs/bridge-integration/WEBHOOKS.md b/docs/bridge-integration/WEBHOOKS.md index 404d64c8d..f11308bd6 100644 --- a/docs/bridge-integration/WEBHOOKS.md +++ b/docs/bridge-integration/WEBHOOKS.md @@ -41,7 +41,7 @@ Sent when a user's KYC application is rejected. ### Deposit Events #### `deposit.completed` -Sent when a USD deposit to a virtual account is successfully converted to USDT and sent to the destination Tron address. +Sent when a USD deposit to a virtual account is successfully converted to USDT and sent to the user's on-chain address. - **Action**: This event is primarily for tracking. The actual crediting of the user's wallet is handled by the IBEX webhook when the USDT arrives. ### Transfer Events diff --git a/package.json b/package.json index 8f7903bd9..30ba998c0 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "test:unit": ". ./.env && LOGLEVEL=warn jest --config ./test/flash/unit/jest.config.js --bail --verbose $TEST", "test:legacy-integration": ". ./.env && LOGLEVEL=warn jest --config ./test/flash/legacy-integration/jest.config.js --bail --runInBand --verbose $TEST | yarn pino-pretty -c -l", "test:integration": ". ./.env && LOGLEVEL=warn jest --config ./test/flash/integration/jest.config.js --bail --runInBand --verbose $TEST", + "test:bridge-sandbox-e2e": ". ./.env && { [ ! -f ./.env.local ] || . ./.env.local; } && RUN_BRIDGE_SANDBOX_E2E=true LOGLEVEL=warn jest --config ./test/flash/bridge-sandbox-e2e/jest.config.js --bail --runInBand --verbose $TEST | yarn pino-pretty -c -l", + "test:bridge-sandbox-e2e:ci": ". ./.env && { [ ! -f ./.env.local ] || . ./.env.local; } && RUN_BRIDGE_SANDBOX_E2E=true LOGLEVEL=warn jest --config ./test/flash/bridge-sandbox-e2e/jest.config.js --bail --runInBand --verbose $TEST", "build-docs": "npx spectaql spectaql-config.yml -1", "fix-yaml": "prettier --write '**/*.(yaml|yml)'", "check-yaml": "prettier --check '**/*.(yaml|yml)'", @@ -212,4 +214,4 @@ "**/**/mongoose": "~7.5.1" }, "private": true -} \ No newline at end of file +} diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index b2c19fe57..052947ad7 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -98,7 +98,18 @@ type WithdrawalResult = { createdAt: string } -type KycStatusResult = "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" | null +type KycStatusResult = + | "open" + | "not_started" + | "incomplete" + | "awaiting_questionnaire" + | "awaiting_ubo" + | "under_review" + | "paused" + | "approved" + | "rejected" + | "offboarded" + | null type VirtualAccountResult = { bridgeVirtualAccountId: string @@ -166,9 +177,15 @@ const checkAccountLevel = async ( ): Promise => { const account = await AccountsRepository().findById(accountId) if (account instanceof Error) return account - if (account.level < 2) { - return new BridgeAccountLevelError() + if (account.level < 1) { + const err = new BridgeAccountLevelError() + baseLogger.warn( + { accountId, level: account.level, requiredLevel: 1 }, + "Bridge account level too low", + ) + return err } + return account } @@ -241,12 +258,18 @@ const initiateKyc = async ({ return result } catch (error) { - const bridgeError = error as { statusCode?: number; response?: { existing_kyc_link?: { kyc_link: string; customer_id: string; tos_link: string } } } + const bridgeError = error as { + statusCode?: number + response?: { + existing_kyc_link?: { kyc_link: string; customer_id: string; tos_link: string } + } + } if (bridgeError?.statusCode === 400 && bridgeError.response?.existing_kyc_link) { - - // store the customer id and the kyc status - const customerId = toBridgeCustomerId(bridgeError.response.existing_kyc_link.customer_id) + // store the customer id and the kyc status + const customerId = toBridgeCustomerId( + bridgeError.response.existing_kyc_link.customer_id, + ) await AccountsRepository().updateBridgeFields(accountId, { bridgeCustomerId: customerId, bridgeKycStatus: "not_started", @@ -288,7 +311,6 @@ const createVirtualAccount = async ( const account = await checkAccountLevel(accountId) if (account instanceof Error) return account - const PENDING_BRIDGE_STATUSES = new Set([ "incomplete", "awaiting_questionnaire", @@ -298,7 +320,6 @@ const createVirtualAccount = async ( ]) try { - if (!account.bridgeCustomerId) { return new BridgeCustomerNotFoundError( "Account has no Bridge customer ID. Complete KYC first.", @@ -311,19 +332,18 @@ const createVirtualAccount = async ( ) } - const customer = await BridgeApiClient.getCustomer(customerId); + const customer = await BridgeApiClient.getCustomer(customerId) if (customer instanceof Error) { baseLogger.error( { accountId, error: customer }, - "Failed to retrieve Bridge customer status" + "Failed to retrieve Bridge customer status", ) return customer } const kycStatus = customer.status - // Check KYC status if (kycStatus === "offboarded") { return new BridgeKycOffboardedError() @@ -331,10 +351,10 @@ const createVirtualAccount = async ( if (kycStatus === "rejected") { return new BridgeKycRejectedError() } - if (PENDING_BRIDGE_STATUSES.has(kycStatus!) || kycStatus as string === "open") { + if (PENDING_BRIDGE_STATUSES.has(kycStatus!) || (kycStatus as string) === "open") { return new BridgeKycPendingError() } - if (kycStatus !== "active" && kycStatus as string !== "approved") { + if (kycStatus !== "active" && (kycStatus as string) !== "approved") { return new BridgeKycPendingError("KYC not yet completed") } @@ -806,8 +826,6 @@ const getKycStatus = async (accountId: AccountId): Promise va.destination.address === account.bridgeEthereumAddress) + const relatedVa = bridgeVirtualAccounts.find( + (va) => va.destination.address === account.bridgeEthereumAddress, + ) if (relatedVa?.status === "activated") { - // if there's a related VA on Bridge side but it's not in our repo, create it in our repo to keep them in sync if (existingVa instanceof RepositoryError) { const repoResult = await BridgeAccountsRepo.createVirtualAccount({ accountId: accountId as string, bridgeVirtualAccountId: relatedVa.id, bankName: relatedVa.source_deposit_instructions.bank_name || "", - routingNumber: relatedVa.source_deposit_instructions.bank_routing_number || "", - accountNumber: relatedVa.source_deposit_instructions.bank_account_number || "", - accountNumberLast4: relatedVa.source_deposit_instructions.bank_account_number?.slice(-4) || "", + routingNumber: + relatedVa.source_deposit_instructions.bank_routing_number || "", + accountNumber: + relatedVa.source_deposit_instructions.bank_account_number || "", + accountNumberLast4: + relatedVa.source_deposit_instructions.bank_account_number?.slice(-4) || "", }) if (repoResult instanceof Error) { baseLogger.error( @@ -892,7 +913,6 @@ const getKycStatus = async (accountId: AccountId): Promise { + const date = value ? new Date(value) : new Date() + + if (Number.isNaN(date.getTime())) return value ?? "" + + const pad = (part: number) => String(part).padStart(2, "0") + + return [ + `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`, + `${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}`, + ].join(" ") +} + export class BridgeTransferRequest { static doctype = "Bridge Transfer Request" readonly input: BridgeTransferRequestInput @@ -73,8 +86,10 @@ export class BridgeTransferRequest { source_event_id: this.input.sourceEventId, source_event_type: this.input.sourceEventType, source_systems_seen: sourceSystemsSeen || undefined, - first_seen_at: this.input.firstSeenAt, - last_seen_at: this.input.lastSeenAt ?? new Date().toISOString(), + first_seen_at: this.input.firstSeenAt + ? toFrappeDatetime(this.input.firstSeenAt) + : undefined, + last_seen_at: toFrappeDatetime(this.input.lastSeenAt), raw_payload_json: this.input.rawPayload === undefined ? undefined diff --git a/test/flash/bridge-sandbox-e2e/README.md b/test/flash/bridge-sandbox-e2e/README.md new file mode 100644 index 000000000..f43f89b0c --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/README.md @@ -0,0 +1,208 @@ +# Bridge Sandbox E2E Tests + +This suite exercises Bridge sandbox flows through the public GraphQL schema and local webhook handlers. It is opt-in by design: normal unit and integration test runs do not execute these specs. + +## What It Covers + +- Bridge KYC initiation +- Optional Bridge virtual account creation +- Optional external account link URL generation +- External-account webhook handling +- Deposit webhook handling and idempotency +- Withdrawal validation error paths +- Optional cash wallet cutover smoke checks +- Optional ETH-USDT Lightning parity smoke checks + +The suite uses local MongoDB for test user setup, real Bridge/IBEX sandbox configuration for Bridge mutations, and direct Express handler injection for webhook tests. Webhook injection avoids requiring a public tunnel while still exercising the production route handlers. + +## Prerequisites + +Run from the repository root: + +```bash +cd /path/to/your/repo +``` + +### `.env` Setup (First Run) + +The package scripts source `.env` from the project root, then source `.env.local` when it exists. Create or update `.env` with at minimum: + +```bash +# Required +export IBEX_ENVIRONMENT=sandbox +export MONGODB_CON=mongodb://localhost:27017/flash + +# Bridge sandbox — fill from Bridge dashboard +# These are the API key and webhook secret, not stored in .env directly in production: +export BRIDGE_BASE_URL=https://api.sandbox.bridge.xyz/v0 +export BRIDGE_WEBHOOK_URL=http://localhost:4009 +``` + +### Bridge Webhook Setup + +For localhost testing, use ngrok and the setup helper: + +```bash +./dev/setup.sh --webhook +``` + +The helper: + +1. Starts or reuses `ngrok http 4009`. +2. Lists existing Bridge sandbox webhooks. +3. Deletes old active/disabled Bridge sandbox webhooks. +4. Creates fresh `kyc`, `deposit`, `transfer`, and `external_account` webhooks. +5. Copies the returned Bridge webhook public keys into `~/.config/flash/dev-overrides.yaml`. +6. Prints the command to start the local Bridge webhook server. + +The helper writes local secrets and public keys to `~/.config/flash/dev-overrides.yaml`; do not hard-code them in `dev/config/base-config.yaml`. + +### Required Setup + +- Node dependencies installed or available in the worktree (`yarn install`). +- `.env` present and sourceable by the package script. +- MongoDB available using the repo's normal test configuration. +- `IBEX_ENVIRONMENT=sandbox` in `.env`. +- Bridge sandbox webhook public keys populated in `~/.config/flash/dev-overrides.yaml`: + + ```yaml + bridge: + webhook: + publicKeys: + kyc: "" + deposit: "" + transfer: "" + external_account: "" + ``` + +- `src/services/bridge/index.ts` service guard allowing Level 1 accounts (✅ already applied in this PR). + +The setup file enforces the two safety gates: + +- `RUN_BRIDGE_SANDBOX_E2E=true` +- `IBEX_ENVIRONMENT=sandbox` + +The package scripts set `RUN_BRIDGE_SANDBOX_E2E=true` automatically, but `IBEX_ENVIRONMENT` must already be present in `.env` or exported in the shell. + +## First Run (Human Verification) + +Run from the worktree root: + +```bash +cd /path/to/your/repo +source .env +IBEX_ENVIRONMENT=sandbox yarn test:bridge-sandbox-e2e +``` + +### What to check on first run + +| Layer | What to verify | If it fails | +|-------|---------------|------------| +| Preflight | Source-code check of `checkAccountLevel()` allows level ≥ 1 | `src/services/bridge/index.ts` guard must be `level < 1`, not `level < 2` | +| KYC spec | `bridgeInitiateKyc` returns `{kycLink, tosLink}` URLs | Ensure ENG-345 deployed, sandbox has Bridge customer API set up | +| Virtual account | Skipped by default; with `BRIDGE_SANDBOX_VIRTUAL_ACCOUNT_CONFIRMED=true`, `bridgeCreateVirtualAccount` returns account details | Requires a Bridge-side KYC-approved sandbox customer; local webhook injection alone does not approve the hosted Bridge customer | +| External account | Skipped by default; with `BRIDGE_SANDBOX_EXTERNAL_ACCOUNT_LINK_CONFIRMED=true`, `bridgeAddExternalAccount` returns `{linkUrl, expiresAt}` | Requires Bridge sandbox API key/customer entitlement for hosted bank-linking | +| Deposit webhook | Injected webhook processes and persists deposit | Verify webhook secret in config.yaml | +| Withdrawal error paths | Validation errors returned for invalid inputs | Check withdrawal schema deployed (ENG-348) | +| Withdrawal **success** path | ⚠️ **Not expected to pass first run** — requires real KYC-approved sandbox customer, funded wallet, and verified external account. | The full withdrawal flow only runs with `BRIDGE_SANDBOX_WITHDRAWAL_CONFIRMED=true`; error-path tests run without it | + +### If something fails + +1. Check `IBEX_ENVIRONMENT` is `sandbox` (not `production`) +2. Confirm MongoDB is running: `mongosh --eval "db.adminCommand('ping')"` +3. Run `./dev/setup.sh --webhook` again to refresh ngrok, Bridge webhook endpoints, and local public keys +4. Preflight failure → `src/services/bridge/index.ts` still has `level < 2` — apply the Task 0 fix +5. KYC/VA failures → confirm the corresponding ENG issue is deployed to sandbox + +## Commands + +Run the whole suite: + +```bash +export IBEX_ENVIRONMENT=sandbox +yarn test:bridge-sandbox-e2e +``` + +Run the CI-style variant without `pino-pretty`: + +```bash +export IBEX_ENVIRONMENT=sandbox +yarn test:bridge-sandbox-e2e:ci +``` + +Run one spec: + +```bash +export IBEX_ENVIRONMENT=sandbox +TEST=test/flash/bridge-sandbox-e2e/deposit-withdrawal.spec.ts yarn test:bridge-sandbox-e2e:ci +``` + +Increase timeout for slow sandbox calls: + +```bash +export IBEX_ENVIRONMENT=sandbox +JEST_TIMEOUT=240000 yarn test:bridge-sandbox-e2e:ci +``` + +## Optional Smoke Gates + +These specs are skipped unless explicitly enabled: + +```bash +export IBEX_ENVIRONMENT=sandbox +CUTOVER_TESTS=true yarn test:bridge-sandbox-e2e:ci +``` + +```bash +export IBEX_ENVIRONMENT=sandbox +LN_PARITY_TESTS=true yarn test:bridge-sandbox-e2e:ci +``` + +These Bridge-hosted success paths are also skipped unless explicitly enabled because +they require sandbox state/entitlements outside the local test harness: + +```bash +export IBEX_ENVIRONMENT=sandbox +BRIDGE_SANDBOX_VIRTUAL_ACCOUNT_CONFIRMED=true yarn test:bridge-sandbox-e2e:ci +``` + +```bash +export IBEX_ENVIRONMENT=sandbox +BRIDGE_SANDBOX_EXTERNAL_ACCOUNT_LINK_CONFIRMED=true yarn test:bridge-sandbox-e2e:ci +``` + +## Files + +- `jest.config.js` - Jest config scoped to this suite. +- `jest.setup.ts` - opt-in guards, yargs config-path mock, MongoDB setup, Redis/Mongo cleanup. +- `config-overrides.yaml` - sandbox-only non-secret overrides used by Jest after local dev overrides. +- `preflight.ts` - source check that verifies Bridge Level 1 access is not blocked by the service guard. +- `helpers.ts` - test user creation, GraphQL execution, Bridge mutation wrappers, webhook injection, ERPNext lookup, deposit lookup. +- `helpers/http-utils.ts` - mock Express request/response objects for route-handler injection. +- `kyc-virtual-account.spec.ts` - KYC link and virtual account flow. +- `external-account.spec.ts` - Plaid link URL and external-account webhook behavior. +- `deposit-withdrawal.spec.ts` - deposit webhook handling, deposit persistence, withdrawal validation paths. +- `cutover-state.spec.ts` - optional cash wallet cutover state smoke test. +- `ln-parity.spec.ts` - optional Lightning USD invoice smoke test. + +## Known Limitations + +- The external account spec verifies injected webhook behavior by default. Link URL generation is gated because some Bridge sandbox keys/customers are not authorized for hosted bank-linking. +- The deposit tests validate webhook handling and persistence. Full wallet-balance reconciliation depends on sandbox deposit state and is not asserted yet. +- Virtual account and withdrawal success are not covered by default because they require a real Bridge-side KYC-approved sandbox customer, funded wallet, and verified external account. +- Deposit webhook processing writes `BridgeTransferRequest` audit rows to the local ERPNext instance when `~/.config/flash/dev-overrides.yaml` points Frappe at the local Docker site. +- The suite uses Jest `forceExit` because importing the public GraphQL schema creates app-wide Redis clients; teardown calls `disconnectAll()`, but ioredis TCP handles can otherwise keep the opt-in E2E process alive after the tests finish. + +## Troubleshooting + +If the suite exits before running tests, check the setup guards first: + +```bash +echo "$IBEX_ENVIRONMENT" +``` + +It must print `sandbox`. + +If MongoDB setup fails, start the repo's normal local services before rerunning. The suite creates local test users and wallets before calling Bridge flows. + +If preflight fails, inspect the guard in `src/services/bridge/index.ts`. The suite expects `BridgeService.checkAccountLevel()` to block Level 0 only, so Level 1 accounts can run Bridge operations. diff --git a/test/flash/bridge-sandbox-e2e/config-overrides.yaml b/test/flash/bridge-sandbox-e2e/config-overrides.yaml new file mode 100644 index 000000000..f00b74ad0 --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/config-overrides.yaml @@ -0,0 +1,2 @@ +sendgrid: + apiKey: "SG.sandbox-e2e-placeholder" diff --git a/test/flash/bridge-sandbox-e2e/cutover-state.spec.ts b/test/flash/bridge-sandbox-e2e/cutover-state.spec.ts new file mode 100644 index 000000000..ab69d191d --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/cutover-state.spec.ts @@ -0,0 +1,102 @@ +/** + * Bridge Sandbox E2E — Post-Cutover State Assertions + * + * Verifies the system-wide cash wallet cutover state via the public + * `cashWalletCutover` query, and optionally validates that accounts + * see correct wallet routing after cutover. + * + * The cutover state is a system-wide singleton (not per-account) stored in + * the CashWalletCutoverConfig collection. + * + * This spec is guarded by SKIP_CUTOVER_TESTS — it only runs actively when + * CUTOVER_TESTS=true is set, since sandbox environments may not have + * cutover infrastructure seeded. + * + * Verified shapes (from source audit of `cashWalletCutover.ts`, `lifecycle.ts`): + * - cashWalletCutover query (public) returns CashWalletCutoverObject: + * { state: CashWalletCutoverState!, scheduledAt, startedAt, + * completedAt, pausedAt, pauseReason, cutoverVersion: Int!, + * runId, updatedBy, updatedAt: Timestamp! } + * - Valid states: "not_started", "started", "provisioned", "balance_read", + * "invoice_created", "balance_move_sending", "balance_move_sent", + * "balance_move_verified", "fee_reimbursement_invoice_created", + * "fee_reimbursement_sending", "fee_reimbursed", "pointer_flipped", + * "legacy_zero_verified", "complete", "failed", "requires_operator_review", + * "skipped_already_migrated", "rollback_started", "rolled_back" + */ + +const CUTOVER_TESTS = process.env.CUTOVER_TESTS === "true" + +;(CUTOVER_TESTS ? describe : describe.skip)("Post-Cutover State", () => { + describe("System-wide cutover config query", () => { + // The cutover query doesn't use auth context, but + // execQuery requires an accountId for context building. + const dummyAccountId = `acct_cutover_test_${Date.now()}` + + it("returns cashWalletCutover with expected shape", async () => { + const { execQuery } = await import("./helpers") + + const source = ` + query CashWalletCutover { + cashWalletCutover { + state + cutoverVersion + runId + scheduledAt + startedAt + completedAt + pausedAt + pauseReason + updatedAt + updatedBy + } + } + ` + + const response = await execQuery(source, dummyAccountId) + + expect(response.cashWalletCutover).toBeDefined() + expect(typeof response.cashWalletCutover.state).toBe("string") + expect(response.cashWalletCutover.cutoverVersion).toEqual(expect.any(Number)) + expect(typeof response.cashWalletCutover.updatedAt).toBe("string") + }) + + it("state is a valid cutover state enum value", async () => { + const { execQuery } = await import("./helpers") + + const VALID_STATES = new Set([ + "not_started", + "started", + "provisioned", + "balance_read", + "invoice_created", + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "fee_reimbursed", + "pointer_flipped", + "legacy_zero_verified", + "complete", + "failed", + "requires_operator_review", + "skipped_already_migrated", + "rollback_started", + "rolled_back", + ]) + + const source = ` + query CashWalletCutover { + cashWalletCutover { + state + } + } + ` + + const response = await execQuery(source, dummyAccountId) + + expect(VALID_STATES.has(response.cashWalletCutover?.state)).toBe(true) + }) + }) +}) diff --git a/test/flash/bridge-sandbox-e2e/deposit-withdrawal.spec.ts b/test/flash/bridge-sandbox-e2e/deposit-withdrawal.spec.ts new file mode 100644 index 000000000..d7389032a --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/deposit-withdrawal.spec.ts @@ -0,0 +1,194 @@ +/** + * Bridge Sandbox E2E — Deposit → Withdrawal Lifecycle + * + * Tests the deposit webhook processing and withdrawal initiation flow. + * + * Verified shapes (from source audit): + * - depositHandler accepts + * { event_id, event_object: { id, state, amount, currency, on_behalf_of, receipt? } } + * returns { status: "success" } on 200 + * - bridgeInitiateWithdrawal(input: { amount: String!, externalAccountId: ID! }) returns + * { errors, withdrawal: { id, amount, currency, status, failureReason, createdAt } } + * + * The deposit handler is fully testable via webhook injection — it's self-contained + * and persists to BridgeDeposits + optional ERPNext. + * + * The withdrawal mutation calls Bridge API's createTransfer endpoint, which requires + * real sandbox state (KYC'd customer, funded wallet, verified external account). + * Full-flow tests are guarded by BRIDGE_SANDBOX_WITHDRAWAL_CONFIRMED=true. + * Error-path tests verify expected failures when prerequisites are missing. + * + * ⚠️ The deposit handler triggers reconciliation only when state === "payment_processed" + * WITH a destination_tx_hash. For sandbox testing, the reconciliation may or may not + * succeed — the test asserts the handler responds correctly regardless. + */ + +import { + createBridgeSandboxUser, + initiateWithdrawal, + injectDepositWebhook, + findDepositLogByEventId, + BridgeTestUser, +} from "./helpers" + +describe("Bridge Deposit → Withdrawal", () => { + let user: BridgeTestUser + + beforeAll(async () => { + user = await createBridgeSandboxUser(1) + }) + + // ============ Deposit Webhook ============ + + describe("Deposit Webhook Processing", () => { + it("processes a valid deposit webhook and returns success", async () => { + const eventId = `dep-test-${Date.now()}` + const response = await injectDepositWebhook({ + event_id: eventId, + event_object: { + id: `transfer_test_${Date.now()}`, + state: "payment_processed", + amount: "100.00", + currency: "usdt", + on_behalf_of: `sandbox_cus_${user.accountId.slice(-8)}`, + receipt: { + initial_amount: "100.00", + subtotal_amount: "100.00", + final_amount: "96.00", + developer_fee: "2.00", + destination_tx_hash: `0x${Date.now().toString(16)}dead`, + }, + }, + }) + + expect(response.status).toBe(200) + expect(response.body).toBeDefined() + expect(response.body.status).toBe("success") + }) + + it("persists a deposit log to the BridgeDeposits collection", async () => { + const eventId = `dep-log-${Date.now()}` + const transferId = `transfer_log_${Date.now()}` + const response = await injectDepositWebhook({ + event_id: eventId, + event_object: { + id: transferId, + state: "payment_processed", + amount: "50.00", + currency: "usdt", + on_behalf_of: `sandbox_cus_${user.accountId.slice(-8)}`, + }, + }) + + expect(response.status).toBe(200) + + // Query the deposit log directly + const log = await findDepositLogByEventId(eventId) + expect(log).toBeTruthy() + expect(log!.eventId).toBe(eventId) + expect(log!.transferId).toBe(transferId) + expect(log!.amount).toBe("50.00") + expect(log!.currency).toBe("usdt") + expect(log!.state).toBe("payment_processed") + }) + + it("returns already_processed for duplicate deposit webhooks", async () => { + const eventId = `dep-dup-${Date.now()}` + const payload = { + event_id: eventId, + event_object: { + id: `transfer_dup_${Date.now()}`, + state: "payment_processed", + amount: "25.00", + currency: "usdt", + on_behalf_of: `sandbox_cus_${user.accountId.slice(-8)}`, + }, + } + + // First call + const first = await injectDepositWebhook(payload) + expect(first.status).toBe(200) + expect(first.body.status).toBe("success") + + // Duplicate event_id — idempotency lock fires before the handler re-processes + const second = await injectDepositWebhook(payload) + expect(second.status).toBe(200) + expect(second.body.status).toBe("already_processed") + }) + + it("handles intermediate state transitions (not just payment_processed)", async () => { + const response = await injectDepositWebhook({ + event_id: `dep-pending-${Date.now()}`, + event_object: { + id: `transfer_pending_${Date.now()}`, + state: "pending_transfer", + amount: "75.00", + currency: "usdt", + on_behalf_of: `sandbox_cus_${user.accountId.slice(-8)}`, + }, + }) + + // Intermediate states are logged and return success but do NOT trigger + // reconciliation (only payment_processed with tx hash does) + expect(response.status).toBe(200) + expect(response.body.status).toBe("success") + }) + + it("rejects a deposit webhook with missing required fields", async () => { + const response = await injectDepositWebhook({ + event_id: "dep-invalid", + event_object: { + id: "", + state: "", + amount: "", + currency: "", + on_behalf_of: "", + }, + }) + + // Handler validates presence of event_object.id, event_id, amount, on_behalf_of + expect(response.status).toBe(400) + }) + }) + + // ============ Withdrawal ============ + + describe("Withdrawal Initiation", () => { + it("rejects withdrawal when amount is below minimum", async () => { + // minimum withdrawal is 2 (from config), so 0.50 should be rejected + const result = await initiateWithdrawal(user.accountId, { + amount: "0.50", + externalAccountId: "ext_acct_placeholder", + }) + + expect(result.errors).toBeDefined() + expect(result.errors.length).toBeGreaterThan(0) + expect(result.errors[0].message).toMatch(/minimum/i) + expect(result.withdrawal).toBeNull() + }) + + it("rejects withdrawal when amount is invalid (non-numeric)", async () => { + const result = await initiateWithdrawal(user.accountId, { + amount: "abc", + externalAccountId: "ext_acct_placeholder", + }) + + expect(result.errors).toBeDefined() + expect(result.errors.length).toBeGreaterThan(0) + expect(result.withdrawal).toBeNull() + }) + + it("rejects withdrawal when account has no Bridge customer ID", async () => { + // No KYC has been initiated for this account, so bridgeCustomerId is null + const result = await initiateWithdrawal(user.accountId, { + amount: "50.00", + externalAccountId: "ext_acct_no_customer", + }) + + expect(result.errors).toBeDefined() + expect(result.errors.length).toBeGreaterThan(0) + expect(result.errors[0].message).toMatch(/customer|KYC/i) + expect(result.withdrawal).toBeNull() + }) + }) +}) diff --git a/test/flash/bridge-sandbox-e2e/external-account.spec.ts b/test/flash/bridge-sandbox-e2e/external-account.spec.ts new file mode 100644 index 000000000..412eefd03 --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/external-account.spec.ts @@ -0,0 +1,156 @@ +/** + * Bridge Sandbox E2E — External Account (Plaid) Flow + * + * Tests the `bridgeAddExternalAccount` mutation and external-account + * webhook handling. + * + * Verified shapes (from source audit): + * - bridgeAddExternalAccount returns + * { errors, externalAccount: { linkUrl: string!, expiresAt: string! } } + * - externalAccountHandler accepts + * { event_id, event_object: { id, customer_id, bank_name, last_4, active } } + * and returns { status: "success" } or { status: "already_processed" } on 200 + * + * ⚠️ Plaid sandbox linking is a manual step — the test generates the link URL + * and verifies it's well-formed, then simulates the webhook that follows + * successful Plaid linking. The test does NOT automate the Plaid browser UI. + * + * ⚠️ Some sandboxes return "link_url" instead of "linkUrl" — check actual response + * against the configured return shape and update assertions if needed. + */ + +import { + createBridgeSandboxUser, + initiateKyc, + addExternalAccount, + injectKycWebhook, + injectExternalAccountWebhook, + getAccountById, + BridgeTestUser, +} from "./helpers" + +const EXTERNAL_ACCOUNT_LINK_TESTS = + process.env.BRIDGE_SANDBOX_EXTERNAL_ACCOUNT_LINK_CONFIRMED === "true" + +describe("Bridge External Account", () => { + let user: BridgeTestUser + + beforeAll(async () => { + user = await createBridgeSandboxUser(1) + + // === Prerequisites: KYC + Virtual Account === + const kycResult = await initiateKyc( + user.accountId, + `ext-acct-${user.accountId.slice(-8)}-${Date.now()}@test.flashapp.me`, + ) + if (kycResult.errors?.length) { + throw new Error(`KYC initiation failed: ${kycResult.errors[0].message}`) + } + + // Approve KYC via webhook injection using the customer ID persisted by KYC initiation. + const account = await getAccountById(user.accountId) + const webhookCustomerId = account.bridgeCustomerId + if (!webhookCustomerId) { + throw new Error("KYC initiation did not persist a Bridge customer ID") + } + user.customerId = webhookCustomerId + const webhookResult = await injectKycWebhook({ + event_id: `ext-acct-kyc-${Date.now()}`, + event_object: { customer_id: webhookCustomerId, kyc_status: "approved" }, + }) + if (webhookResult.status !== 200) { + throw new Error(`KYC webhook failed with status ${webhookResult.status}`) + } + }) + ;(EXTERNAL_ACCOUNT_LINK_TESTS ? describe : describe.skip)( + "Plaid Link URL Generation", + () => { + it("generates a Plaid link URL when called", async () => { + const result = await addExternalAccount(user.accountId) + + expect(result.errors).toBeDefined() + expect(result.errors).toHaveLength(0) + expect(result.externalAccount).toBeDefined() + expect(result.externalAccount!.linkUrl).toBeTruthy() + expect(result.externalAccount!.linkUrl).toMatch(/^https:\/\//) + expect(result.externalAccount!.expiresAt).toBeTruthy() + }) + + it("link URL is different on each call (one-time use tokens)", async () => { + const result1 = await addExternalAccount(user.accountId) + const result2 = await addExternalAccount(user.accountId) + + expect(result1.errors).toHaveLength(0) + expect(result2.errors).toHaveLength(0) + + // Plaid link tokens are one-time use; consecutive calls should differ + expect(result1.externalAccount?.linkUrl).toBeTruthy() + expect(result2.externalAccount?.linkUrl).toBeTruthy() + expect(result1.externalAccount!.linkUrl).not.toBe( + result2.externalAccount!.linkUrl, + ) + }) + }, + ) + + describe("External Account Webhook Processing", () => { + it("processes a valid external-account webhook and returns success", async () => { + // This simulates what Bridge sends after a user completes the Plaid flow + const response = await injectExternalAccountWebhook({ + event_id: `ext-created-${Date.now()}`, + event_object: { + id: `ext_acct_test_${Date.now()}`, + customer_id: user.customerId!, + bank_name: "Test Bank", + last_4: "1234", + active: true, + }, + }) + + expect(response.status).toBe(200) + expect(response.body).toBeDefined() + expect(response.body.status).toBe("success") + }) + + it("returns already_processed for duplicate external-account webhooks", async () => { + const eventId = `ext-duplicate-${Date.now()}` + const payload = { + event_id: eventId, + event_object: { + id: `ext_acct_dup_${Date.now()}`, + customer_id: user.customerId!, + bank_name: "Test Bank", + last_4: "9999", + active: true, + }, + } + + // First call — should succeed + const first = await injectExternalAccountWebhook(payload) + expect(first.status).toBe(200) + expect(first.body.status).toBe("success") + + // Second call with same event_id — idempotency lock + const second = await injectExternalAccountWebhook(payload) + expect(second.status).toBe(200) + expect(second.body.status).toBe("already_processed") + }) + + it("rejects a webhook with missing customer_id", async () => { + const response = await injectExternalAccountWebhook({ + event_id: `ext-missing-${Date.now()}`, + event_object: { + id: "ext_acct_missing_cus", + customer_id: "", + bank_name: "No Customer", + last_4: "0000", + active: true, + }, + }) + + // Handler validates customer_id presence — returns 400 or 503 depending + // on whether it fails the initial guard (400) or the account lookup (503) + expect(response.status).toBeGreaterThanOrEqual(400) + }) + }) +}) diff --git a/test/flash/bridge-sandbox-e2e/helpers.ts b/test/flash/bridge-sandbox-e2e/helpers.ts new file mode 100644 index 000000000..e2fb5b939 --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/helpers.ts @@ -0,0 +1,374 @@ +/** + * Bridge Sandbox E2E — Helpers + * + * Shared utilities for sandbox end-to-end tests. + * Follows the test/galoy/helpers pattern: executes GraphQL operations + * via the schema (graphql() from the graphql library), not via + * direct resolver calls. + * + * All GraphQL return shapes verified against source code. + */ + +import { graphql, Source } from "graphql" + +import { createAccountWithPhoneIdentifier } from "@app/accounts" +import { addWalletIfNonexistent } from "@app/accounts/add-wallet" +import { DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES } from "@app/cash-wallet-cutover/client-capability" +import { getDefaultAccountsConfig } from "@config" +import { AccountLevel } from "@domain/accounts" +import { CouldNotFindAccountFromKratosIdError, RepositoryError } from "@domain/errors" +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" +import { gqlMainSchema } from "@graphql/public" +import { depositHandler } from "@services/bridge/webhook-server/routes/deposit" +import { externalAccountHandler } from "@services/bridge/webhook-server/routes/external-account" +import { kycHandler } from "@services/bridge/webhook-server/routes/kyc" +import { AuthWithPhonePasswordlessService } from "@services/kratos" +import { + AccountsRepository, + UsersRepository, + WalletsRepository, +} from "@services/mongoose" +import { AccountsRepository as AccountsRepo } from "@services/mongoose/accounts" +import { Account as AccountModel, BridgeDeposits } from "@services/mongoose/schema" + +import { createReqRes } from "./helpers/http-utils" + +import { randomPhone } from "test/galoy/helpers" + +// ============ Types ============ + +export interface BridgeTestUser { + accountId: string + walletId: string + customerId?: string + virtualAccountId?: string + level: AccountLevel +} + +type GraphQlErrorResponse = { + errors: Array<{ message: string }> +} + +type KycInitiationResult = GraphQlErrorResponse & { + kycLink?: { kycLink: string; tosLink: string } +} + +type VirtualAccountResult = GraphQlErrorResponse & { + virtualAccount?: Record +} + +type ExternalAccountResult = GraphQlErrorResponse & { + externalAccount?: { linkUrl: string; expiresAt: string } +} + +type WithdrawalResult = GraphQlErrorResponse & { + withdrawal?: Record | null +} + +type HandlerResponse = { + status: number + body?: unknown +} + +// ============ Schema Execution ============ + +function buildContext(accountId: string): GraphQLPublicContextAuth { + return { + domainAccount: { id: accountId, level: 1 }, + cashWalletClientCapabilities: DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES, + } as GraphQLPublicContextAuth +} + +export async function execQuery( + source: string, + accountId: string, + variableValues?: Record, +): Promise | GraphQlErrorResponse> { + const result = await graphql({ + schema: gqlMainSchema, + source: new Source(source), + contextValue: buildContext(accountId), + variableValues, + }) + if (result.errors) { + return { errors: result.errors.map((error) => ({ message: error.message })) } + } + return result.data ?? {} +} + +// ============ User Creation ============ + +/** + * Create a test user with the given account level and USDT wallet. + * Persists to local MongoDB (not Bridge sandbox). + */ +export async function createBridgeSandboxUser( + level: AccountLevel = AccountLevel.One, +): Promise { + const phone = randomPhone() + const kratosUserId = await AuthWithPhonePasswordlessService().createIdentityNoSession({ + phone, + }) + if (kratosUserId instanceof Error) throw kratosUserId + + // Create Kratos user + const user = await UsersRepository().update({ + id: kratosUserId, + deviceTokens: [`token-${kratosUserId}`] as DeviceToken[], + phone, + }) + if (user instanceof Error) throw user + + // Create account + let account = await AccountsRepository().findByUserId(kratosUserId) + + if (account instanceof CouldNotFindAccountFromKratosIdError) { + account = await createAccountWithPhoneIdentifier({ + newAccountInfo: { phone, kratosUserId }, + config: { + ...getDefaultAccountsConfig(), + initialLevel: level, + }, + }) + if (account instanceof Error) throw account + + // Add USDT wallet for Bridge flows + const usdtWallet = await addWalletIfNonexistent({ + currency: WalletCurrency.Usdt, + accountId: account.id, + type: WalletType.Checking, + }) + if (usdtWallet instanceof Error) throw usdtWallet + + // Set account level directly (createAccountWithPhoneIdentifier may not enforce initialLevel) + await AccountModel.updateOne({ _id: account.id }, { $set: { level } }) + } + + if (account instanceof Error) throw account + + // Get the USDT wallet + const walletsResult = await WalletsRepository().listByAccountId(account.id) + if (walletsResult instanceof RepositoryError) throw walletsResult + const usdtWallet = walletsResult.find( + (wallet) => + wallet.currency === WalletCurrency.Usdt && wallet.type === WalletType.Checking, + ) + if (!usdtWallet) throw new Error("No USDT wallet created for sandbox user") + + return { + accountId: account.id, + walletId: usdtWallet.id, + level, + } +} + +// ============ Bridge Mutation Wrappers ============ + +const BRIDGE_INITIATE_KYC = ` + mutation BridgeInitiateKyc($input: BridgeInitiateKycInput!) { + bridgeInitiateKyc(input: $input) { + errors { message } + kycLink { kycLink tosLink } + } + } +` + +const BRIDGE_CREATE_VIRTUAL_ACCOUNT = ` + mutation BridgeCreateVirtualAccount { + bridgeCreateVirtualAccount { + errors { message } + virtualAccount { id bankName routingNumber accountNumber accountNumberLast4 pending message kycLink tosLink } + } + } +` + +const BRIDGE_ADD_EXTERNAL_ACCOUNT = ` + mutation BridgeAddExternalAccount { + bridgeAddExternalAccount { + errors { message } + externalAccount { linkUrl expiresAt } + } + } +` + +const BRIDGE_REQUEST_WITHDRAWAL = ` + mutation BridgeRequestWithdrawal($input: BridgeRequestWithdrawalInput!) { + bridgeRequestWithdrawal(input: $input) { + errors { message } + withdrawal { id amount currency status failureReason createdAt } + } + } +` + +/** + * Initiate Bridge KYC for a user. + * Returns { errors, kycLink: { kycLink, tosLink } | null } + */ +export async function initiateKyc( + accountId: string, + email: string, +): Promise { + const data = (await execQuery(BRIDGE_INITIATE_KYC, accountId, { + input: { email }, + })) as { bridgeInitiateKyc?: KycInitiationResult } + return data?.bridgeInitiateKyc ?? { errors: [{ message: "No data returned" }] } +} + +/** + * Create a virtual account for a user. + * Requires KYC to be completed first. + */ +export async function createVirtualAccount( + accountId: string, +): Promise { + const data = (await execQuery(BRIDGE_CREATE_VIRTUAL_ACCOUNT, accountId)) as { + bridgeCreateVirtualAccount?: VirtualAccountResult + } + return data?.bridgeCreateVirtualAccount ?? { errors: [{ message: "No data returned" }] } +} + +/** + * Add an external account (Plaid). + * Requires KYC + virtual account to be completed first. + */ +export async function addExternalAccount( + accountId: string, +): Promise { + const data = (await execQuery(BRIDGE_ADD_EXTERNAL_ACCOUNT, accountId)) as { + bridgeAddExternalAccount?: ExternalAccountResult + } + return data?.bridgeAddExternalAccount ?? { errors: [{ message: "No data returned" }] } +} + +/** + * Initiate a withdrawal. + */ +export async function initiateWithdrawal( + accountId: string, + input: { amount: string; externalAccountId: string }, +): Promise { + const data = (await execQuery(BRIDGE_REQUEST_WITHDRAWAL, accountId, { + input, + })) as { bridgeRequestWithdrawal?: WithdrawalResult } + return data?.bridgeRequestWithdrawal ?? { errors: [{ message: "No data returned" }] } +} + +// ============ Webhook Injection ============ + +/** + * Inject a KYC webhook payload directly into the Express route handler. + * Tests the same handler code that runs in production. + */ +export async function injectKycWebhook(payload: { + event_id: string + event_object: { customer_id: string; kyc_status: string } +}): Promise { + const { req, res } = createReqRes({ body: payload }) + await kycHandler( + req as Parameters[0], + res as Parameters[1], + ) + return { status: res.statusCode, body: res._body } +} + +/** + * Inject an external account webhook payload directly into the Express route handler. + * Simulates Bridge sending an external_account.created event after Plaid linking. + */ +export async function injectExternalAccountWebhook(payload: { + event_id: string + event_object: { + id: string + customer_id: string + bank_name?: string + last_4?: string + active?: boolean + } +}): Promise { + const { req, res } = createReqRes({ body: payload }) + await externalAccountHandler( + req as Parameters[0], + res as Parameters[1], + ) + return { status: res.statusCode, body: res._body } +} + +/** + * Inject a deposit webhook payload directly into the Express route handler. + * Simulates Bridge sending a transfer state-transition event. + */ +export async function injectDepositWebhook(payload: { + event_id: string + event_object: { + id: string + state: string + amount: string + currency: string + on_behalf_of: string + receipt?: { + initial_amount?: string + subtotal_amount?: string + final_amount?: string + developer_fee?: string + destination_tx_hash?: string + } + } +}): Promise { + const { req, res } = createReqRes({ body: payload }) + await depositHandler( + req as Parameters[0], + res as Parameters[1], + ) + return { status: res.statusCode, body: res._body } +} + +// ============ ERPNext Verification ============ + +/** + * Query ERPNext for a matching audit row. + * Silently returns null when ERPNEXT_URL is not set. + */ +export async function verifyErpnextAuditRow( + docType: string, + referenceId: string, +): Promise | null> { + if (!process.env.ERPNEXT_URL) { + return null + } + try { + const response = await fetch( + `${process.env.ERPNEXT_URL}/api/resource/${docType}?filters=${JSON.stringify([["reference_id", "=", referenceId]])}`, + { + headers: { + Authorization: `token ${process.env.ERPNEXT_API_KEY}:${process.env.ERPNEXT_API_SECRET}`, + }, + }, + ) + if (!response.ok) return null + const json = await response.json() + return json.data?.[0] || null + } catch { + return null + } +} + +// ============ Deposit Log Lookup ============ + +/** + * Find a deposit log by event ID directly from MongoDB. + */ +export async function findDepositLogByEventId( + eventId: string, +): Promise | null> { + const doc = await BridgeDeposits.findOne({ eventId }).lean().exec() + return (doc as Record) ?? null +} + +// ============ Account Lookup ============ + +export async function getAccountById(accountId: string) { + const account = await AccountsRepo().findById(accountId as AccountId) + if (account instanceof Error) throw account + return account +} diff --git a/test/flash/bridge-sandbox-e2e/helpers/http-utils.ts b/test/flash/bridge-sandbox-e2e/helpers/http-utils.ts new file mode 100644 index 000000000..a5d9942ef --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/helpers/http-utils.ts @@ -0,0 +1,68 @@ +/** + * Create mock Express req/res objects for webhook handler injection. + */ + +import { EventEmitter } from "events" + +interface MockRequestOptions { + body?: Record + headers?: Record + method?: string + path?: string +} + +class MockResponse extends EventEmitter { + public statusCode: number = 200 + public _body: unknown = null + public _headers: Record = {} + public _ended: boolean = false + + status(code: number): this { + this.statusCode = code + return this + } + + json(data: unknown): this { + this._body = data + this._ended = true + this.emit("finish") + return this + } + + send(data: unknown): this { + this._body = data + this._ended = true + this.emit("finish") + return this + } + + setHeader(name: string, value: string): this { + this._headers[name] = value + return this + } + + end(): this { + this._ended = true + this.emit("finish") + return this + } +} + +export function createReqRes(options: MockRequestOptions = {}): { + req: Record + res: MockResponse +} { + const req: Record = { + body: options.body || {}, + headers: options.headers || { "content-type": "application/json" }, + method: options.method || "POST", + path: options.path || "/", + ip: "127.0.0.1", + query: {}, + params: {}, + } + + const res = new MockResponse() + + return { req, res } +} diff --git a/test/flash/bridge-sandbox-e2e/jest.config.js b/test/flash/bridge-sandbox-e2e/jest.config.js new file mode 100644 index 000000000..021f2d501 --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/jest.config.js @@ -0,0 +1,27 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const swcConfig = require("../../swc-config.json") + +module.exports = { + moduleFileExtensions: ["js", "json", "ts", "cjs", "mjs"], + rootDir: "../../../", + roots: ["/test/flash/bridge-sandbox-e2e"], + transform: { + "^.+\\.(t|j)sx?$": ["@swc/jest", swcConfig], + }, + testRegex: ".*\\.spec\\.ts$", + setupFilesAfterEnv: ["/test/flash/bridge-sandbox-e2e/jest.setup.ts"], + testEnvironment: "node", + forceExit: true, + moduleNameMapper: { + "^@config$": ["src/config/index"], + "^@app$": ["src/app/index"], + "^@utils$": ["src/utils/index"], + "^@core/(.*)$": ["src/core/$1"], + "^@app/(.*)$": ["src/app/$1"], + "^@domain/(.*)$": ["src/domain/$1"], + "^@services/(.*)$": ["src/services/$1"], + "^@servers/(.*)$": ["src/servers/$1"], + "^@graphql/(.*)$": ["src/graphql/$1"], + "^test/(.*)$": ["test/$1"], + }, +} diff --git a/test/flash/bridge-sandbox-e2e/jest.setup.ts b/test/flash/bridge-sandbox-e2e/jest.setup.ts new file mode 100644 index 000000000..c4bbfe2f2 --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/jest.setup.ts @@ -0,0 +1,85 @@ +/** + * Bridge Sandbox E2E Setup + * + * This suite requires: + * - RUN_BRIDGE_SANDBOX_E2E=true in environment + * - A running backend connected to Bridge sandbox + * - BridgeService.checkAccountLevel() allowing level >= 1 + * - IBEX_ENVIRONMENT=sandbox (safety guard) + */ + +// Must mock yargs BEFORE any config imports so yaml.ts gets a valid --configPath +jest.mock("yargs", () => { + const defaultOverridesPath = `${process.env.CONFIG_PATH ?? `${process.env.HOME}/.config/flash`}/dev-overrides.yaml` + const overridePath = process.env.BRIDGE_SANDBOX_CONFIG_PATH ?? defaultOverridesPath + const yargsMock = { + option: jest.fn().mockReturnThis(), + argv: { + configPath: [ + "./dev/config/base-config.yaml", + overridePath, + "./test/flash/bridge-sandbox-e2e/config-overrides.yaml", + ], + }, + } + return jest.fn(() => yargsMock) +}) + +jest.mock("@services/notifications/firebase", () => ({ + __esModule: true, + default: { + isDeviceTokenValid: jest.fn(async () => true), + subscribeToTopics: jest.fn(async () => undefined), + }, + messaging: null, +})) + +import { setupMongoConnection } from "@services/mongodb" +import { disconnectAll } from "@services/redis" + +import { preflightServiceLevelGuard } from "./preflight" + +let mongoose: Awaited> | undefined + +beforeAll(async () => { + // === Guard: Must be explicitly opted in === + if (!process.env.RUN_BRIDGE_SANDBOX_E2E) { + throw new Error( + "Bridge sandbox E2E skipped. Set RUN_BRIDGE_SANDBOX_E2E=true in env to run.", + ) + } + + // === Guard: Must be pointed at sandbox, not production === + // Set via: export IBEX_ENVIRONMENT=sandbox (or add to .env) + if (process.env.IBEX_ENVIRONMENT !== "sandbox") { + throw new Error( + "IBEX_ENVIRONMENT must be 'sandbox' for Bridge sandbox E2E tests.\n" + + " Run: export IBEX_ENVIRONMENT=sandbox\n" + + " Or add to .env: export IBEX_ENVIRONMENT=sandbox", + ) + } + + // === Connect MongoDB for test user creation === + try { + mongoose = await setupMongoConnection(true) + } catch (err) { + throw new Error( + `MongoDB connection failed: ${err instanceof Error ? err.message : err}`, + ) + } + + // === Preflight: Verify service-level guard allows level >= 1 === + const preflightOk = preflightServiceLevelGuard() + if (!preflightOk) { + throw new Error("Preflight failed — aborting suite.") + } +}) + +afterAll(async () => { + disconnectAll() + if (mongoose) { + await mongoose.connection.close() + } +}) + +jest.setTimeout(Number(process.env.JEST_TIMEOUT) || 120000) diff --git a/test/flash/bridge-sandbox-e2e/kyc-virtual-account.spec.ts b/test/flash/bridge-sandbox-e2e/kyc-virtual-account.spec.ts new file mode 100644 index 000000000..1baffb0e5 --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/kyc-virtual-account.spec.ts @@ -0,0 +1,103 @@ +/** + * Bridge Sandbox E2E — KYC + Virtual Account Flow + * + * Tests the complete KYC initiation → webhook processing → virtual account creation flow. + * + * Verified return shapes (from source audit): + * - bridgeInitiateKyc returns { errors, kycLink: { kycLink: string!, tosLink: string! } } + * - bridgeCreateVirtualAccount returns { errors, virtualAccount: { id, bankName, ... } } + * - No ERPNext writer exists for BridgeVirtualAccount (only BridgeTransferRequest exists) + */ + +import { + createBridgeSandboxUser, + initiateKyc, + createVirtualAccount, + injectKycWebhook, + getAccountById, + BridgeTestUser, +} from "./helpers" + +const VIRTUAL_ACCOUNT_TESTS = + process.env.BRIDGE_SANDBOX_VIRTUAL_ACCOUNT_CONFIRMED === "true" + +describe("Bridge KYC → Virtual Account", () => { + let user: BridgeTestUser + + beforeAll(async () => { + user = await createBridgeSandboxUser(1) + }) + + describe("KYC Initiation", () => { + it("initiates KYC and returns a KYC link URL and TOS link", async () => { + const result = await initiateKyc( + user.accountId, + `sandbox-${user.accountId.slice(-8)}@test.flashapp.me`, + ) + + expect(result.errors).toBeDefined() + expect(result.errors).toHaveLength(0) + expect(result.kycLink).toBeDefined() + expect(result.kycLink!.kycLink).toBeTruthy() + expect(result.kycLink!.kycLink).toMatch(/^https:\/\//) + expect(result.kycLink!.tosLink).toBeTruthy() + expect(result.kycLink!.tosLink).toMatch(/^https:\/\//) + }) + }) + + describe("KYC Webhook Processing", () => { + beforeAll(async () => { + // Initiate KYC first to create the Bridge customer + const kycResult = await initiateKyc( + user.accountId, + `webhook-${user.accountId.slice(-8)}-${Date.now()}@test.flashapp.me`, + ) + if (kycResult.errors?.length) { + throw new Error(`KYC initiation failed: ${kycResult.errors[0].message}`) + } + }) + + it("processes a KYC-approved webhook and marks account as approved", async () => { + const account = await getAccountById(user.accountId) + const webhookCustomerId = account.bridgeCustomerId + if (!webhookCustomerId) { + throw new Error("KYC initiation did not persist a Bridge customer ID") + } + + const response = await injectKycWebhook({ + event_id: `test-kyc-approved-${Date.now()}`, + event_object: { + customer_id: webhookCustomerId, + kyc_status: "approved", + }, + }) + + // The handler returns 200 for any valid webhook payload structure + expect(response.status).toBe(200) + }) + }) + ;(VIRTUAL_ACCOUNT_TESTS ? describe : describe.skip)("Virtual Account Creation", () => { + it("creates a virtual account after KYC approval", async () => { + const result = await createVirtualAccount(user.accountId) + + expect(result.errors).toBeDefined() + expect(result.errors).toHaveLength(0) + expect(result.virtualAccount).toBeDefined() + expect(result.virtualAccount!.id).toBeTruthy() + // Virtual account should include bank details + expect(result.virtualAccount!.bankName).toBeDefined() + expect(result.virtualAccount!.routingNumber).toBeDefined() + expect(result.virtualAccount!.accountNumberLast4).toBeDefined() + }) + + it("virtual account is idempotent — calling twice returns same result", async () => { + const result1 = await createVirtualAccount(user.accountId) + const result2 = await createVirtualAccount(user.accountId) + + expect(result1.errors).toHaveLength(0) + expect(result2.errors).toHaveLength(0) + // Both calls should succeed (idempotent) — the second may return existing VA + expect(result1.virtualAccount?.id || result2.virtualAccount?.id).toBeTruthy() + }) + }) +}) diff --git a/test/flash/bridge-sandbox-e2e/ln-parity.spec.ts b/test/flash/bridge-sandbox-e2e/ln-parity.spec.ts new file mode 100644 index 000000000..0e7a356f8 --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/ln-parity.spec.ts @@ -0,0 +1,59 @@ +/** + * Bridge Sandbox E2E — ETH-USDT LN Parity Smoke + * + * Verifies that Lightning payments still route correctly after USDT + * on-chain deposits are handled by Bridge. + * + * Key assertions (when infrastructure is available): + * - LN USD invoices can be created on a Bridge-capable account + * - LN USD invoice amounts convert correctly from USD → USDT parity + * (addressing CurrencyPrecisionAnalysis bug #282 / flash-mobile #555) + * - On-chain USDT deposits through Bridge don't affect LN routing + * + * ⚠️ GUARDED: Requires LN payment infrastructure + funded wallet in + * the sandbox environment. Only runs when LN_PARITY_TESTS=true. + * + * This spec is a placeholder for validation that should be run manually + * after the sandbox environment is seeded with: + * 1. A user with completed KYC + virtual account (Bridge mode) + * 2. A funded USDT wallet (via sandbox deposit) + * 3. Working LNURL-USD invoice creation + */ + +const ACCOUNT_ID = `acct_lnparity_test_${Date.now()}` +const LN_PARITY_TESTS = process.env.LN_PARITY_TESTS === "true" + +;(LN_PARITY_TESTS ? describe : describe.skip)("ETH-USDT LN Parity", () => { + describe("Lightning invoice creation (USD)", () => { + it("creates a LN USD invoice for a Bridge-capable account", async () => { + const { execQuery } = await import("./helpers") + + const source = ` + mutation LnUsdInvoiceCreate($input: LnUsdInvoiceCreateInput!) { + lnUsdInvoiceCreate(input: $input) { + errors { message } + invoice { paymentRequest paymentHash } + } + } + ` + + const response = await execQuery(source, ACCOUNT_ID, { + input: { amount: 1000 }, // 1000 millisatoshis = $0.10 USD-ish + }) + + expect(response.lnUsdInvoiceCreate).toBeDefined() + + const errors = response.lnUsdInvoiceCreate?.errors + if (errors?.length) { + // Log known missing-infrastructure errors without failing + console.warn("LN invoice creation returned errors:", errors) + return + } + + const invoice = response.lnUsdInvoiceCreate.invoice + expect(invoice.paymentRequest).toBeTruthy() + expect(invoice.paymentRequest).toMatch(/^lnb\d+/) + expect(invoice.paymentHash).toBeTruthy() + }) + }) +}) diff --git a/test/flash/bridge-sandbox-e2e/preflight.ts b/test/flash/bridge-sandbox-e2e/preflight.ts new file mode 100644 index 000000000..6810ed100 --- /dev/null +++ b/test/flash/bridge-sandbox-e2e/preflight.ts @@ -0,0 +1,64 @@ +/** + * Preflight Checks for Bridge Sandbox E2E Suite + * + * IMPORTANT: BridgeService.checkAccountLevel() is a private module-level + * function — it is NOT exported and cannot be imported by tests. + * + * This preflight uses source-code analysis to verify the guard condition. + * It checks the source file for `account.level < N` within the + * `checkAccountLevel` function and validates that N <= 1. + */ + +import fs from "fs" +import path from "path" + +/** + * Verify the service-level guard condition in BridgeService.checkAccountLevel(). + * + * Checks the source file for the account level comparison. + * The guard in src/services/bridge/index.ts should be `account.level < 1` + * so that Level 1 accounts can access Bridge operations. + * + * @returns true if the guard is correctly configured (level >= 1 allowed) + */ +export function preflightServiceLevelGuard(): boolean { + const servicePath = path.resolve(__dirname, "../../../src/services/bridge/index.ts") + + let content: string + try { + content = fs.readFileSync(servicePath, "utf-8") + } catch { + console.error("PREFLIGHT FAILED: Could not read BridgeService source at", servicePath) + return false + } + + // Extract the guard comparison value from checkAccountLevel. + // Matches: `account.level < N` anywhere inside the function body. + const funcMatch = content.match( + /const\s+checkAccountLevel[\s\S]*?account\.level\s*<(\s*\d+)/, + ) + + if (!funcMatch) { + console.error( + "PREFLIGHT FAILED: Could not detect service-level guard pattern in BridgeService.", + ) + return false + } + + const guardLevel = parseInt(funcMatch[1], 10) + + if (guardLevel <= 1) { + // Level 0 only is blocked — level 1+ is allowed. Correct configuration. + return true + } + + console.error( + `PREFLIGHT FAILED: BridgeService.checkAccountLevel() blocks level < ${guardLevel}, ` + + `but the e2e suite requires level >= 1 to pass through.\n` + + `Fix required in src/services/bridge/index.ts:\n` + + ` if (account.level < ${guardLevel}) -> if (account.level < 1)\n` + + `See test/flash/bridge-sandbox-e2e/README.md for setup details.`, + ) + + return false +} diff --git a/test/flash/unit/dev/setup-bridge-webhooks.spec.ts b/test/flash/unit/dev/setup-bridge-webhooks.spec.ts new file mode 100644 index 000000000..4668525a3 --- /dev/null +++ b/test/flash/unit/dev/setup-bridge-webhooks.spec.ts @@ -0,0 +1,126 @@ +import { + buildWebhookDefinitions, + extractNgrokHttpsUrl, + mergeDevOverrides, + reconcileBridgeWebhooks, +} from "../../../../dev/setup-bridge-webhooks" + +describe("setup-bridge-webhooks", () => { + it("builds one Bridge webhook definition per local route", () => { + const definitions = buildWebhookDefinitions("https://flash-dev.ngrok-free.app") + + expect(definitions).toEqual({ + kyc: { + url: "https://flash-dev.ngrok-free.app/kyc", + eventCategories: ["customer", "kyc_link"], + }, + deposit: { + url: "https://flash-dev.ngrok-free.app/deposit", + eventCategories: ["virtual_account.activity", "bridge_wallet.activity"], + }, + transfer: { + url: "https://flash-dev.ngrok-free.app/transfer", + eventCategories: ["transfer"], + }, + external_account: { + url: "https://flash-dev.ngrok-free.app/external-account", + eventCategories: ["external_account"], + }, + }) + }) + + it("extracts the HTTPS ngrok public URL", () => { + const url = extractNgrokHttpsUrl({ + tunnels: [ + { proto: "http", public_url: "http://example.ngrok-free.app" }, + { proto: "https", public_url: "https://example.ngrok-free.app" }, + ], + }) + + expect(url).toBe("https://example.ngrok-free.app") + }) + + it("merges Bridge secrets and webhook public keys into existing dev overrides", () => { + const merged = mergeDevOverrides( + { + ibex: { environment: "sandbox" }, + bridge: { webhook: { replaySecret: "keep-me" } }, + }, + { + apiKey: "sk-test-123", + baseUrl: "https://api.sandbox.bridge.xyz/v0", + webhookBaseUrl: "https://example.ngrok-free.app", + publicKeys: { + kyc: "kyc-pem", + deposit: "deposit-pem", + transfer: "transfer-pem", + external_account: "external-account-pem", + }, + }, + ) + + expect(merged).toEqual({ + ibex: { environment: "sandbox" }, + bridge: { + apiKey: "sk-test-123", + baseUrl: "https://api.sandbox.bridge.xyz/v0", + webhook: { + replaySecret: "keep-me", + uri: "https://example.ngrok-free.app", + publicKeys: { + kyc: "kyc-pem", + deposit: "deposit-pem", + transfer: "transfer-pem", + external_account: "external-account-pem", + }, + }, + }, + }) + }) + + it("deletes old webhooks, creates new disabled webhooks, then enables them", async () => { + const calls: string[] = [] + const definitions = buildWebhookDefinitions("https://fresh.ngrok-free.app") + const api = { + listWebhooks: jest.fn().mockResolvedValue([ + { id: "wep_old_1", status: "active", url: "https://old.example/kyc" }, + { id: "wep_old_2", status: "disabled", url: "https://old.example/deposit" }, + { id: "wep_deleted", status: "deleted", url: "https://old.example/deleted" }, + ]), + deleteWebhook: jest.fn(async (id: string) => { + calls.push(`delete:${id}`) + }), + createWebhook: jest.fn(async ({ key }: { key: string }) => { + calls.push(`create:${key}`) + return { + id: `wep_${key}`, + public_key: `${key}-public-key`, + } + }), + enableWebhook: jest.fn(async (id: string) => { + calls.push(`enable:${id}`) + }), + } + + const result = await reconcileBridgeWebhooks(api, definitions) + + expect(calls).toEqual([ + "delete:wep_old_1", + "delete:wep_old_2", + "create:kyc", + "enable:wep_kyc", + "create:deposit", + "enable:wep_deposit", + "create:transfer", + "enable:wep_transfer", + "create:external_account", + "enable:wep_external_account", + ]) + expect(result.publicKeys).toEqual({ + kyc: "kyc-public-key", + deposit: "deposit-public-key", + transfer: "transfer-public-key", + external_account: "external_account-public-key", + }) + }) +}) diff --git a/test/flash/unit/services/frappe/models/BridgeTransferRequest.spec.ts b/test/flash/unit/services/frappe/models/BridgeTransferRequest.spec.ts index 37b3df425..a27a5c5ff 100644 --- a/test/flash/unit/services/frappe/models/BridgeTransferRequest.spec.ts +++ b/test/flash/unit/services/frappe/models/BridgeTransferRequest.spec.ts @@ -62,4 +62,23 @@ describe("BridgeTransferRequest", () => { expect(request.toErpnext().source_systems_seen).toBe("ibex_crypto_receive") }) + + it("serializes datetimes in the format accepted by Frappe", () => { + const request = new BridgeTransferRequest({ + requestId: "tr_123", + transactionType: BridgeTransferRequestTransactionType.Topup, + status: BridgeTransferRequestStatus.Settled, + amount: "2.500000", + currency: "USDT", + firstSeenAt: "2026-06-08T20:30:01.373Z", + lastSeenAt: "2026-06-08T20:31:02.540Z", + }) + + expect(request.toErpnext()).toEqual( + expect.objectContaining({ + first_seen_at: "2026-06-08 20:30:01", + last_seen_at: "2026-06-08 20:31:02", + }), + ) + }) }) From c06711965d16a665da33a73b42f29d7b0f52533f Mon Sep 17 00:00:00 2001 From: Patoo Date: Thu, 11 Jun 2026 12:43:24 -0400 Subject: [PATCH 35/43] chore(bridge): add refreshed ERPNext dev backup (#400) --- .../20260611_073822-frontend-database.sql.gz | Bin 0 -> 858304 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dev/erpnext/backups/20260611_073822-frontend-database.sql.gz diff --git a/dev/erpnext/backups/20260611_073822-frontend-database.sql.gz b/dev/erpnext/backups/20260611_073822-frontend-database.sql.gz new file mode 100644 index 0000000000000000000000000000000000000000..b2e3827e3fb5d4497d5e73926f2f52a7b9062892 GIT binary patch literal 858304 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%0DF);_~cf(XRp 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
%LtV6hm4)AxoCmk=5L?r$u*_gaVBjrH)lt`wd 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 z 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%0AYIPC9Y2)@z6;;}*qSuWS& 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