Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
98d4c4e
fix(#1): add configurable security middleware
SlavaSereb Jun 2, 2026
621a695
fix(#2): add fee parameter validation with bounds
SlavaSereb Jun 2, 2026
0435939
fix(#5): make protocol parameters configurable
SlavaSereb Jun 2, 2026
a584343
fix(#6): propagate fetchUtxos API errors instead of swallowing them
SlavaSereb Jun 2, 2026
e47651e
fix(#7): document process-local UTXO lock limitation
SlavaSereb Jun 2, 2026
73f90ee
fix(#8): add UTXO locking to staking and governance operations
SlavaSereb Jun 2, 2026
e708a8d
docs: add security and protocol parameters configuration to README
SlavaSereb Jun 2, 2026
74d887d
ci: add automatic CI workflow on push/PR to main
SlavaSereb Jun 2, 2026
193d508
fix(M-01): verify webhook signature before parsing JSON
SlavaSereb Jun 9, 2026
ba74331
fix(M-02): validate recipient address format and network
SlavaSereb Jun 9, 2026
3b32153
fix(M-04): encode lovelace and fee as BigInt in CBOR
SlavaSereb Jun 9, 2026
f24bf22
fix(M-06): exclude script-locked UTxOs from selection
SlavaSereb Jun 9, 2026
608dd50
fix(M-07): bound governanceActionId.index to uint16
SlavaSereb Jun 9, 2026
5f5510c
fix(M-08): validate DRep ID bech32 checksum and payload length
SlavaSereb Jun 9, 2026
189567b
refactor(M-02): extract validateRecipientAddress to a free helper
SlavaSereb Jun 9, 2026
f45f90c
test(M-01..M-08): add unit tests for medium-priority fixes
SlavaSereb Jun 9, 2026
d72ebe6
fix(M-09): drop internal info and service fields from error responses
SlavaSereb Jun 9, 2026
6db4e5c
fix(M-10): throw when fee convergence loop fails instead of returning…
SlavaSereb Jun 9, 2026
c703a23
fix(M-11): align DRep ID encode/decode/validate with CIP-129
SlavaSereb Jun 9, 2026
a73dfe5
fix(M-12): reject oversize transactions before signing
SlavaSereb Jun 9, 2026
29a5299
fix(M-13): do not leak raw exception messages on unhandled errors
SlavaSereb Jun 9, 2026
56d60ea
fix(M-14): follow pagination when aggregating vault history
SlavaSereb Jun 9, 2026
d670ba5
fix(M-15): reject delegation to retired or unknown pools
SlavaSereb Jun 9, 2026
b9b328a
fix(M-17): make Iagon base URL configurable
SlavaSereb Jun 9, 2026
945e5f4
fix(M-18): plug WASM handle leaks on the signing path
SlavaSereb Jun 9, 2026
69e58cb
fix(M-20): drop docker-compose references from README
SlavaSereb Jun 9, 2026
bf1abd5
fix(M-21): coalesce concurrent SDK creations per vault account
SlavaSereb Jun 9, 2026
1245e6f
ci: run unit tests on every PR and push to main
SlavaSereb Jun 9, 2026
11145d0
fix(H-1): surface Iagon submit error instead of swallowing it
SlavaSereb Jun 9, 2026
772f6cf
fix(H-2): only report "not registered" on Iagon 404, not on every error
SlavaSereb Jun 9, 2026
051afae
bump version
SlavaSereb Jun 9, 2026
3efcc80
change e2e to preprod
SlavaSereb Jun 9, 2026
bf864a3
ci: run e2e ADA transfer on preprod to match dispatch workflow default
SlavaSereb Jun 9, 2026
672e575
remove fee estimation for e2e ADA transfer
SlavaSereb Jun 9, 2026
ee7462c
docs(M-20): remove Docker Compose references and regenerate TypeDoc
tomer-shoham Jul 8, 2026
0ccfb8f
fix(M-10): rebuild tx body with converged fee so reported fee matches…
tomer-shoham Jul 8, 2026
69ae5dc
fix(M-15): gate pool retirement on status/retiring_epoch served by Iagon
tomer-shoham Jul 8, 2026
d11abb1
fix(C-01): fail fast on API key misconfiguration, warn when auth disa…
tomer-shoham Jul 8, 2026
c0c9e52
docs(M-19): estimate-only disclaimer on ADA fee estimation
tomer-shoham Jul 8, 2026
1ac7094
fix(M-06): apply spendability filter on all selection paths, log excl…
tomer-shoham Jul 8, 2026
c696e31
fix(M-16): log dropped non-BASE addresses; apply M-06 filter on conso…
tomer-shoham Jul 8, 2026
3c29e47
fix(M-18): free WASM handles on throw in buildDelegationCertificate
tomer-shoham Jul 8, 2026
092a675
docs(OC-1): record registration semantics of StakeAccountInfo.active
tomer-shoham Jul 8, 2026
f733438
ci(M-05): default test-staking workflow to preprod
tomer-shoham Jul 8, 2026
fd1635f
fix(security): exempt signature-authenticated webhook route from API …
tomer-shoham Jul 19, 2026
0509fc5
fix(security): support TRUST_PROXY so rate limiting works behind reve…
tomer-shoham Jul 19, 2026
caefae9
fix(security): disable CORS by default; serve wildcard origin without…
tomer-shoham Jul 19, 2026
ca4344c
fix(consolidation): preserve partial result when UTxO fetch fails mid…
tomer-shoham Jul 19, 2026
95b96ea
fix(staking): acquire UTxO locks atomically to close selection race
tomer-shoham Jul 19, 2026
5e08acf
fix(staking): validate pool info response so the retirement gate fail…
tomer-shoham Jul 19, 2026
7df2f77
fix(fees): free intermediate WASM handles in the fee-convergence loop
tomer-shoham Jul 19, 2026
24150bf
fix(config): document actual protocolParams scope; warn on cross-inst…
tomer-shoham Jul 19, 2026
e68bc65
fix(validation): name the unsupported Byron address format in the rej…
tomer-shoham Jul 19, 2026
ce361a7
fix(history): fail loudly on a failed page and honor hasMore in pagin…
tomer-shoham Jul 19, 2026
911bed0
fix(M-04): carry withdrawal and DRep deposit coins as BigInt in hand-…
tomer-shoham Jul 19, 2026
2d3aad1
fix(M-11): assert the exact CIP-129 header byte when decoding DRep IDs
tomer-shoham Jul 19, 2026
5d3b22e
docs: regenerate TypeDoc output
tomer-shoham Jul 19, 2026
4b3e834
fix(config): drop explicitly-undefined fields before merging protocol…
tomer-shoham Jul 19, 2026
d67c20d
fix(security): exact-match /health, boundary-match /docs and /api-doc…
tomer-shoham Jul 19, 2026
16a018d
fix: audit follow-up findings (S-3/S-4/S-6/S-7, M-13/M-14, OC-2)
SlavaSereb Aug 4, 2026
91fc0ca
chore(e2e): default the manual test workflows to mainnet
SlavaSereb Aug 4, 2026
d4e7029
Revert "chore(e2e): default the manual test workflows to mainnet"
SlavaSereb Aug 24, 2026
00803cf
fix: second-round review follow-ups (OC-2, S-4, M-14, S-6)
SlavaSereb Aug 24, 2026
21b31fe
fix: restore page-cap truncation warning in collectAllPages (review nit)
SlavaSereb Aug 24, 2026
04dd954
fix: third-round review follow-ups (OC-2 final-batch metadata + retry…
SlavaSereb Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,46 @@ FIREBLOCKS_BASE_PATH=https://api.fireblocks.io

# ── Iagon API (Required) ─────────────────────────────────────
IAGON_API_KEY=<your-iagon-api-key>
# Optional - override the Iagon API base URL (defaults to https://api.fireblocks.partners.iagon.com).
# Set this for staging or private Iagon deployments.
# IAGON_BASE_URL=https://api.fireblocks.partners.iagon.com

# ── Cardano Network ──────────────────────────────────────────
# Valid values: mainnet, preprod (defaults to mainnet)
CARDANO_NETWORK=mainnet

# ── Security (Optional) ─────────────────────────────────────
# Max request body size (default: 1mb)
MAX_BODY_SIZE=1mb

# Rate limiting: requests per window (default: 100 requests per 15 min)
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100

# Express "trust proxy" setting (default: false)
# Set to the number of reverse-proxy hops in front of the server
# (e.g. 1 for a single nginx/ALB), a preset/subnet string ("loopback",
# "10.0.0.0/8"), or true to trust the entire X-Forwarded-For chain.
# Required for rate limiting to key on the client IP behind a proxy.
TRUST_PROXY=false

# CORS: comma-separated list of allowed origins, or * for all.
# Unset by default: no CORS headers are sent and browsers enforce
# same-origin. Set only if browser clients must call this API directly.
CORS_ORIGINS=

# API Key authentication (default: disabled)
# When enabled, all requests (except /health, /api-docs, /docs) require X-API-Key header
API_KEY_ENABLED=false
API_KEY=

# ── Protocol Parameters (Optional) ──────────────────────────
# Override Cardano protocol parameters if they change at a hard fork.
# Only set these if you need to override the SDK defaults.
# These are passed to SDK via createInstance({ protocolParams: {...} })
#
# MIN_FEE_A=44 # Fee coefficient: lovelace per tx byte
# MIN_FEE_B=155381 # Fee constant: base lovelace fee
# COINS_PER_UTXO_BYTE=4310 # For min-ada calculations
# STAKE_KEY_DEPOSIT=2000000 # Stake key registration deposit
# DREP_DEPOSIT=500000000 # DRep registration deposit
113 changes: 113 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint-and-build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install

- name: Lint
run: npm run lint

- name: Type check
run: npm run typecheck

- name: Build
run: npm run build

unit-tests:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install

- name: Run unit tests
run: npm test

e2e-staking-info:
runs-on: ubuntu-latest
needs: [lint-and-build, unit-tests]
environment: tests

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install

- name: Build
run: npm run build

- name: Run staking info test (read-only)
env:
FIREBLOCKS_API_USER_KEY: ${{ secrets.FIREBLOCKS_API_KEY }}
FIREBLOCKS_API_USER_SECRET_KEY: ${{ secrets.FIREBLOCKS_API_SECRET }}
IAGON_API_KEY: ${{ secrets.IAGON_API_KEY }}
CARDANO_NETWORK: mainnet
VAULT_ACCOUNT_ID: '1'
STAKING_ACTION: info
FIREBLOCKS_BASE_PATH: ${{ secrets.FIREBLOCKS_BASE_PATH }}
run: npx tsx scripts/test-staking.ts

e2e-ada-transfer:
runs-on: ubuntu-latest
needs: [lint-and-build, unit-tests]
environment: tests

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install

- name: Build
run: npm run build

- name: Run ADA transfer test
env:
FIREBLOCKS_API_USER_KEY: ${{ secrets.FIREBLOCKS_API_KEY }}
FIREBLOCKS_API_USER_SECRET_KEY: ${{ secrets.FIREBLOCKS_API_SECRET }}
IAGON_API_KEY: ${{ secrets.IAGON_API_KEY }}
CARDANO_NETWORK: preprod
TRANSFER_AMOUNT_ADA: '3'
SOURCE_VAULT: '0'
DEST_VAULT: '1'
FIREBLOCKS_BASE_PATH: ${{ secrets.FIREBLOCKS_BASE_PATH }}
run: npx tsx scripts/test-ada-transfer.ts
2 changes: 1 addition & 1 deletion .github/workflows/test-ada-transfer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
network:
description: 'Cardano network'
required: true
default: 'mainnet'
default: 'preprod'
type: choice
options:
- preprod
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-staking.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
network:
description: 'Cardano network'
required: true
default: 'mainnet'
default: 'preprod'
type: choice
options:
- preprod
Expand Down
Loading
Loading