From 11423ece723e00b54fbbdcdf765ee4ae285c9da8 Mon Sep 17 00:00:00 2001 From: jarekr-da Date: Tue, 7 Apr 2026 17:08:24 +0200 Subject: [PATCH 1/6] added synchronizerId Signed-off-by: jarekr-da --- .../examples/scripts/06-merge-utxos.ts | 17 +++++++++++++++++ scripts/src/start-localnet.ts | 4 ++-- .../wallet/namespace/party/external/signed.ts | 4 +++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts index 9ca87a591..541035cfc 100644 --- a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts +++ b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts @@ -19,9 +19,18 @@ const amulet = await sdk.amulet(AMULET_NAMESPACE_CONFIG) const aliceKeys = sdk.keys.generate() +// Discover the synchronizer where Amulet contracts live by building a tap command +// (this only fetches contracts from the registry/scan, no ledger submission) +const [, probeDisclosed] = await amulet.tap( + 'probe::0000000000000000000000000000000000000000000000000000000000000000', + '1' +) +const synchronizerId = probeDisclosed[0]?.synchronizerId + const alice = await sdk.party.external .create(aliceKeys.publicKey, { partyHint: 'v1-06-alice', + synchronizerId, }) .sign(aliceKeys.privateKey) .execute() @@ -36,11 +45,15 @@ const tapPromises = tapIndices.map(async () => { '2000000' ) + const synchronizerId = + amuletTapDisclosedContracts[0]?.synchronizerId ?? undefined + return sdk.ledger .prepare({ partyId: alice.partyId, commands: amuletTapCommand, disclosedContracts: amuletTapDisclosedContracts, + synchronizerId, }) .sign(aliceKeys.privateKey) .execute({ partyId: alice.partyId }) @@ -59,11 +72,15 @@ const [mergeUtxoCommands, mergedDisclosedContracts] = await token.utxos.merge({ }) const mergePromises = mergeUtxoCommands.map((mergeCommand) => { + const synchronizerId = + mergedDisclosedContracts[0]?.synchronizerId ?? undefined + return sdk.ledger .prepare({ partyId: alice.partyId, commands: mergeCommand, disclosedContracts: mergedDisclosedContracts, + synchronizerId, }) .sign(aliceKeys.privateKey) .execute({ partyId: alice.partyId }) diff --git a/scripts/src/start-localnet.ts b/scripts/src/start-localnet.ts index e2b19d97b..e17fab7e6 100644 --- a/scripts/src/start-localnet.ts +++ b/scripts/src/start-localnet.ts @@ -55,8 +55,8 @@ const composeBase = [ 'app-provider', '--profile', 'app-user', - // '--profile', - // 'multi-sync', + '--profile', + 'multi-sync', ] const network = getNetworkArg() diff --git a/sdk/wallet-sdk/src/wallet/namespace/party/external/signed.ts b/sdk/wallet-sdk/src/wallet/namespace/party/external/signed.ts index 1893cef8c..d77fbe302 100644 --- a/sdk/wallet-sdk/src/wallet/namespace/party/external/signed.ts +++ b/sdk/wallet-sdk/src/wallet/namespace/party/external/signed.ts @@ -144,7 +144,9 @@ export class SignedPartyCreation { } = options const ledgerProvider = defaultLedgerProvider ?? this.ctx.ledgerProvider try { - const synchronizerId = this.ctx.defaultSynchronizerId + const synchronizerId = + this.createPartyOptions?.synchronizerId ?? + this.ctx.defaultSynchronizerId await this.allocate( ledgerProvider, From c10259728abd14258a73b6a3b570aecf8f37422e Mon Sep 17 00:00:00 2001 From: jarekr-da Date: Wed, 8 Apr 2026 07:53:05 +0200 Subject: [PATCH 2/6] improved code Signed-off-by: jarekr-da --- .../examples/scripts/06-merge-utxos.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts index 541035cfc..99ca33786 100644 --- a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts +++ b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts @@ -45,8 +45,7 @@ const tapPromises = tapIndices.map(async () => { '2000000' ) - const synchronizerId = - amuletTapDisclosedContracts[0]?.synchronizerId ?? undefined + const synchronizerId = amuletTapDisclosedContracts[0]?.synchronizerId return sdk.ledger .prepare({ @@ -72,8 +71,7 @@ const [mergeUtxoCommands, mergedDisclosedContracts] = await token.utxos.merge({ }) const mergePromises = mergeUtxoCommands.map((mergeCommand) => { - const synchronizerId = - mergedDisclosedContracts[0]?.synchronizerId ?? undefined + const synchronizerId = mergedDisclosedContracts[0]?.synchronizerId return sdk.ledger .prepare({ From 4e6f34d5eeb328ff929d519a0df5a1f9784d2cc9 Mon Sep 17 00:00:00 2001 From: jarekr-da Date: Wed, 8 Apr 2026 09:18:04 +0200 Subject: [PATCH 3/6] trick to fix synchronizerId Signed-off-by: jarekr-da --- .../examples/scripts/06-merge-utxos.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts index 99ca33786..95ba3f6bf 100644 --- a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts +++ b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts @@ -30,7 +30,7 @@ const synchronizerId = probeDisclosed[0]?.synchronizerId const alice = await sdk.party.external .create(aliceKeys.publicKey, { partyHint: 'v1-06-alice', - synchronizerId, + ...(synchronizerId && { synchronizerId }), }) .sign(aliceKeys.privateKey) .execute() @@ -52,7 +52,7 @@ const tapPromises = tapIndices.map(async () => { partyId: alice.partyId, commands: amuletTapCommand, disclosedContracts: amuletTapDisclosedContracts, - synchronizerId, + ...(synchronizerId && { synchronizerId }), }) .sign(aliceKeys.privateKey) .execute({ partyId: alice.partyId }) @@ -78,7 +78,7 @@ const mergePromises = mergeUtxoCommands.map((mergeCommand) => { partyId: alice.partyId, commands: mergeCommand, disclosedContracts: mergedDisclosedContracts, - synchronizerId, + ...(synchronizerId && { synchronizerId }), }) .sign(aliceKeys.privateKey) .execute({ partyId: alice.partyId }) From 7fe5ee1ea57e3b5d8cd58e80ed1b61d9f3af15f7 Mon Sep 17 00:00:00 2001 From: vkalashnykov Date: Wed, 8 Apr 2026 20:13:03 +0200 Subject: [PATCH 4/6] Multi-Sync example: added connectedSynchronizers() method for Ledger to fetch connected synchronizers by party, particiapntId and identityProviderId Signed-off-by: vkalashnykov --- .../examples/scripts/06-merge-utxos.ts | 16 ++++++ .../src/wallet/namespace/ledger/client.ts | 3 + .../src/wallet/namespace/state/client.ts | 55 +++++++++++++++++++ .../src/wallet/namespace/state/index.ts | 4 ++ 4 files changed, 78 insertions(+) create mode 100644 sdk/wallet-sdk/src/wallet/namespace/state/client.ts create mode 100644 sdk/wallet-sdk/src/wallet/namespace/state/index.ts diff --git a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts index 95ba3f6bf..aba416612 100644 --- a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts +++ b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts @@ -95,3 +95,19 @@ if (utxosAliceMerged.length === 1) { } else { throw new Error(`utxos not successfully merged`) } + +// Test: fetch connected synchronizers for alice via sdk.ledger.state +const connectedSyncResponse = await sdk.ledger.state.connectedSynchronizers({ + party: alice.partyId, +}) + +if ( + connectedSyncResponse.connectedSynchronizers && + connectedSyncResponse.connectedSynchronizers.length > 0 +) { + logger.info( + `connected synchronizers for alice: ${connectedSyncResponse.connectedSynchronizers.map((s) => s.synchronizerId).join(', ')}` + ) +} else { + throw new Error('No connected synchronizers found for alice') +} diff --git a/sdk/wallet-sdk/src/wallet/namespace/ledger/client.ts b/sdk/wallet-sdk/src/wallet/namespace/ledger/client.ts index 2484bbc36..0d5ee83a7 100644 --- a/sdk/wallet-sdk/src/wallet/namespace/ledger/client.ts +++ b/sdk/wallet-sdk/src/wallet/namespace/ledger/client.ts @@ -13,6 +13,7 @@ import { Dar } from './dar/client.js' import { AcsOptions } from '@canton-network/core-acs-reader' import { InternalPartySubmitterService } from './internal.js' import { PreparedTransactionService } from './hash/index.js' +import { State } from '../state/index.js' type ListACSBody = { filter?: v3_4.components['schemas']['TransactionFilter'] @@ -22,10 +23,12 @@ export class Ledger { public readonly dar: Dar public readonly internal: InternalPartySubmitterService public readonly preparedTransaction: PreparedTransactionService + public readonly state: State constructor(private readonly sdkContext: CommonCtx) { this.dar = new Dar(sdkContext) this.internal = new InternalPartySubmitterService(sdkContext) this.preparedTransaction = new PreparedTransactionService(sdkContext) + this.state = new State(sdkContext) } public async ledgerEnd() { diff --git a/sdk/wallet-sdk/src/wallet/namespace/state/client.ts b/sdk/wallet-sdk/src/wallet/namespace/state/client.ts new file mode 100644 index 000000000..1b4e69bdd --- /dev/null +++ b/sdk/wallet-sdk/src/wallet/namespace/state/client.ts @@ -0,0 +1,55 @@ +// Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { CommonCtx } from '../../sdk.js' +import { Ops } from '@canton-network/core-provider-ledger' +import { SDKLogger } from '../../logger/index.js' + +export type ConnectedSynchronizersOptions = { + party?: string + participantId?: string + identityProviderId?: string +} + +export class State { + private readonly logger: SDKLogger + + constructor(private readonly ctx: CommonCtx) { + this.logger = ctx.logger.child({ namespace: 'State' }) + } + + /** + * Returns the list of connected synchronizers for the given party / participant. + * + * Calls GET /v2/state/connected-synchronizers with optional query parameters. + * + * @param options - Optional filters: party, participantId, identityProviderId. + */ + public async connectedSynchronizers( + options?: ConnectedSynchronizersOptions + ) { + this.logger.debug({ options }, 'Fetching connected synchronizers') + + const result = + await this.ctx.ledgerProvider.request( + { + method: 'ledgerApi', + params: { + resource: '/v2/state/connected-synchronizers', + requestMethod: 'get', + query: { + ...(options?.party && { party: options.party }), + ...(options?.participantId && { + participantId: options.participantId, + }), + ...(options?.identityProviderId && { + identityProviderId: options.identityProviderId, + }), + }, + }, + } + ) + + return result + } +} diff --git a/sdk/wallet-sdk/src/wallet/namespace/state/index.ts b/sdk/wallet-sdk/src/wallet/namespace/state/index.ts new file mode 100644 index 000000000..ccb7adf4e --- /dev/null +++ b/sdk/wallet-sdk/src/wallet/namespace/state/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +export * from './client.js' From 441ed1d1d950678c48840d27e086db987f690b4c Mon Sep 17 00:00:00 2001 From: vkalashnykov Date: Thu, 9 Apr 2026 10:00:58 +0200 Subject: [PATCH 5/6] Fix: Code Review Signed-off-by: vkalashnykov --- sdk/wallet-sdk/src/wallet/namespace/state/client.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sdk/wallet-sdk/src/wallet/namespace/state/client.ts b/sdk/wallet-sdk/src/wallet/namespace/state/client.ts index 1b4e69bdd..06fb8824c 100644 --- a/sdk/wallet-sdk/src/wallet/namespace/state/client.ts +++ b/sdk/wallet-sdk/src/wallet/namespace/state/client.ts @@ -38,11 +38,13 @@ export class State { resource: '/v2/state/connected-synchronizers', requestMethod: 'get', query: { - ...(options?.party && { party: options.party }), - ...(options?.participantId && { + ...(options?.party !== undefined && { + party: options.party, + }), + ...(options?.participantId !== undefined && { participantId: options.participantId, }), - ...(options?.identityProviderId && { + ...(options?.identityProviderId !== undefined && { identityProviderId: options.identityProviderId, }), }, From b9f5547bc467f2b9458d181124ad904c3af98b14 Mon Sep 17 00:00:00 2001 From: vkalashnykov Date: Thu, 9 Apr 2026 11:47:21 +0200 Subject: [PATCH 6/6] Improvement: improved retrieval of Synchronizer for Alice of 06-merge-utxos scenario Signed-off-by: vkalashnykov --- .../examples/scripts/06-merge-utxos.ts | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts index aba416612..008d5a70d 100644 --- a/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts +++ b/docs/wallet-integration-guide/examples/scripts/06-merge-utxos.ts @@ -19,13 +19,22 @@ const amulet = await sdk.amulet(AMULET_NAMESPACE_CONFIG) const aliceKeys = sdk.keys.generate() -// Discover the synchronizer where Amulet contracts live by building a tap command -// (this only fetches contracts from the registry/scan, no ledger submission) -const [, probeDisclosed] = await amulet.tap( - 'probe::0000000000000000000000000000000000000000000000000000000000000000', - '1' -) -const synchronizerId = probeDisclosed[0]?.synchronizerId +const connectedSyncResponse = await sdk.ledger.state.connectedSynchronizers({}) + +let synchronizerId + +if ( + connectedSyncResponse.connectedSynchronizers && + connectedSyncResponse.connectedSynchronizers.length > 0 +) { + logger.info( + `connected synchronizers: ${connectedSyncResponse.connectedSynchronizers.map((s) => s.synchronizerId).join(', ')}` + ) + synchronizerId = + connectedSyncResponse.connectedSynchronizers[0].synchronizerId +} else { + throw new Error('No connected synchronizers found') +} const alice = await sdk.party.external .create(aliceKeys.publicKey, { @@ -95,19 +104,3 @@ if (utxosAliceMerged.length === 1) { } else { throw new Error(`utxos not successfully merged`) } - -// Test: fetch connected synchronizers for alice via sdk.ledger.state -const connectedSyncResponse = await sdk.ledger.state.connectedSynchronizers({ - party: alice.partyId, -}) - -if ( - connectedSyncResponse.connectedSynchronizers && - connectedSyncResponse.connectedSynchronizers.length > 0 -) { - logger.info( - `connected synchronizers for alice: ${connectedSyncResponse.connectedSynchronizers.map((s) => s.synchronizerId).join(', ')}` - ) -} else { - throw new Error('No connected synchronizers found for alice') -}