Releases: ChainSafe/canton-middleware
Releases · ChainSafe/canton-middleware
Release list
v0.8.0
0.8.0 (2026-07-15)
Features
- api: transfer history + outgoing/expired endpoints (#331) (1cd3f5f)
- api: transfer to external party via party id (#324) (a0268a0)
- canceled status for withdrawn transfer offers (#346) (428e725)
- claim back expired offers (#339) (b1e6361)
- configurable transfer validity (#334) (2115077)
- exclude offer-locked holdings from indexed balances (#340) (8a39cc8)
- indexer: generalized transfers table (#330) (ccb5d76)
- ledger tx id on offer-based transfers (#347) (83d4116)
- relayer: persist eth scan progress (#344) (123a84a)
- validate transfer recipient party (#362) (924c9d3)
Bug Fixes
v0.7.0
v0.6.0
v0.5.3
v0.5.2
v0.5.1
v0.5.0
0.5.0 (2026-06-08)
Features
- api and indexer instrumented with metrics (#221) (6dd9d7d)
- ci: add release-please workflow with reusable docker build (#288) (1f95bf2)
- ci: use GraphQL createCommitOnBranch for signed commits (#286) (38ad918)
- metric: instrument submitter (#295) (57602c1)
- metrics: eth rpc instrumented (#297) (9a0044e)
- metrics: instrument accept worker (#299) (661d817)
Bug Fixes
v0.4.2
What's Changed
- fix: increased server timeout by @sadiq1971 in #280
- feat: async eth_sendRawTransaction by @sadiq1971 in #281
Full Changelog: v0.4.1...v0.4.2
v0.4.1
fix: USDCx registry — match DA hosted token-standard API shape (#276)
* fix: USDCx registry — match DA hosted token-standard API shape
The Splice transfer-factory endpoint hosted by DA's utilities API
(api.utilities.digitalasset-dev.com) mounts the registry per-registrar
under /api/token-standard/v0/registrars/{registrar}/, and expects the
on-ledger choice arguments wrapped in `choiceArguments` with a structured
`instrumentId` plus `executeBefore`/`requestedAt`/`extraArgs` fields.
The SDK was emitting the legacy unprefixed path with a flat body, so
transfers against the deployed devnet failed with `registry returned
404` from nginx. The local devstack happened to register the legacy
path, masking the bug. Update both the SDK client and the devstack
sidecar to the spec-conformant shape, and reuse the registry call's
requestedAt/executeBefore in the on-ledger TransferFactory_Transfer
choice so the timestamps stay consistent.
* fix: unwrap nested choiceContext envelope from DA's registry response
DA's hosted token-standard registry returns the AnyValue choice context
and disclosed contracts both nested inside `choiceContext` (mirroring
the receiver-side AcceptContextResponse shape):
{ "factoryId": ..., "transferKind": ...,
"choiceContext": { "choiceContextData": {"values": {...}},
"disclosedContracts": [...] } }
The previous PR fixed the request side but left the SDK's RegistryResponse
expecting a flat shape (choiceContext = AnyValue map directly,
disclosedContracts at top-level). Against DA's response that would put
the whole {choiceContextData, disclosedContracts} object into
ChoiceContext and leave DisclosedContracts null; ConvertAnyValueChoiceContext
finds no `values` key at that level, falls through to ConvertChoiceContext,
which fails with "json: cannot unmarshal object into Go value of type string".
GetTransferFactory now decodes into a wire struct and lifts choiceContextData
and disclosedContracts back into the flat RegistryResponse the downstream
converters expect. The local devstack mock is updated to emit the same
nested shape, so it actually exercises the unwrap path.
Also URL-escape the registrar party in the request path so future party IDs
with reserved characters don't silently misroute (Gemini's review).
Diagnosis and fix by salindne.
* Update pkg/cantonsdk/token/client.go
Co-authored-by: Sebastian Lindner <33971232+salindne@users.noreply.github.com>
* fix: strip trailing whitespace from comment (gofmt)
---------
Co-authored-by: Sebastian Lindner <33971232+salindne@users.noreply.github.com>
v0.4.0 - USDCx Support
feat: E2E devstack and tests for USDCx offer/accept transfer flow (#271) * fix: replace CIP56TransferFactory with AllocationFactory in USDCx bootstrap Switch from CIP56TransferFactory (atomic, local-only) to DA's AllocationFactory from utility_registry_app_v0. This factory creates TransferOffer contracts on TransferFactory_Transfer, matching devnet behaviour where the receiver must exercise TransferInstruction_Accept to complete the transfer. Also bootstraps TransferRule and InstrumentConfiguration (with empty holderRequirements) which are required as choiceContextData when the receiver accepts the transfer offer. Closes #258 * feat: E2E devstack and tests for USDCx offer/accept transfer flow (#263) - Canton interface: add FindPendingInboundTransferInstructions and AcceptTransferInstruction - CantonShim: add usdcxTokenClient (ExternalTokens + RegistryClient) and implement both methods - Canton2Shim: implement both methods returning errP2NotSupported - ServiceManifest: add USDCxRegistryHTTP; ServiceDiscovery resolves usdcx-registry:8090 - DSL: add WaitForPendingTransferOffer helper (polls until TransferOffer appears in ACS) - Tests: remove t.Skip, add accept step after each TransferToken call so the AllocationFactory offer/accept flow is exercised end-to-end * fix: use api-server for USDCx incoming transfer accept flow Replace the direct-Canton accept approach with API-based acceptance: - APIServer interface: add ListIncomingTransfers, PrepareAcceptTransfer, ExecuteAcceptTransfer - APIServerShim: implement the three new methods with EIP-191 auth - http.go: add getAuth helper for authenticated GET requests - DSL: replace WaitForPendingTransferOffer (Canton-direct) with WaitForIncomingTransferOffer (polls GET /api/v2/transfer/incoming) - Revert Canton interface / shim changes from first approach (no direct AcceptTransferInstruction / FindPendingInboundTransferInstructions) - Revert ServiceManifest.USDCxRegistryHTTP and discovery.go port 8090 - Tests: split into custodial (accept worker auto-accepts) and non-custodial (receiver accepts via PrepareAcceptTransfer / ExecuteAcceptTransfer API) * fix: regenerate custodial mock and address lint issues - pkg/custodial/mocks: regenerate mock_canton_token.go to include PrepareAcceptTransfer (added to token.Token interface in PR #269) - docker/discovery.go, shim/canton2.go: gofmt alignment fixes - cross_transfer_test.go: fix err variable shadow in ExecuteAcceptTransfer calls * fix: fall back to AllocationFactory when no CIP56TransferFactory found USDCx on P2 deploys an AllocationFactory (utility_registry_app_v0) instead of a CIP56TransferFactory. getTransferFactoryCID now retries with GetAllocationFactory when ErrTransferFactoryNotFound is returned and UtilityRegistryAppPackageID is configured. The exercise command is unchanged — buildTransferCommand already uses the Splice interface template ID (TransferInstructionV1:TransferFactory) which is implemented by both concrete templates. - token/client.go: add AllocationFactory constants and GetAllocationFactory; modify getTransferFactoryCID to fall back - shim/canton.go: add utilityRegistryAppPackageID constant - shim/canton2.go: pass UtilityRegistryAppPackageID to token.New * fix: usdcx support and test fixed * fix: optimized implementation * fix: dar download before devstack up * fix: removed unused changes * fix: removed unused changes * fix: enable indexer Holding/Offer capture against real Canton chains Three fixes verified against ChainSafe DevNet (the indexer now correctly indexes a real USDCx Holding owned by an external party): - decoder.go: NewHoldingDecoder and NewOfferDecoder filtered events by ev.PackageID == configPackageID. That equality fails whenever the config uses a package-name reference (#name), because Canton 3.x accepts those in stream filters but events arrive carrying the resolved package hash. Match by module+entity only, mirroring the CIP56 decoder; the stream- level template filter already narrows the wire to the right template. - migrations/indexerdb/5,6: CreateModelIndexes is variadic — each string becomes one index. Both migrations passed a comma-joined column list as a single argument, producing ERROR: column "a,b,c" does not exist. Split into separate args. - config.indexer.docker.yaml: switch package fields to package-name references so the same indexer config layout works on the local devstack and on real chains (devnet/mainnet reject raw hashes in stream filters with INVALID_FIELD). * fix: config updated * fix: config updated for devnet --------- Co-authored-by: Sebastian Lindner <33971232+salindne@users.noreply.github.com>