@distrohelena/canton-typescript-sdk 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,3 +3,5 @@ import { SignCommandResult } from "./sign-command-result.js";
3
3
  export interface ICommandSigner {
4
4
  signAsync(request: SignCommandRequest): Promise<SignCommandResult>;
5
5
  }
6
+ /** One signer for each party that acts in an interactively signed command. */
7
+ export type CommandSigners = Readonly<Record<string, ICommandSigner>>;
@@ -185,7 +185,9 @@ import { CompletionObserver } from "../../services/command-completion/completion
185
185
  import { CommitmentChunkObserver } from "../../services/participant-inspection/commitment-chunk-observer.interface.js";
186
186
  import { ContractObserver } from "../../services/contracts/contract-observer.interface.js";
187
187
  import { TransactionObserver } from "../../services/events/transaction-observer.interface.js";
188
- import { ICommandSigner } from "../signing/command-signer.interface.js";
188
+ import { CommandSigners, ICommandSigner } from "../signing/command-signer.interface.js";
189
+ import { SignCommandResult } from "../signing/sign-command-result.js";
190
+ import { PreparedCommandSubmission } from "../types/prepared-command-submission.js";
189
191
  import { RequestOptions } from "../types/request-options.js";
190
192
  import { SubmitCommandRequest } from "../types/requests/submit-command-request.js";
