Files
bridge/app/server/package.json
T
zeekay 29b7a31655 feat(app/server): real Fireblocks cosigner implementation + 10 tests (issue #386)
Replaces the runFireblocks STUB from v1.1.12 with a real RAW-sign
implementation against [email protected].0.

Flow:
  1. `new FireblocksSDK(secretPem, intent.api_key, intent.api_host)`
  2. `createTransaction({ operation: RAW, source: { type: VAULT_ACCOUNT,
     id: intent.vault_account_id ?? '0' }, extraParameters: {
     rawMessageData: { messages: [{ content: opts.txHash }] }, note:
     `lux-bridge cosign swap=${opts.swapId}` })`
  3. Poll `getTransactionById` every 1.5s, partition Fireblocks's wide
     status enum into three buckets:
       complete:  COMPLETED, BROADCASTING, CONFIRMING → approved
       reject:    REJECTED, CANCELLED, BLOCKED        → rejected
       transient: FAILED, TIMEOUT                     → failed
     Unknown statuses (SUBMITTED, QUEUED, PENDING_*, etc.) keep polling
     until terminal or the timeout ceiling fires.
  4. Ceiling: 60s default, overridable via
     FIREBLOCKS_COSIGNER_TIMEOUT_MS. Read on each call so config picks
     up without a restart.

Edge cases covered by tests:
  - happy: COMPLETED with signature → approved
  - happy: BROADCASTING/CONFIRMING (sig already attested) → approved
  - rejected: REJECTED with subStatus → rejected, reason surfaces both
  - rejected: CANCELLED, BLOCKED → rejected
  - failed: FAILED + INSUFFICIENT_FUNDS → failed
  - failed: COMPLETED but signedMessages empty (Fireblocks bug guard)
  - failed: createTransaction throws (transport / config error)
  - failed: PENDING_* indefinitely → timeout failure with externalId
  - failed: env-var fallback unset → clear KMS-TODO error

Env-var fallback path also hardened: fetchCosignerSecret now normalises
public IDs (hyphens / dots → underscores) before checking
`process.env.FIREBLOCKS_COSIGNER_PEM__<NORMALISED>` so a tenant id
like `pub-key-id` maps to `FIREBLOCKS_COSIGNER_PEM__PUB_KEY_ID`.

Tests:
  - SDK:    58/58 (unchanged)
  - Server: 26/26 — 16 validator + 10 Fireblocks (new)

What's still STUB (next-dev work on #386):
  - runUtila — Utila Connect-RPC flow needs the v1alpha2
    InitiateTransaction subtype shape worked out per tenant
  - Prisma persistence — Swap.metadata JSON or related CosignerStep model
  - State-machine gating — wire dispatchCosigners() after native MPC sign
2026-05-22 00:33:15 -07:00

216 lines
8.3 KiB
JSON

{
"name": "@luxbridge/server",
"version": "1.0.0",
"private": true,
"description": "",
"main": "src/server.ts",
"_moduleAliases": {
"@": "dist"
},
"scripts": {
"dev": "nodemon",
"db": "node dist/db.js",
"rm": "node dist/delete.js",
"nodes": "node dist/nodes.js",
"tc": "tsc",
"typecheck": "tsc",
"test": "vitest run",
"clean": "rm -rf ./node_modules",
"prisma:gen": "pnpm exec prisma generate",
"build": "pnpm exec prisma generate && tsc && tsc-alias",
"start": "node dist/server.js",
"lint": "eslint src/**/*.ts",
"format": "eslint src/**/*.ts --fix",
"update-db": "kubectl exec -it $(kubectl get pod -l app=bridge-server -o jsonpath='{.items[0].metadata.name}') -- pnpm run db",
"colima:start": "colima start --arch aarch64,x86_64",
"docker:build": "cd ../.. && docker buildx build --load --platform linux/amd64 -t ghcr.io/luxfi/bridge-server:latest -f app/server/docker/Dockerfile .",
"docker:login": "docker login --username $GH_USER --password $GH_TOKEN ghcr.io",
"docker:push": "docker push ghcr.io/luxfi/bridge/server:latest",
"k8s:apply": "kubectl apply -f k8s/bridge-deployment.yaml && kubectl apply -f k8s/bridge-service.yaml",
"k8s:logs": "kubectl logs -l app=bridge-server -f",
"k8s:scale": "kubectl scale deployment bridge-server --replicas=$REPLICAS",
"k8s:create-secret": "kubectl create secret generic bridge-server-secret --from-literal=POSTGRESQL_URL=\"$POSTGRESQL_URL\" --dry-run=client -o yaml | kubectl apply -f -",
"k8s:restart": "kubectl rollout restart deployment bridge-server",
"k8s:status": "kubectl get deployments,service,pods -l app=bridge-server -o wide",
"k8s:stop": "kubectl delete deployment bridge-server || true && kubectl delete service bridge-service || true"
},
"prisma": {
"schema": "prisma/schema.prisma"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.7",
"@types/morgan": "^1.9.9",
"@types/multicoin-address-validator": "^0.5.3",
"@types/node": "^20.17.1",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.2.0",
"@typescript-eslint/parser": "^8.2.0",
"nodemon": "^3.1.4",
"prettier": "^3.3.3",
"ts-node": "^10.9.2",
"tsc-alias": "^1.8.10",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.5.4",
"vitest": "^2.1.0"
},
"dependencies": {
"@luxfi/core": "workspace:*",
"@luxfi/utila": "^3.0.0",
"@noble/ed25519": "^2.3.0",
"@prisma/client": "^6.3.1",
"@solana/addresses": "2.0.0",
"axios": "^1.7.4",
"consul": "^2.0.1",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"ethers": "^6.13.4",
"express": "^4.21.1",
"express-rate-limit": "^8.3.1",
"express-validator": "^7.2.0",
"fireblocks-sdk": "^5.39.0",
"helmet": "^8.0.0",
"jsonwebtoken": "^9.0.2",
"module-alias": "^2.2.3",
"morgan": "^1.10.0",
"multicoin-address-validator": "^0.5.22",
"nats": "^2.29.3",
"prisma": "^6.3.1",
"uuid": "^9.0.0",
"web3": "^4.16.0",
"winston": "^3.17.0"
},
"pnpm": {
"overrides": {
"color-string@<1.5.5": ">=1.5.5",
"tar@<6.2.1": ">=6.2.1",
"axios@>=0.8.1 <0.28.0": ">=0.28.0",
"tough-cookie@<4.1.3": ">=4.1.3",
"ws@>=8.0.0 <8.17.1": ">=8.17.1",
"ws@>=7.0.0 <7.5.10": ">=7.5.10",
"ws@>=2.1.0 <5.2.4": ">=5.2.4",
"zod@<=3.22.2": ">=3.22.3",
"next@>=14.0.0 <14.2.21": ">=14.2.21",
"undici@>=6.0.0 <6.21.1": ">=6.21.1",
"undici@>=4.5.0 <5.28.5": ">=5.28.5",
"esbuild@<=0.24.2": ">=0.25.0",
"vite@>=5.0.0 <=5.4.11": ">=5.4.12",
"elliptic@<=6.6.0": ">=6.6.1",
"vite@>=5.0.0 <5.4.15": ">=5.4.15",
"@remix-run/express@>=2.11.1 <2.16.3": ">=2.16.3",
"estree-util-value-to-estree@<3.3.3": ">=3.3.3",
"vite@>=5.0.0 <5.4.18": ">=5.4.18",
"cookie@<0.7.0": ">=0.7.0",
"@babel/runtime@<7.26.10": ">=7.26.10",
"@babel/helpers@<7.26.10": ">=7.26.10",
"vite@>=5.0.0 <=5.4.18": ">=5.4.19",
"base-x@<=3.0.10": ">=3.0.11",
"base-x@=4.0.0": ">=4.0.1",
"vite@>=5.0.0 <5.4.17": ">=5.4.17",
"image-size@>=1.1.0 <1.2.1": ">=1.2.1",
"brace-expansion@>=1.0.0 <=1.1.11": ">=1.1.12",
"brace-expansion@>=2.0.0 <=2.0.1": ">=2.0.2",
"next@>=13.0 <14.2.30": ">=14.2.30",
"pbkdf2@>=3.0.10 <=3.1.2": ">=3.1.3",
"@eslint/plugin-kit@<0.3.4": ">=0.3.4",
"on-headers@<1.1.0": ">=1.1.0",
"next@>=0.9.9 <14.2.31": ">=14.2.31",
"vite@<=5.4.19": ">=5.4.20",
"next@>=0.9.9 <14.2.32": ">=14.2.32",
"@metamask/sdk-communication-layer@>=0.16.0 <=0.33.0": ">=0.33.1",
"@metamask/sdk@>=0.16.0 <=0.33.0": ">=0.33.1",
"next@>=0.9.9 <14.2.24": ">=14.2.24",
"vite@>=5.2.6 <=5.4.20": ">=5.4.21",
"validator@<13.15.20": ">=13.15.20",
"tar-fs@>=2.0.0 <2.1.4": ">=2.1.4",
"tar-fs@>=3.0.0 <3.1.1": ">=3.1.1",
"sha.js@<=2.4.11": ">=2.4.12",
"tmp@<=0.2.3": ">=0.2.4",
"form-data@>=4.0.0 <4.0.4": ">=4.0.4",
"form-data@<2.5.4": ">=2.5.4",
"tar-fs@>=3.0.0 <3.0.9": ">=3.0.9",
"tar-fs@>=2.0.0 <2.1.3": ">=2.1.3",
"tar-fs@>=3.0.0 <3.0.7": ">=3.0.7",
"tar-fs@>=2.0.0 <2.1.2": ">=2.1.2",
"elliptic@>=4.0.0 <=6.5.6": ">=6.5.7",
"elliptic@>=2.0.0 <=6.5.6": ">=6.5.7",
"elliptic@>=5.2.1 <=6.5.6": ">=6.5.7",
"min-document@<=2.19.0": ">=2.19.1",
"glob@>=10.2.0 <10.5.0": ">=10.5.0",
"valibot@>=0.31.0 <1.2.0": ">=1.2.0",
"node-forge@<1.3.2": ">=1.3.2",
"axios@<0.30.0": ">=0.30.0",
"axios@>=1.0.0 <1.8.2": ">=1.8.2",
"elliptic@<6.6.0": ">=6.6.0",
"elliptic@<6.5.6": ">=6.5.6",
"jws@<3.2.3": ">=3.2.3",
"next@>=13.3.0 <14.2.34": ">=14.2.34",
"@remix-run/server-runtime@<=2.17.2": ">=2.17.3",
"@remix-run/router@<=1.23.1": ">=1.23.2",
"@remix-run/react@<2.17.3": ">=2.17.3",
"react-router@>=6.0.0 <6.30.2": ">=6.30.2",
"@remix-run/node@<=2.17.1": ">=2.17.2",
"@remix-run/react@>=1.15.0 <=2.17.0": ">=2.17.1",
"next@>=13.3.1-canary.0 <14.2.35": ">=14.2.35",
"h3@<=1.15.4": ">=1.15.5",
"axios@>=1.0.0 <1.12.0": ">=1.12.0",
"@coinbase/wallet-sdk@>=4.0.0-beta.0 <4.3.0": ">=4.3.0",
"lodash-es@>=4.0.0 <=4.17.22": ">=4.17.23",
"lodash@>=4.0.0 <=4.17.22": ">=4.17.23",
"undici@<6.23.0": ">=6.23.0",
"vite@>=5.0.0 <5.4.16": ">=5.4.16",
"next@>=10.0.0 <15.5.10": ">=15.5.10",
"next@>=13.0.0 <15.0.8": ">=15.0.8",
"tar@<7.5.7": ">=7.5.7",
"diff@>=4.0.0 <4.0.4": ">=4.0.4",
"diff@>=5.0.0 <5.2.2": ">=5.2.2",
"validator@<13.15.22": ">=13.15.22",
"js-yaml@<3.14.2": ">=3.14.2",
"js-yaml@>=4.0.0 <4.1.1": ">=4.1.1",
"semver@>=2.0.0-alpha <5.7.2": ">=5.7.2",
"webpack@>=5.49.0 <=5.104.0": ">=5.104.1",
"webpack@>=5.49.0 <5.104.0": ">=5.104.0",
"mdast-util-to-hast@>=13.0.0 <13.2.1": ">=13.2.1",
"undici@>=6.0.0 <6.21.2": ">=6.21.2",
"undici@<5.29.0": ">=5.29.0",
"qs@>=6.7.0 <=6.14.1": ">=6.14.2",
"pbkdf2@>=1.0.0 <=3.1.2": ">=3.1.3",
"axios@<=0.30.2": ">=0.30.3",
"axios@>=1.0.0 <=1.13.4": ">=1.13.5",
"tar@<=7.5.2": ">=7.5.3",
"tar@<7.5.8": ">=7.5.8",
"bn.js@>=5.0.0 <5.2.3": ">=5.2.3",
"bn.js@<4.12.3": ">=4.12.3",
"minimatch@<3.1.3": ">=3.1.3",
"minimatch@>=5.0.0 <5.1.7": ">=5.1.7",
"minimatch@>=9.0.0 <9.0.6": ">=9.0.6",
"rollup@>=4.0.0 <4.59.0": ">=4.59.0",
"rollup@<2.80.0": ">=2.80.0",
"minimatch@>=5.0.0 <5.1.8": ">=5.1.8",
"minimatch@>=9.0.0 <9.0.7": ">=9.0.7",
"minimatch@<3.1.4": ">=3.1.4",
"serialize-javascript@<=7.0.2": ">=7.0.3",
"next@>=14.0.0 <14.2.25": ">=14.2.25",
"ajv@<6.14.0": ">=6.14.0",
"ajv@>=7.0.0-alpha.0 <8.18.0": ">=8.18.0",
"qs@<6.14.1": ">=6.14.1",
"svgo@>=3.0.0 <3.3.3": ">=3.3.3",
"svgo@>=2.1.0 <2.8.1": ">=2.8.1",
"tar@<=7.5.9": ">=7.5.10",
"tar@<=7.5.10": ">=7.5.11",
"flatted@<3.4.0": ">=3.4.0",
"undici@>=6.0.0 <6.24.0": ">=6.24.0",
"undici@<6.24.0": ">=6.24.0",
"tar@<=7.5.3": ">=7.5.4",
"next@>=9.5.0 <15.5.13": ">=15.5.13",
"next@>=10.0.0 <16.1.7": ">=16.1.7",
"h3@<1.15.6": ">=1.15.6",
"socket.io-parser@>=4.0.0 <4.2.6": ">=4.2.6"
}
}
}