Skip to content

feat: multi sync example#1622

Open
Viktor-Kalashnykov-da wants to merge 88 commits into
mainfrom
wiktor/multisync-example
Open

feat: multi sync example#1622
Viktor-Kalashnykov-da wants to merge 88 commits into
mainfrom
wiktor/multisync-example

Conversation

@Viktor-Kalashnykov-da

@Viktor-Kalashnykov-da Viktor-Kalashnykov-da commented Apr 14, 2026

Copy link
Copy Markdown

Multi-Synchronizer DvP Example - On ledger API - Part 1

Showcases automatic reassignment of Token from private synchronizer to global one, used on a realistic trade scenario with use of a custom Token.

New test:
https://github.com/canton-network/wallet/pull/1622/changes#diff-37db720ea457076fe6d04f6938d19c44e5dde3d7e1a5815953ff6b83199a1052

Design principles:

  1. !!! We remove handling of synchronizers inside wallet-sdk.
    Client code should be responsible of selecting synchronizerId in multi-sync scenarios.
    If not provided - synchronizer choice is left for canton participant logic.
    Existing tests are updated (refactor(wallet-sdk): remove default synchronizer auto-selection #1740).

  2. We try to show "realistic scenario" - we use multiple parties (Alice, Bob, TokenAdmin, TradingApp) we also
    distribute dars to selected synchronizers (private dars to private). This contributes to relative complexity of the test code

image

source:
https://docs.google.com/presentation/d/1q6LpHi-wC_MO_mzf7wp5D-15j4lNeKjCMW5xmBggaTY

  1. Example works with Token Standard V1
    We have experimented with token standard V2 -> but as V2 is not yet merged to splice use of V2 means even more code

  2. All tests run on multi-sync

This is a first PR in a series
Showcases only on-ledger part - and ensures code works.
There are 2 follow up PRs (wip)

Technical limitations:
In this PR we introduce separate tests for multi-sync (otherwise regular tests are flaky on multi-sync) - that split will be removed in a separate PR (above)

New example script:
docs/wallet-integration-guide/examples/scripts/15-multi-sync-trade.ts

Notes

  1. explicit reassignment
    we experimented with automatic reassignment of contracts - and it basically worked BUT.

It worked as long as Bob is owner of TokenRules contract which seems unrealistic.
We introduced TokenAdmin as additional party that is issuer of Token on app-synchronizer (private one).
But this in fact forces us to use explicit reassignment.

  1. TestToken on global synchronizer. The original intention seemed to be that TestToken dar is only vetted on private synchronizer.
    But since the settlement actually happens on global - TestToken related models must be also on global synchronizer

@Viktor-Kalashnykov-da Viktor-Kalashnykov-da requested review from a team as code owners April 14, 2026 19:09
@Viktor-Kalashnykov-da Viktor-Kalashnykov-da marked this pull request as draft April 14, 2026 19:09
Comment thread docs/wallet-integration-guide/examples/scripts/multi-sync/15-multi-sync-trade.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/multi-sync/15-multi-sync-trade.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/15-multi-sync-trade.ts Outdated
Comment thread sdk/wallet-sdk/src/wallet/sdk.ts Fixed
@Viktor-Kalashnykov-da Viktor-Kalashnykov-da changed the title Multi-Sync Example feat(multisync-example): Multi-Sync Example Apr 17, 2026
Comment thread core/token-standard-service/src/token-standard-service.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/utils/synchronizer.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/utils/synchronizer.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/multi-sync/15-multi-sync-trade.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/15-multi-sync-trade.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/multi-sync/15-multi-sync-trade.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/multi-sync/15-multi-sync-trade.ts Outdated
Comment thread docs/wallet-integration-guide/examples/scripts/utils/synchronizer.ts Outdated
Comment thread scripts/src/start-localnet.ts Outdated
Comment thread sdk/wallet-sdk/src/wallet/namespace/contract/client.ts Outdated
Comment thread sdk/wallet-sdk/src/wallet/namespace/ledger/dar/client.ts Outdated
Comment thread .gitignore Outdated

@jarekr-da jarekr-da left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made first round or review.
Generally that is a scenario we want.
I posted questions and problems to recheck / fix.

@Viktor-Kalashnykov-da Viktor-Kalashnykov-da changed the title feat(multisync-example): Multi-Sync Example feat: multi sync example Apr 21, 2026
Comment thread core/wallet-test-utils/src/wallet-gateway.ts Outdated
Comment thread .vscode/settings.json Outdated
Comment thread api-specs/ledger-api/3.4.12/openapi.yaml
Comment thread core/ledger-client/src/ledger-client.ts Outdated
mziolekda and others added 4 commits June 9, 2026 10:29
* fix: eliminate copying of dars from global to app synchronizer

Signed-off-by: Marcin Ziolek <marcin.ziolek@digitalasset.com>

* fix: elmininate the need for custom bootstrap script completely

Signed-off-by: Marcin Ziolek <marcin.ziolek@digitalasset.com>

---------

Signed-off-by: Marcin Ziolek <marcin.ziolek@digitalasset.com>
Signed-off-by: mziolekda <marcin.ziolek@digitalasset.com>
…s TestToken before app-synchronizer self-transfer in run-15

Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
@jarekr-da jarekr-da marked this pull request as ready for review June 10, 2026 10:37
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
…sync flag from run-15 usage

Multi-sync is now the default for start:localnet; --no-multi-sync starts single-synchronizer debug mode.

Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
…out test token

Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
}
)

