From 93a566b3575287b09d3b17b5cfe1b463661b50cb Mon Sep 17 00:00:00 2001 From: ayushsingh82 Date: Fri, 21 Aug 2026 02:03:35 +0530 Subject: [PATCH 1/2] Create separate SynchronizerId type, mirroring PartyId synchronizerId was passed around as a bare string in several places even where a sibling partyId parameter already used the PartyId type from core-types. Added a SynchronizerId zod schema/type following the same pattern as PartyId, and applied it at the sites that already sit next to a PartyId-typed value: SDKContext.defaultSynchronizerId, CreatePartyOptions, SignedPartyCreationService.allocate(), and MergeDelegationNamespace.setup(). Left the ~200 other bare `synchronizerId: string` occurrences as-is -- most are auto-generated OpenAPI/AsyncAPI/protobuf client code that would be overwritten by codegen anyway, consistent with how PartyId itself isn't applied everywhere either. Fixes #522 Signed-off-by: ayushsingh82 --- core/types/src/index.test.ts | 26 ++++++++++++++++++- core/types/src/index.ts | 6 +++++ .../src/wallet/init/types/context.ts | 3 ++- .../wallet/namespace/party/external/signed.ts | 4 +-- .../wallet/namespace/party/external/types.ts | 3 ++- .../namespace/token/utxos/mergeDelegation.ts | 4 +-- 6 files changed, 39 insertions(+), 7 deletions(-) diff --git a/core/types/src/index.test.ts b/core/types/src/index.test.ts index 3c210d688..ea80f1350 100644 --- a/core/types/src/index.test.ts +++ b/core/types/src/index.test.ts @@ -2,7 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { describe, expect, it } from 'vitest' -import { WalletEvent, isSpliceMessage, isSpliceMessageEvent } from './index' +import { + WalletEvent, + isSpliceMessage, + isSpliceMessageEvent, + SynchronizerId, + SYNCHRONIZER_ID_EXAMPLE, +} from './index' describe('isSpliceMessage', () => { it('accepts each splice message variant', () => { @@ -54,6 +60,24 @@ describe('isSpliceMessage', () => { }) }) +describe('SynchronizerId', () => { + it('accepts a well-formed synchronizer id', () => { + expect(SynchronizerId.safeParse(SYNCHRONIZER_ID_EXAMPLE).success).toBe( + true + ) + }) + + it('rejects values missing the "::" separator', () => { + expect(SynchronizerId.safeParse('not-a-synchronizer-id').success).toBe( + false + ) + }) + + it('rejects values shorter than 10 characters', () => { + expect(SynchronizerId.safeParse('a::b').success).toBe(false) + }) +}) + describe('isSpliceMessageEvent', () => { it('accepts objects with valid splice message data', () => { const message = { diff --git a/core/types/src/index.ts b/core/types/src/index.ts index 98b306e0b..bc2147147 100644 --- a/core/types/src/index.ts +++ b/core/types/src/index.ts @@ -26,6 +26,12 @@ export const PartyId = z export type PartyId = z.infer +export const SYNCHRONIZER_ID_EXAMPLE = 'sync::122012312312312312123' + +export const SynchronizerId = z.string().includes('::').min(10) + +export type SynchronizerId = z.infer + export const HttpUrl = z .url({ message: 'Must be a valid HTTP or HTTPS URL', diff --git a/sdk/wallet-sdk/src/wallet/init/types/context.ts b/sdk/wallet-sdk/src/wallet/init/types/context.ts index 44f64fad8..1d174fe88 100644 --- a/sdk/wallet-sdk/src/wallet/init/types/context.ts +++ b/sdk/wallet-sdk/src/wallet/init/types/context.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AbstractLedgerProvider } from '@canton-network/core-provider-ledger' +import { SynchronizerId } from '@canton-network/core-types' import { SDKLogger } from '../../logger/logger.js' import { SDKErrorHandler } from '../../error/handler.js' @@ -10,7 +11,7 @@ export type SDKContext = { userId: string logger: SDKLogger error: SDKErrorHandler - defaultSynchronizerId: string + defaultSynchronizerId: SynchronizerId } export type OfflineSDKContext = { 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 ab98168bc..98005c611 100644 --- a/sdk/wallet-sdk/src/wallet/namespace/party/external/signed.ts +++ b/sdk/wallet-sdk/src/wallet/namespace/party/external/signed.ts @@ -10,7 +10,7 @@ import { OnboardingTransactions, } from './types.js' -import { PartyId } from '@canton-network/core-types' +import { PartyId, SynchronizerId } from '@canton-network/core-types' import { AbstractLedgerProvider, LedgerProvider, @@ -315,7 +315,7 @@ export class SignedPartyCreationService { private async allocate( ledgerProvider: AbstractLedgerProvider, - synchronizerId: string, + synchronizerId: SynchronizerId, onboardingTransactions: OnboardingTransactions, multiHashSignatures: MultiHashSignatures ): Promise { diff --git a/sdk/wallet-sdk/src/wallet/namespace/party/external/types.ts b/sdk/wallet-sdk/src/wallet/namespace/party/external/types.ts index 54f11b172..282da4af5 100644 --- a/sdk/wallet-sdk/src/wallet/namespace/party/external/types.ts +++ b/sdk/wallet-sdk/src/wallet/namespace/party/external/types.ts @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { Ops } from '@canton-network/core-provider-ledger' +import { SynchronizerId } from '@canton-network/core-types' import { TokenProviderConfig } from '@canton-network/core-wallet-auth' export type CreatePartyOptions = Partial<{ isAdmin: boolean partyHint: string confirmingThreshold: number - synchronizerId: string + synchronizerId: SynchronizerId confirmingParticipantEndpoints: ParticipantEndpointConfig[] observingParticipantEndpoints: ParticipantEndpointConfig[] localParticipantObservationOnly: boolean diff --git a/sdk/wallet-sdk/src/wallet/namespace/token/utxos/mergeDelegation.ts b/sdk/wallet-sdk/src/wallet/namespace/token/utxos/mergeDelegation.ts index c926fcf3e..c10988504 100644 --- a/sdk/wallet-sdk/src/wallet/namespace/token/utxos/mergeDelegation.ts +++ b/sdk/wallet-sdk/src/wallet/namespace/token/utxos/mergeDelegation.ts @@ -8,7 +8,7 @@ import { ExerciseCommand, } from '@canton-network/core-token-standard-service' import { Holding, PrettyContract } from '@canton-network/core-tx-parser' -import { PartyId } from '@canton-network/core-types' +import { PartyId, SynchronizerId } from '@canton-network/core-types' import { LedgerNamespace } from '../../ledger/index.js' import { UtxoNamespace } from './index.js' import { resolveProviderParty } from '../utils.js' @@ -23,7 +23,7 @@ export class MergeDelegationNamespace { this.ledger = new LedgerNamespace(ctx.commonCtx) } - async setup(synchronizerId: string = '', validatorParty?: PartyId) { + async setup(synchronizerId: SynchronizerId = '', validatorParty?: PartyId) { const providerParty = resolveProviderParty( this.ctx, 'setup', From efd2d19482e820be371b4da3689da1471af8303a Mon Sep 17 00:00:00 2001 From: ayushsingh82 Date: Fri, 21 Aug 2026 17:03:19 +0530 Subject: [PATCH 2/2] Move SYNCHRONIZER_ID_EXAMPLE out of types library into test file Address review feedback: the example constant shouldn't be exported from core/types since it's only used in tests. Signed-off-by: ayushsingh82 --- core/types/src/index.test.ts | 3 ++- core/types/src/index.ts | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/types/src/index.test.ts b/core/types/src/index.test.ts index ea80f1350..1cbc7a90c 100644 --- a/core/types/src/index.test.ts +++ b/core/types/src/index.test.ts @@ -7,9 +7,10 @@ import { isSpliceMessage, isSpliceMessageEvent, SynchronizerId, - SYNCHRONIZER_ID_EXAMPLE, } from './index' +const SYNCHRONIZER_ID_EXAMPLE = 'sync::122012312312312312123' + describe('isSpliceMessage', () => { it('accepts each splice message variant', () => { expect( diff --git a/core/types/src/index.ts b/core/types/src/index.ts index bc2147147..4262e4278 100644 --- a/core/types/src/index.ts +++ b/core/types/src/index.ts @@ -26,8 +26,6 @@ export const PartyId = z export type PartyId = z.infer -export const SYNCHRONIZER_ID_EXAMPLE = 'sync::122012312312312312123' - export const SynchronizerId = z.string().includes('::').min(10) export type SynchronizerId = z.infer