191
193
  export interface ITransport {
@@ -380,5 +382,7 @@ export interface ITransport {
380
382
  * Submits a command.
381
383
  * Supported on JSON and gRPC. External signing is gRPC-only.
382
384
  */
383
- submitCommandAsync(request: SubmitCommandRequest, signer?: ICommandSigner, options?: RequestOptions): Promise<SubmitCommandResponse>;
385
+ submitCommandAsync(request: SubmitCommandRequest, signer?: ICommandSigner | CommandSigners, options?: RequestOptions): Promise<SubmitCommandResponse>;
386
+ prepareCommandAsync?(request: SubmitCommandRequest, options?: RequestOptions): Promise<PreparedCommandSubmission>;
387
+ executePreparedCommandAndWaitAsync?(prepared: PreparedCommandSubmission, signatures: Readonly<Record<string, SignCommandResult>>, options?: RequestOptions): Promise<SubmitCommandResponse>;
384
388
  }
@@ -0,0 +1,13 @@
1
+ import { TemplateId } from "../../query/model-types.js";
2
+ export declare class DisclosedContract {
3
+ readonly createdEventBlob: Uint8Array;
4
+ readonly templateId?: TemplateId;
5
+ readonly contractId?: string;
6
+ readonly synchronizerId?: string;
7
+ constructor(init: {
8
+ createdEventBlob: Uint8Array;
9
+ templateId?: TemplateId;
10
+ contractId?: string;
11
+ synchronizerId?: string;
12
+ });
13
+ }
@@ -0,0 +1,15 @@
1
+ import { ValidationError } from "../errors/validation-error.js";
2
+ export class DisclosedContract {
3
+ createdEventBlob;
4
+ templateId;
5
+ contractId;
6
+ synchronizerId;
7
+ constructor(init) {
8
+ if (init.createdEventBlob.length === 0)
9
+ throw new ValidationError("disclosed contracts require a createdEventBlob");
10
+ this.createdEventBlob = init.createdEventBlob;
11
+ this.templateId = init.templateId;
12
+ this.contractId = init.contractId;
13
+ this.synchronizerId = init.synchronizerId;
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ import { SubmitCommandRequest } from "./requests/submit-command-request.js";
2
+ /** Opaque result of interactive command preparation, suitable for detached signing. */
3
+ export declare class PreparedCommandSubmission {
4
+ readonly request: SubmitCommandRequest;
5
+ readonly transaction: unknown;
6
+ readonly transactionHash: Uint8Array;
7
+ readonly hashingSchemeVersion: number;
8
+ constructor(request: SubmitCommandRequest, transaction: unknown, transactionHash: Uint8Array, hashingSchemeVersion: number);
9
+ }
@@ -0,0 +1,13 @@
1
+ /** Opaque result of interactive command preparation, suitable for detached signing. */
2
+ export class PreparedCommandSubmission {
3
+ request;
4
+ transaction;
5
+ transactionHash;
6
+ hashingSchemeVersion;
7
+ constructor(request, transaction, transactionHash, hashingSchemeVersion) {
8
+ this.request = request;
9
+ this.transaction = transaction;
10
+ this.transactionHash = transactionHash;
11
+ this.hashingSchemeVersion = hashingSchemeVersion;
12
+ }
13
+ }
@@ -1,15 +1,20 @@
1
1
  import { LedgerCommand } from "../commands/ledger-command.js";
2
+ import { DisclosedContract } from "../disclosed-contract.js";
2
3
  export declare class SubmitCommandRequest {
3
4
  readonly applicationId: string;
4
5
  readonly userId?: string;
5
6
  readonly actAs: readonly string[];
6
7
  readonly readAs: readonly string[];
7
8
  readonly command: LedgerCommand;
9
+ readonly disclosedContracts: readonly DisclosedContract[];
10
+ readonly synchronizerId?: string;
8
11
  constructor(init: {
9
12
  applicationId: string;
10
13
  userId?: string;
11
14
  actAs: readonly string[];
12
15
  readAs?: readonly string[];
13
16
  command: LedgerCommand;
17
+ disclosedContracts?: readonly DisclosedContract[];
18
+ synchronizerId?: string;
14
19
  });
15
20
  }
@@ -5,6 +5,8 @@ export class SubmitCommandRequest {
5
5
  actAs;
6
6
  readAs;
7
7
  command;
8
+ disclosedContracts;
9
+ synchronizerId;
8
10
  constructor(init) {
9
11
  if (init.actAs.length === 0) {
10
12
  throw new ValidationError("submit requests require at least one actAs party");
@@ -14,5 +16,7 @@ export class SubmitCommandRequest {
14
16
  this.actAs = init.actAs;
15
17
  this.readAs = init.readAs ?? [];
16
18
  this.command = init.command;
19
+ this.disclosedContracts = init.disclosedContracts ?? [];
20
+ this.synchronizerId = init.synchronizerId;
17
21
  }
18
22
  }
package/dist/index.d.ts CHANGED
@@ -206,6 +206,8 @@ export { AllocateExternalPartyRequest } from "./core/types/requests/allocate-ext
206
206
  export { CreateExternalPartyRequest, } from "./core/types/requests/create-external-party-request.js";
207
207
  export type { ExternalPartySigner, ExternalPartySigningRequest, ExternalPartySigningResult, ExternalPartySigningPayloadKind, } from "./core/types/requests/create-external-party-request.js";
208
208
  export { AllocatePartyRequest } from "./core/types/requests/allocate-party-request.js";
209
+ export { DisclosedContract } from "./core/types/disclosed-contract.js";
210
+ export { PreparedCommandSubmission } from "./core/types/prepared-command-submission.js";
209
211
  export { AddPartyAsyncArguments } from "./core/types/requests/add-party-async-request.js";
210
212
  export { AddPartyAsyncRequest } from "./core/types/requests/add-party-async-request.js";
211
213
  export { AssembleSignedTopologyTransactionsRequest } from "./core/types/requests/assemble-signed-topology-transactions-request.js";
package/dist/index.js CHANGED
@@ -193,6 +193,8 @@ export { FinalizeDecentralizedPartyRequest, PreparedDecentralizedParty } from ".
193
193
  export { AllocateExternalPartyRequest } from "./core/types/requests/allocate-external-party-request.js";
194
194
  export { CreateExternalPartyRequest, } from "./core/types/requests/create-external-party-request.js";
195
195
  export { AllocatePartyRequest } from "./core/types/requests/allocate-party-request.js";
196
+ export { DisclosedContract } from "./core/types/disclosed-contract.js";
197
+ export { PreparedCommandSubmission } from "./core/types/prepared-command-submission.js";
196
198
  export { AddPartyAsyncArguments } from "./core/types/requests/add-party-async-request.js";
197
199
  export { AddPartyAsyncRequest } from "./core/types/requests/add-party-async-request.js";
198
200
  export { AssembleSignedTopologyTransactionsRequest } from "./core/types/requests/assemble-signed-topology-transactions-request.js";
@@ -1,11 +1,15 @@
1
- import { ICommandSigner } from "../../core/signing/command-signer.interface.js";
1
+ import { CommandSigners, ICommandSigner } from "../../core/signing/command-signer.interface.js";
2
2
  import { RequestOptions } from "../../core/types/request-options.js";
3
3
  import { ITransport } from "../../core/transports/transport.interface.js";
4
4
  import { SubmitCommandRequest } from "../../core/types/requests/submit-command-request.js";
5
5
  import { SubmitCommandResponse } from "../../core/types/responses/submit-command-response.js";
6
+ import { PreparedCommandSubmission } from "../../core/types/prepared-command-submission.js";
7
+ import { SignCommandResult } from "../../core/signing/sign-command-result.js";
6
8
  export declare class CommandServiceClient {
7
9
  private readonly pipeline;
8
- constructor(transport: ITransport, signer?: ICommandSigner);
10
+ constructor(transport: ITransport, signer?: ICommandSigner | CommandSigners);
9
11
  /** Submits a command and waits for the result. Supported on JSON and gRPC. */
10
12
  submitAndWaitAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<SubmitCommandResponse>;
13
+ prepareAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<PreparedCommandSubmission>;
14
+ executeAndWaitAsync(prepared: PreparedCommandSubmission, signatures: Readonly<Record<string, SignCommandResult>>, options?: RequestOptions): Promise<SubmitCommandResponse>;
11
15
  }
@@ -11,4 +11,6 @@ export class CommandServiceClient {
11
11
  submitAndWaitAsync(request, options) {
12
12
  return this.pipeline.submitAsync(request, options);
13
13
  }
14
+ prepareAsync(request, options) { return this.pipeline.prepareAsync(request, options); }
15
+ executeAndWaitAsync(prepared, signatures, options) { return this.pipeline.executeAsync(prepared, signatures, options); }
14
16
  }
@@ -1,13 +1,17 @@
1
- import { ICommandSigner } from "../../core/signing/command-signer.interface.js";
1
+ import { CommandSigners, ICommandSigner } from "../../core/signing/command-signer.interface.js";
2
2
  import { ITransport } from "../../core/transports/transport.interface.js";
3
3
  import { RequestOptions } from "../../core/types/request-options.js";
4
4
  import { SubmitCommandRequest } from "../../core/types/requests/submit-command-request.js";
5
5
  import { SubmitCommandResponse } from "../../core/types/responses/submit-command-response.js";
6
+ import { PreparedCommandSubmission } from "../../core/types/prepared-command-submission.js";
7
+ import { SignCommandResult } from "../../core/signing/sign-command-result.js";
6
8
  export declare class CommandSubmissionPipeline {
7
9
  private readonly dependencies;
8
10
  constructor(dependencies: {
9
11
  transport: ITransport;
10
- signer?: ICommandSigner;
12
+ signer?: ICommandSigner | CommandSigners;
11
13
  });
12
14
  submitAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<SubmitCommandResponse>;
15
+ prepareAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<PreparedCommandSubmission>;
16
+ executeAsync(prepared: PreparedCommandSubmission, signatures: Readonly<Record<string, SignCommandResult>>, options?: RequestOptions): Promise<SubmitCommandResponse>;
13
17
  }
@@ -12,4 +12,8 @@ export class CommandSubmissionPipeline {
12
12
  }
13
13
  return this.dependencies.transport.submitCommandAsync(request, this.dependencies.signer, options);
14
14
  }
15
+ prepareAsync(request, options) { if (!this.dependencies.transport.prepareCommandAsync)
16
+ throw new NotSupportedError("interactive command preparation is not supported by the selected transport"); return this.dependencies.transport.prepareCommandAsync(request, options); }
17
+ executeAsync(prepared, signatures, options) { if (!this.dependencies.transport.executePreparedCommandAndWaitAsync)
18
+ throw new NotSupportedError("interactive command execution is not supported by the selected transport"); return this.dependencies.transport.executePreparedCommandAndWaitAsync(prepared, signatures, options); }
15
19
  }
@@ -84,7 +84,9 @@ import { TopologyListPartiesRequest } from "../../core/types/requests/topology-l
84
84
  import { TopologyListVettedPackagesRequest } from "../../core/types/requests/topology-list-vetted-packages-request.js";
85
85
  import { TrafficControlStateRequest } from "../../core/types/requests/traffic-control-state-request.js";
86
86
  import { UploadDarFileRequest } from "../../core/types/requests/upload-dar-file-request.js";
87
- import { ICommandSigner } from "../../core/signing/command-signer.interface.js";
87
+ import { CommandSigners, ICommandSigner } from "../../core/signing/command-signer.interface.js";
88
+ import { SignCommandResult } from "../../core/signing/sign-command-result.js";
89
+ import { PreparedCommandSubmission } from "../../core/types/prepared-command-submission.js";
88
90
  import { AllocatePartyResponse as SdkAllocatePartyResponse } from "../../core/types/responses/allocate-party-response.js";
89
91
  import { AllocateExternalPartyResponse } from "../../core/types/responses/allocate-external-party-response.js";
90
92
  import { AddPartyAsyncResponse } from "../../core/types/responses/add-party-async-response.js";
@@ -275,7 +277,9 @@ export declare class GrpcTransport implements ITransport {
275
277
  getUpdateByHashAsync(request: GetUpdateByHashRequest, options?: RequestOptions): Promise<GetUpdateByHashResponse>;
276
278
  getUpdatesPageAsync(request: GetUpdatesPageRequest, options?: RequestOptions): Promise<GetUpdatesPageResponse>;
277
279
  getCompletionsAsync(request: GetCompletionsRequest, observer: CompletionObserver, options?: RequestOptions): Promise<void>;
278
- submitCommandAsync(request: SubmitCommandRequest, signer?: ICommandSigner, options?: RequestOptions): Promise<SubmitCommandResponse>;
280
+ submitCommandAsync(request: SubmitCommandRequest, signer?: ICommandSigner | CommandSigners, options?: RequestOptions): Promise<SubmitCommandResponse>;
281
+ prepareCommandAsync(request: SubmitCommandRequest, options?: RequestOptions): Promise<PreparedCommandSubmission>;
282
+ executePreparedCommandAndWaitAsync(prepared: PreparedCommandSubmission, signatures: Readonly<Record<string, SignCommandResult>>, options?: RequestOptions): Promise<SubmitCommandResponse>;
279
283
  private throwIfDisposed;
280
284
  private throwPartyTopologyReadCompatibilityError;
281
285
  private isProtobufDeserializationFailure;
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { ValidationError } from "../../core/errors/validation-error.js";
3
3
  import { SignCommandRequest } from "../../core/signing/sign-command-request.js";
4
+ import { PreparedCommandSubmission } from "../../core/types/prepared-command-submission.js";
4
5
  import { AllocatePartyResponse as SdkAllocatePartyResponse } from "../../core/types/responses/allocate-party-response.js";
5
6
  import { GetActiveContractsPageResponse } from "../../core/types/responses/get-active-contracts-page-response.js";
6
7
  import { GetLedgerApiVersionResponse as SdkGetLedgerApiVersionResponse } from "../../core/types/responses/get-ledger-api-version-response.js";
@@ -583,7 +584,7 @@ export class GrpcTransport {
583
584
  const payload = await this.operations.submitCommandAsync(mapGrpcSubmitCommandRequest(request), options);
584
585
  return mapGrpcSubmitCommand(payload);
585
586
  }
586
- else if (request.actAs.length !== 1) {
587
+ else if (request.actAs.length !== 1 && !("signAsync" in signer)) {
587
588
  throw new ValidationError("interactive gRPC command signing currently requires exactly one actAs party");
588
589
  }
589
590
  else if (!this.operations.prepareSubmissionAsync) {
@@ -601,20 +602,37 @@ export class GrpcTransport {
601
602
  else if (prepared.preparedTransactionHash.length === 0) {
602
603
  throw new ValidationError("interactive prepare submission did not return a preparedTransactionHash");
603
604
  }
604
- const signerResult = await signer.signAsync(new SignCommandRequest({
605
- payload: prepared.preparedTransactionHash,
606
- party: request.actAs[0],
607
- algorithmHint: "ed25519",
605
+ const signerResults = await Promise.all(request.actAs.map(async (party) => {
606
+ const partySigner = "signAsync" in signer ? signer : signer[party];
607
+ if (partySigner === undefined)
608
+ throw new ValidationError(`interactive command signing requires a signer for ${party}`);
609
+ return { party, result: await partySigner.signAsync(new SignCommandRequest({ payload: prepared.preparedTransactionHash, party, algorithmHint: "ed25519" })) };
608
610
  }));
609
611
  const executed = await this.operations.executeSubmissionAndWaitAsync(mapGrpcExecuteSubmissionAndWaitRequest({
610
612
  request,
611
613
  preparedTransaction: prepared.preparedTransaction,
612
614
  hashingSchemeVersion: prepared.hashingSchemeVersion,
613
615
  submissionId,
614
- signerResult,
616
+ signerResults,
615
617
  }), options);
616
618
  return mapGrpcInteractiveSubmitCommand(executed);
617
619
  }
620
+ async prepareCommandAsync(request, options) {
621
+ if (!this.operations.prepareSubmissionAsync)
622
+ throw new NotSupportedError("interactive gRPC command signing is not available on this transport");
623
+ const prepared = await this.operations.prepareSubmissionAsync(mapGrpcPrepareSubmissionRequest(request, randomUUID()), options);
624
+ if (!prepared.preparedTransaction || prepared.preparedTransactionHash.length === 0)
625
+ throw new ValidationError("interactive prepare submission returned an incomplete result");
626
+ return new PreparedCommandSubmission(request, prepared.preparedTransaction, prepared.preparedTransactionHash, prepared.hashingSchemeVersion);
627
+ }
628
+ async executePreparedCommandAndWaitAsync(prepared, signatures, options) {
629
+ if (!this.operations.executeSubmissionAndWaitAsync)
630
+ throw new NotSupportedError("interactive gRPC command signing is not available on this transport");
631
+ const signerResults = prepared.request.actAs.map((party) => { const result = signatures[party]; if (!result)
632
+ throw new ValidationError(`interactive command signing requires a signature for ${party}`); return { party, result }; });
633
+ const result = await this.operations.executeSubmissionAndWaitAsync(mapGrpcExecuteSubmissionAndWaitRequest({ request: prepared.request, preparedTransaction: prepared.transaction, hashingSchemeVersion: prepared.hashingSchemeVersion, submissionId: randomUUID(), signerResults }), options);
634
+ return mapGrpcInteractiveSubmitCommand(result);
635
+ }
618
636
  throwIfDisposed() {
619
637
  if (this.disposed) {
620
638
  throw new ObjectDisposedError("The client or transport has been disposed.");
@@ -20,13 +20,16 @@ export function mapGrpcSubmitCommandRequest(request) {
20
20
  actAs: [...request.actAs],
21
21
  readAs: [...request.readAs],
22
22
  submissionId: "",
23
- disclosedContracts: [],
24
- synchronizerId: "",
23
+ disclosedContracts: request.disclosedContracts.map(mapGrpcDisclosedContract),
24
+ synchronizerId: request.synchronizerId ?? "",
25
25
  packageIdSelectionPreference: [],
26
26
  prefetchContractKeys: [],
27
27
  },
28
28
  };
29
29
  }
30
+ function mapGrpcDisclosedContract(value) {
31
+ return { createdEventBlob: value.createdEventBlob, contractId: value.contractId ?? "", synchronizerId: value.synchronizerId ?? "", templateId: value.templateId === undefined ? undefined : { packageId: value.templateId.packageId, moduleName: value.templateId.moduleName, entityName: value.templateId.entityName } };
32
+ }
30
33
  export function mapGrpcSubmitCommand(payload) {
31
34
  return new SubmitCommandResponse({
32
35
  commandId: "commandId" in payload ? payload.commandId : undefined,
@@ -9,7 +9,10 @@ export declare function mapGrpcExecuteSubmissionAndWaitRequest(init: {
9
9
  preparedTransaction: PreparedTransaction;
10
10
  hashingSchemeVersion: HashingSchemeVersion;
11
11
  submissionId: string;
12
- signerResult: SignCommandResult;
12
+ signerResults: readonly {
13
+ readonly party: string;
14
+ readonly result: SignCommandResult;
15
+ }[];
13
16
  }): ExecuteSubmissionAndWaitRequest;
14
17
  export declare function mapGrpcInteractiveSubmitCommand(payload: ExecuteSubmissionAndWaitResponse): SubmitCommandResponse;
15
18
  export declare function mapGrpcSignature(result: SignCommandResult): Signature;
@@ -9,8 +9,8 @@ export function mapGrpcPrepareSubmissionRequest(request, commandId) {
9
9
  commands: [mapGrpcLedgerCommand(request.command)],
10
10
  actAs: [...request.actAs],
11
11
  readAs: [...request.readAs],
12
- disclosedContracts: [],
13
- synchronizerId: "",
12
+ disclosedContracts: request.disclosedContracts.map(value => ({ createdEventBlob: value.createdEventBlob, contractId: value.contractId ?? "", synchronizerId: value.synchronizerId ?? "", templateId: value.templateId === undefined ? undefined : { packageId: value.templateId.packageId, moduleName: value.templateId.moduleName, entityName: value.templateId.entityName } })),
13
+ synchronizerId: request.synchronizerId ?? "",
14
14
  packageIdSelectionPreference: [],
15
15
  prefetchContractKeys: [],
16
16
  verboseHashing: false,
@@ -20,12 +20,7 @@ export function mapGrpcExecuteSubmissionAndWaitRequest(init) {
20
20
  return {
21
21
  preparedTransaction: init.preparedTransaction,
22
22
  partySignatures: {
23
- signatures: [
24
- {
25
- party: init.request.actAs[0] ?? "",
26
- signatures: [mapGrpcSignature(init.signerResult)],
27
- },
28
- ],
23
+ signatures: init.signerResults.map(({ party, result }) => ({ party, signatures: [mapGrpcSignature(result)] })),
29
24
  },
30
25
  deduplicationPeriod: {
31
26
  oneofKind: undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@distrohelena/canton-typescript-sdk",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",