const signature = signTransactionHash(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why we've split up the prepare/sign/execute functions is to enable offline signing. Could we move the synchronizerIds as part of the CreatePartyOptions so that on the sdk.party.external.create call this generates all of the topology transactions to be signed and then the SignedpartyCreationService takes in an array of ExecuteOptions?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or disregard this if offline signing is supposed to be enabled in a separate phase or not supported at all as per this

'Cannot register party on additional synchronizer: publicKey and privateKey must be provided (offline signing is not supported for additionalSynchronizerIds)'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the problem - but I think offline signing with multiple synchronizers is a bigger change -> separate PR/ design.

* `KNOWN_PACKAGE_VERSION`. Since the already-vetted package is resolved by
* package-name at command-submission time, it is safe to reuse it and continue.
*/
async function vetPackageIdempotent(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be added to the dar namespace in the sdk?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic moved to existing funtion

* @param darBytes - Raw DAR file bytes.
* @param synchronizerId - The synchronizer on which the package should be vetted.
*/
export async function vetDar(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why this isn't part of the dar namespace where it can be called with sdk.ledger.dar.vet and then we don't have to pass the provider in as an arg (it will just reuse the provider initialized in the sdk) ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved
also discovered that There is DarService which is duplicate of DarNamespace (comment added)

const events = unassignResponse.reassignment?.events ?? []
const unassignedEvent = events.find((e) => 'JsUnassignedEvent' in e)
if (!unassignedEvent || !('JsUnassignedEvent' in unassignedEvent)) {
throw new Error(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: can we use this.ctx.error.throw here instead

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done (+ in few other places)

* @param synchronizerIds - Synchronizers to submit to in parallel
* @param privateKey - Key used to sign each prepared transaction
*/
public async executeOnSynchronizers(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this convenience method, but I do want the name of this to indicate that it's preparing (for all synchronizers), signing and executing. Do you think there could be any value in also having a bulk prepare method (prepareForAllSynchronizers) for the case of offline signing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method was renamed

as for prepareForAllSynchronizers I would add this in other PR
or just change this one to use few such methods

Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
@jarekr-da jarekr-da force-pushed the wiktor/multisync-example branch from 3435416 to 2b41e6b Compare June 17, 2026 06:58
Signed-off-by: jarekr-da <jaroslaw.ratajski@digitalasset.com>
@jarekr-da jarekr-da force-pushed the wiktor/multisync-example branch from 93653ae to 1bee65e Compare June 17, 2026 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants