@layerzerolabs/common-canton 0.2.110 → 0.2.111
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.
- package/.turbo/turbo-build.log +831 -815
- package/.turbo/turbo-test.log +22 -19
- package/dist/{PVONQCBG.js → 2UKHG3HM.js} +3 -3
- package/dist/{PVONQCBG.js.map → 2UKHG3HM.js.map} +1 -1
- package/dist/{GMQEAD3I.js → 34I4OGQK.js} +3 -3
- package/dist/{GMQEAD3I.js.map → 34I4OGQK.js.map} +1 -1
- package/dist/{NLMU226B.js → 45YQNHS2.js} +3 -3
- package/dist/{NLMU226B.js.map → 45YQNHS2.js.map} +1 -1
- package/dist/{KK2NBQSG.js → 4GYCD3G4.js} +3 -3
- package/dist/{KK2NBQSG.js.map → 4GYCD3G4.js.map} +1 -1
- package/dist/{EIXIZULE.js → 5HH7PZW3.js} +37 -37
- package/dist/{EIXIZULE.js.map → 5HH7PZW3.js.map} +1 -1
- package/dist/{OQ2FVATH.js → 5OA7RJHH.js} +3 -3
- package/dist/{OQ2FVATH.js.map → 5OA7RJHH.js.map} +1 -1
- package/dist/{QYQCAXCB.js → 73I7CBP3.js} +3 -3
- package/dist/{QYQCAXCB.js.map → 73I7CBP3.js.map} +1 -1
- package/dist/{MDEOBKC3.js → 7GI6QGS6.js} +3 -3
- package/dist/{MDEOBKC3.js.map → 7GI6QGS6.js.map} +1 -1
- package/dist/{IOTRKLGP.js → 7STDATVZ.js} +3 -3
- package/dist/{IOTRKLGP.js.map → 7STDATVZ.js.map} +1 -1
- package/dist/{T4AL4FJH.js → ALC4M7WK.js} +3 -3
- package/dist/{T4AL4FJH.js.map → ALC4M7WK.js.map} +1 -1
- package/dist/{TWUSN2QY.js → AMLJTQWB.js} +3 -3
- package/dist/{TWUSN2QY.js.map → AMLJTQWB.js.map} +1 -1
- package/dist/{YCNGW63C.js → BL22CZFA.js} +3 -3
- package/dist/{YCNGW63C.js.map → BL22CZFA.js.map} +1 -1
- package/dist/{ZN2CNFMH.js → BLWJ63ZZ.js} +3 -3
- package/dist/{ZN2CNFMH.js.map → BLWJ63ZZ.js.map} +1 -1
- package/dist/CJ2WHLYC.js +116 -0
- package/dist/CJ2WHLYC.js.map +1 -0
- package/dist/{4AKFOZFY.js → CLAE73FR.js} +3 -3
- package/dist/{4AKFOZFY.js.map → CLAE73FR.js.map} +1 -1
- package/dist/CMDFOA7C.js +47 -0
- package/dist/CMDFOA7C.js.map +1 -0
- package/dist/CXTOAS7N.js +18 -0
- package/dist/CXTOAS7N.js.map +1 -0
- package/dist/{NX67V54F.js → DAP5XJGH.js} +3 -3
- package/dist/{NX67V54F.js.map → DAP5XJGH.js.map} +1 -1
- package/dist/{PSB6BQFH.js → EYLCZMM4.js} +5 -4
- package/dist/EYLCZMM4.js.map +1 -0
- package/dist/FPRRDKT5.js +52 -0
- package/dist/FPRRDKT5.js.map +1 -0
- package/dist/{KJRFPBY4.js → FXR2Z54G.js} +3 -3
- package/dist/{KJRFPBY4.js.map → FXR2Z54G.js.map} +1 -1
- package/dist/{HNZRSDTR.js → GOPXFEBJ.js} +11 -46
- package/dist/GOPXFEBJ.js.map +1 -0
- package/dist/{MJCI4XZ2.js → HLWLB4AY.js} +3 -3
- package/dist/{MJCI4XZ2.js.map → HLWLB4AY.js.map} +1 -1
- package/dist/{XSEEW6TB.js → HRZTWWO4.js} +3 -3
- package/dist/{XSEEW6TB.js.map → HRZTWWO4.js.map} +1 -1
- package/dist/{QHEXOE3Y.js → JI3XNGXO.js} +3 -27
- package/dist/JI3XNGXO.js.map +1 -0
- package/dist/{EPHLTMJZ.js → JSG7MGHH.js} +3 -3
- package/dist/{EPHLTMJZ.js.map → JSG7MGHH.js.map} +1 -1
- package/dist/{JH2I4JGJ.js → JXKVPYCE.js} +3 -3
- package/dist/{JH2I4JGJ.js.map → JXKVPYCE.js.map} +1 -1
- package/dist/{4VLNRPYZ.js → K2SFNQJC.js} +5 -5
- package/dist/{4VLNRPYZ.js.map → K2SFNQJC.js.map} +1 -1
- package/dist/{H7FW24YI.js → KBZ6YXX4.js} +3 -3
- package/dist/{H7FW24YI.js.map → KBZ6YXX4.js.map} +1 -1
- package/dist/{ZF6ASBPX.js → KH7RP6Q4.js} +3 -3
- package/dist/{ZF6ASBPX.js.map → KH7RP6Q4.js.map} +1 -1
- package/dist/{AV2FN74T.js → KTNEUXUI.js} +4 -4
- package/dist/{AV2FN74T.js.map → KTNEUXUI.js.map} +1 -1
- package/dist/{D3AV6J7R.js → LL7Z56UN.js} +3 -3
- package/dist/{D3AV6J7R.js.map → LL7Z56UN.js.map} +1 -1
- package/dist/{AYAFFPBV.js → MFVQ6EHM.js} +3 -3
- package/dist/{AYAFFPBV.js.map → MFVQ6EHM.js.map} +1 -1
- package/dist/{U7ISOK6B.js → NKXEPWZV.js} +3 -3
- package/dist/{U7ISOK6B.js.map → NKXEPWZV.js.map} +1 -1
- package/dist/{P3A2XIFY.js → NS763D57.js} +3 -3
- package/dist/{P3A2XIFY.js.map → NS763D57.js.map} +1 -1
- package/dist/{3UD2GTXW.js → PGQCNYFD.js} +3 -3
- package/dist/{3UD2GTXW.js.map → PGQCNYFD.js.map} +1 -1
- package/dist/{2UWZKS5E.js → Q3RL6UXD.js} +4 -4
- package/dist/{2UWZKS5E.js.map → Q3RL6UXD.js.map} +1 -1
- package/dist/{5KFMHJZB.js → QYQWHMQM.js} +3 -3
- package/dist/{5KFMHJZB.js.map → QYQWHMQM.js.map} +1 -1
- package/dist/{BJXKGZ5Y.js → QZJONP6W.js} +3 -3
- package/dist/{BJXKGZ5Y.js.map → QZJONP6W.js.map} +1 -1
- package/dist/{X66QZBHT.js → SLTAV2JF.js} +9 -2
- package/dist/SLTAV2JF.js.map +1 -0
- package/dist/{JV2DWUBW.js → SMLSAWCA.js} +48 -4
- package/dist/SMLSAWCA.js.map +1 -0
- package/dist/{RQXQCK22.js → UBHUOSK3.js} +3 -3
- package/dist/{RQXQCK22.js.map → UBHUOSK3.js.map} +1 -1
- package/dist/{OMRF3GH2.js → UO7AO4TN.js} +3 -3
- package/dist/{OMRF3GH2.js.map → UO7AO4TN.js.map} +1 -1
- package/dist/{R26MH4UH.js → UQMKPY2S.js} +3 -3
- package/dist/{R26MH4UH.js.map → UQMKPY2S.js.map} +1 -1
- package/dist/{HU7X7G5L.js → WSLU3JGO.js} +3 -3
- package/dist/{HU7X7G5L.js.map → WSLU3JGO.js.map} +1 -1
- package/dist/{WIQFSNER.js → XINHRIWA.js} +3 -3
- package/dist/{WIQFSNER.js.map → XINHRIWA.js.map} +1 -1
- package/dist/{PHVMGN7T.js → ZGWBZC76.js} +3 -3
- package/dist/{PHVMGN7T.js.map → ZGWBZC76.js.map} +1 -1
- package/dist/{AE56UC5I.js → ZPOY5WJ2.js} +3 -3
- package/dist/{AE56UC5I.js.map → ZPOY5WJ2.js.map} +1 -1
- package/dist/amulet.d.ts +7 -0
- package/dist/amulet.d.ts.map +1 -0
- package/dist/amulet.js +4 -0
- package/dist/amulet.js.map +1 -0
- package/dist/auth/index.js +1 -1
- package/dist/client/canton-client.d.ts +12 -0
- package/dist/client/canton-client.d.ts.map +1 -1
- package/dist/client/canton-client.js +138 -138
- package/dist/client/index.js +140 -139
- package/dist/client/interface.d.ts +12 -0
- package/dist/client/interface.d.ts.map +1 -1
- package/dist/client/utils.d.ts.map +1 -1
- package/dist/client/utils.js +2 -1
- package/dist/crypto.d.ts +23 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +4 -0
- package/dist/crypto.js.map +1 -0
- package/dist/daml-serde.d.ts +73 -0
- package/dist/daml-serde.d.ts.map +1 -0
- package/dist/daml-serde.js +4 -0
- package/dist/daml-serde.js.map +1 -0
- package/dist/events.d.ts +9 -36
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -147
- package/dist/open-api/apis/DefaultApi.js +137 -137
- package/dist/open-api/apis/index.js +137 -137
- package/dist/open-api/index.js +137 -137
- package/dist/open-api/models/Command.js +3 -3
- package/dist/open-api/models/Command1.js +2 -2
- package/dist/open-api/models/Completion.js +3 -3
- package/dist/open-api/models/Completion1.js +3 -3
- package/dist/open-api/models/CompletionResponse.js +7 -7
- package/dist/open-api/models/CompletionResponseOneOf.js +3 -3
- package/dist/open-api/models/CompletionStreamResponse.js +7 -7
- package/dist/open-api/models/CreateUserRequest.js +10 -10
- package/dist/open-api/models/CumulativeFilter.js +2 -2
- package/dist/open-api/models/DeduplicationPeriod.js +1 -1
- package/dist/open-api/models/DeduplicationPeriod1.js +1 -1
- package/dist/open-api/models/DeduplicationPeriod2.js +3 -3
- package/dist/open-api/models/EventFormat.js +2 -2
- package/dist/open-api/models/FeaturesDescriptor.js +2 -2
- package/dist/open-api/models/Filters.js +2 -2
- package/dist/open-api/models/GetActiveContractsRequest.js +2 -2
- package/dist/open-api/models/GetEventsByContractIdRequest.js +2 -2
- package/dist/open-api/models/GetLedgerApiVersionResponse.js +3 -3
- package/dist/open-api/models/GetTransactionByIdRequest.js +2 -2
- package/dist/open-api/models/GetTransactionByOffsetRequest.js +2 -2
- package/dist/open-api/models/GetUpdateByIdRequest.js +2 -2
- package/dist/open-api/models/GetUpdateByOffsetRequest.js +2 -2
- package/dist/open-api/models/GetUpdatesRequest.js +2 -2
- package/dist/open-api/models/GrantUserRightsRequest.js +10 -10
- package/dist/open-api/models/GrantUserRightsResponse.js +10 -10
- package/dist/open-api/models/IdentifierFilter.js +2 -2
- package/dist/open-api/models/JsCommands.js +5 -5
- package/dist/open-api/models/JsContractEntry.js +3 -3
- package/dist/open-api/models/JsExecuteSubmissionAndWaitForTransactionRequest.js +7 -7
- package/dist/open-api/models/JsExecuteSubmissionAndWaitRequest.js +5 -5
- package/dist/open-api/models/JsExecuteSubmissionRequest.js +5 -5
- package/dist/open-api/models/JsGetActiveContractsResponse.js +4 -4
- package/dist/open-api/models/JsGetUpdateResponse.js +13 -13
- package/dist/open-api/models/JsGetUpdateTreesResponse.js +5 -5
- package/dist/open-api/models/JsGetUpdatesResponse.js +13 -13
- package/dist/open-api/models/JsPrepareSubmissionRequest.js +5 -5
- package/dist/open-api/models/JsReassignment.js +1 -1
- package/dist/open-api/models/JsReassignmentEvent.js +1 -1
- package/dist/open-api/models/JsSubmitAndWaitForReassignmentResponse.js +1 -1
- package/dist/open-api/models/JsSubmitAndWaitForTransactionRequest.js +9 -9
- package/dist/open-api/models/JsTopologyTransaction.js +6 -6
- package/dist/open-api/models/Kind.js +8 -8
- package/dist/open-api/models/ListUserRightsResponse.js +10 -10
- package/dist/open-api/models/Operation.js +4 -4
- package/dist/open-api/models/Reassignment.js +1 -1
- package/dist/open-api/models/Reassignment1.js +1 -1
- package/dist/open-api/models/ReassignmentCommand.js +3 -3
- package/dist/open-api/models/ReassignmentCommands.js +4 -4
- package/dist/open-api/models/RevokeUserRightsRequest.js +10 -10
- package/dist/open-api/models/RevokeUserRightsResponse.js +10 -10
- package/dist/open-api/models/Right.js +9 -9
- package/dist/open-api/models/SubmitAndWaitForReassignmentRequest.js +7 -7
- package/dist/open-api/models/SubmitReassignmentRequest.js +5 -5
- package/dist/open-api/models/TopologyEvent.js +5 -5
- package/dist/open-api/models/TopologyEventEvent.js +4 -4
- package/dist/open-api/models/TopologyTransaction.js +7 -7
- package/dist/open-api/models/TransactionFilter.js +2 -2
- package/dist/open-api/models/TransactionFormat.js +2 -2
- package/dist/open-api/models/Update.js +12 -12
- package/dist/open-api/models/Update1.js +4 -4
- package/dist/open-api/models/Update1OneOf1.js +1 -1
- package/dist/open-api/models/UpdateFormat.js +2 -2
- package/dist/open-api/models/UpdateOneOf1.js +1 -1
- package/dist/open-api/models/UpdateOneOf2.js +8 -8
- package/dist/open-api/models/UpdateVettedPackagesRequest.js +6 -6
- package/dist/open-api/models/VettedPackagesChange.js +5 -5
- package/dist/open-api/models/index.js +136 -136
- package/dist/provider.d.ts +9 -27
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +140 -140
- package/dist/sdk/canton-sdk.d.ts +7 -0
- package/dist/sdk/canton-sdk.d.ts.map +1 -1
- package/dist/sdk/canton-sdk.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/dist/transaction.d.ts +64 -6
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +1 -1
- package/package.json +13 -10
- package/src/amulet.ts +28 -0
- package/src/client/canton-client.ts +60 -1
- package/src/client/interface.ts +16 -0
- package/src/client/utils.ts +1 -6
- package/src/crypto.ts +42 -0
- package/src/daml-serde.ts +182 -0
- package/src/events.ts +31 -155
- package/src/index.ts +3 -0
- package/src/provider.ts +12 -82
- package/src/sdk/canton-sdk.ts +13 -0
- package/src/transaction.ts +4 -0
- package/test/crypto.test.ts +52 -0
- package/test/daml-serde.test.ts +140 -0
- package/test/mocks/scripts/capture-disclosure.ts +5 -2
- package/test/mocks/scripts/capture-external-requests.ts +5 -2
- package/test/mocks/scripts/capture-unhappy-paths.ts +5 -2
- package/test/provider.test.ts +47 -36
- package/dist/GXJEEYPP.js +0 -104
- package/dist/GXJEEYPP.js.map +0 -1
- package/dist/HNZRSDTR.js.map +0 -1
- package/dist/JV2DWUBW.js.map +0 -1
- package/dist/PSB6BQFH.js.map +0 -1
- package/dist/QHEXOE3Y.js.map +0 -1
- package/dist/X66QZBHT.js.map +0 -1
package/src/events.ts
CHANGED
|
@@ -1,37 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { NonRetryableError } from '@layerzerolabs/common-error-utils';
|
|
1
|
+
import { NonEmittableError } from '@layerzerolabs/common-error-utils';
|
|
3
2
|
|
|
4
3
|
import type { CantonTransaction, ICantonClient } from './client';
|
|
5
|
-
import type { CreatedEvent } from './open-api/models';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
const cantonTxWithResultSchema = buildTransactionWithResultSchema(cantonTransactionSchema);
|
|
9
|
-
|
|
10
|
-
// TODO(canton): confirm these poll values are enough to wait for archival across environments.
|
|
11
|
-
const REQUEST_ARCHIVAL_POLL_INTERVAL_MS = 2000;
|
|
12
|
-
const REQUEST_ARCHIVAL_MAX_ATTEMPTS = 60;
|
|
13
|
-
|
|
14
|
-
// IRequest interface choices that consume the Request contract via `archive reqCid` in their impls.
|
|
15
|
-
const IREQUEST_ACCEPT_CHOICE = 'IRequest_Accept';
|
|
16
|
-
const IREQUEST_REJECT_CHOICE = 'IRequest_Reject';
|
|
17
|
-
|
|
18
|
-
const REQUEST_TEMPLATE_QUALIFIED_NAME = 'Request:Request';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Fetches a transaction by updateId and returns all CreatedEvents.
|
|
22
|
-
*/
|
|
23
|
-
export const fetchCreatedEvents = async (
|
|
24
|
-
client: ICantonClient,
|
|
25
|
-
updateId: string,
|
|
26
|
-
parties: string[],
|
|
27
|
-
): Promise<CreatedEvent[]> => {
|
|
28
|
-
const cantonTx = await client.fetchTransactionByUpdateId(updateId, parties);
|
|
29
|
-
return (
|
|
30
|
-
cantonTx.events
|
|
31
|
-
?.filter((event): event is { CreatedEvent: CreatedEvent } => 'CreatedEvent' in event)
|
|
32
|
-
.map((event) => event.CreatedEvent) ?? []
|
|
33
|
-
);
|
|
34
|
-
};
|
|
4
|
+
import type { CreatedEvent, ExercisedEvent } from './open-api/models';
|
|
5
|
+
import { cantonTransactionWithResultSchema } from './transaction';
|
|
35
6
|
|
|
36
7
|
/**
|
|
37
8
|
* Finds a CreatedEvent matching the expected templateId whose signatories
|
|
@@ -50,6 +21,22 @@ export const findMatchingCreatedEvent = (
|
|
|
50
21
|
});
|
|
51
22
|
};
|
|
52
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Fetches a transaction by updateId and returns all CreatedEvents.
|
|
26
|
+
*/
|
|
27
|
+
export const fetchCreatedEvents = async (
|
|
28
|
+
client: ICantonClient,
|
|
29
|
+
updateId: string,
|
|
30
|
+
parties: string[],
|
|
31
|
+
): Promise<CreatedEvent[]> => {
|
|
32
|
+
const cantonTx = await client.fetchTransactionByUpdateId(updateId, parties);
|
|
33
|
+
return (
|
|
34
|
+
cantonTx.events
|
|
35
|
+
?.filter((event): event is { CreatedEvent: CreatedEvent } => 'CreatedEvent' in event)
|
|
36
|
+
.map((event) => event.CreatedEvent) ?? []
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
53
40
|
/**
|
|
54
41
|
* Parses a Canton transaction-with-result, fetches the created events from the ledger,
|
|
55
42
|
* and returns the first CreatedEvent matching the submitted command's templateId and actAs
|
|
@@ -59,7 +46,7 @@ export const findMatchingCreatedEventFromTx = async (
|
|
|
59
46
|
client: ICantonClient,
|
|
60
47
|
tx: unknown,
|
|
61
48
|
): Promise<CreatedEvent | null> => {
|
|
62
|
-
const parsed =
|
|
49
|
+
const parsed = cantonTransactionWithResultSchema.safeParse(tx);
|
|
63
50
|
if (!parsed.success) return null;
|
|
64
51
|
|
|
65
52
|
if (parsed.data.result.type !== 'success') return null;
|
|
@@ -93,142 +80,31 @@ export const cantonDeploymentSucceeded = async (
|
|
|
93
80
|
};
|
|
94
81
|
|
|
95
82
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* Positively confirms acceptance: locate the Request created by this submission,
|
|
100
|
-
* then delegate to waitForRequestArchival, which waits for the Request to be
|
|
101
|
-
* archived and verifies the IRequest_Accept choice ran (throwing on reject,
|
|
102
|
-
* unexpected archival, or timeout). This is a positive accept check rather than
|
|
103
|
-
* inferring success from the absence of a rejection event.
|
|
104
|
-
*/
|
|
105
|
-
export const cantonRegisterSucceeded = async (
|
|
106
|
-
client: ICantonClient,
|
|
107
|
-
txWithResult: unknown,
|
|
108
|
-
): Promise<boolean> => {
|
|
109
|
-
const parsed = cantonTxWithResultSchema.safeParse(txWithResult);
|
|
110
|
-
if (!parsed.success || parsed.data.result.type !== 'success') return false;
|
|
111
|
-
|
|
112
|
-
const updateId = parsed.data.result.minedTxHash;
|
|
113
|
-
if (!updateId) return false;
|
|
114
|
-
|
|
115
|
-
// Deferred-sender register payloads build with empty actAs (the sender is
|
|
116
|
-
// resolved at sign time), so fall back to the parties this user can read as.
|
|
117
|
-
const { actAs } = parsed.data.populatedTransaction;
|
|
118
|
-
const parties = actAs.length ? actAs : await client.listUserParties();
|
|
119
|
-
if (!parties.length) return false;
|
|
120
|
-
|
|
121
|
-
const createdEvents = await fetchCreatedEvents(client, updateId, parties);
|
|
122
|
-
const requestEvent = createdEvents.find((event) =>
|
|
123
|
-
event.templateId?.includes(REQUEST_TEMPLATE_QUALIFIED_NAME),
|
|
124
|
-
);
|
|
125
|
-
if (!requestEvent) return false;
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
await waitForRequestArchival(client, requestEvent.contractId, parties);
|
|
129
|
-
return true;
|
|
130
|
-
} catch {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Poll until `contractId` is archived, then return the transaction tree that
|
|
137
|
-
* archived it. Generic: knows nothing about which choice did the archiving.
|
|
138
|
-
* Throws NonRetryableError on timeout.
|
|
83
|
+
* Waits for a contract to be archived and returns the transaction that archived it.
|
|
84
|
+
* Throws NonEmittableError if the contract is not yet archived.
|
|
139
85
|
*/
|
|
140
86
|
export const waitForArchival = async (
|
|
141
87
|
client: ICantonClient,
|
|
142
88
|
contractId: string,
|
|
143
89
|
parties: string[],
|
|
144
90
|
): Promise<CantonTransaction> => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return client.fetchTransactionByOffset(archived.offset, parties);
|
|
149
|
-
}
|
|
150
|
-
// Skip the sleep on the final attempt — there's no next iteration to wait for.
|
|
151
|
-
if (attempt < REQUEST_ARCHIVAL_MAX_ATTEMPTS) {
|
|
152
|
-
await new Promise((resolve) => setTimeout(resolve, REQUEST_ARCHIVAL_POLL_INTERVAL_MS));
|
|
153
|
-
}
|
|
91
|
+
const { archived } = await client.fetchEventsByContractId(contractId, parties);
|
|
92
|
+
if (archived) {
|
|
93
|
+
return client.fetchTransactionByOffset(archived.offset, parties);
|
|
154
94
|
}
|
|
155
|
-
throw new
|
|
156
|
-
`Archival timeout (${REQUEST_ARCHIVAL_MAX_ATTEMPTS} attempts) for ${contractId}`,
|
|
157
|
-
);
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Return the argument of the first exercise of `choice` on `contractId` in a
|
|
162
|
-
* transaction tree, typed as the caller expects. Generic over choice and arg
|
|
163
|
-
* type; returns undefined when no such exercise is present.
|
|
164
|
-
*/
|
|
165
|
-
export const getExercisedChoiceArgument = <TArg = unknown>(
|
|
166
|
-
transaction: CantonTransaction,
|
|
167
|
-
contractId: string,
|
|
168
|
-
choice: string,
|
|
169
|
-
): TArg | undefined => {
|
|
170
|
-
for (const event of transaction.events ?? []) {
|
|
171
|
-
if (!('ExercisedEvent' in event)) continue;
|
|
172
|
-
const { ExercisedEvent: exercised } = event;
|
|
173
|
-
if (exercised.contractId === contractId && exercised.choice === choice) {
|
|
174
|
-
return exercised.choiceArgument as TArg;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return undefined;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Wait for a Request to be archived and confirm it was accepted, returning the
|
|
182
|
-
* IRequest_Accept choice argument typed as `TAccept` (callers pass the codegen
|
|
183
|
-
* `IRequest_Accept` type to get `acceptContext` typed).
|
|
184
|
-
*
|
|
185
|
-
* IRequest_Accept and IRequest_Reject both end with `archive reqCid` in their
|
|
186
|
-
* Daml impls, so the archive alone can't distinguish them — the choice is read
|
|
187
|
-
* off the archive transaction tree. Throws NonRetryableError on rejection, on
|
|
188
|
-
* archival via any other choice (unexpected), or on timeout.
|
|
189
|
-
*/
|
|
190
|
-
export const waitForRequestArchival = async <TAccept = unknown>(
|
|
191
|
-
client: ICantonClient,
|
|
192
|
-
requestContractId: string,
|
|
193
|
-
parties: string[],
|
|
194
|
-
): Promise<TAccept> => {
|
|
195
|
-
const transaction = await waitForArchival(client, requestContractId, parties);
|
|
196
|
-
|
|
197
|
-
const accept = getExercisedChoiceArgument<TAccept>(
|
|
198
|
-
transaction,
|
|
199
|
-
requestContractId,
|
|
200
|
-
IREQUEST_ACCEPT_CHOICE,
|
|
201
|
-
);
|
|
202
|
-
if (accept !== undefined) return accept;
|
|
203
|
-
|
|
204
|
-
const reject = getExercisedChoiceArgument<{ rejectContext?: unknown }>(
|
|
205
|
-
transaction,
|
|
206
|
-
requestContractId,
|
|
207
|
-
IREQUEST_REJECT_CHOICE,
|
|
208
|
-
);
|
|
209
|
-
if (reject !== undefined) {
|
|
210
|
-
const reason = reject.rejectContext
|
|
211
|
-
? JSON.stringify(reject.rejectContext).slice(0, 500)
|
|
212
|
-
: 'unknown';
|
|
213
|
-
throw new NonRetryableError(`Request rejected — rejectContext: ${reason}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const consuming = getConsumingChoice(transaction, requestContractId);
|
|
217
|
-
throw new NonRetryableError(
|
|
218
|
-
`Request ${requestContractId} archived via unexpected choice ${consuming ?? '<none>'}`,
|
|
219
|
-
);
|
|
95
|
+
throw new NonEmittableError(`Contract ${contractId} is not archived yet`);
|
|
220
96
|
};
|
|
221
97
|
|
|
222
|
-
/**
|
|
223
|
-
const
|
|
98
|
+
/** Returns the first exercised event on `contractId` in a transaction tree, if any. */
|
|
99
|
+
export const getExercisedEvent = (
|
|
224
100
|
transaction: CantonTransaction,
|
|
225
101
|
contractId: string,
|
|
226
|
-
):
|
|
102
|
+
): ExercisedEvent | undefined => {
|
|
227
103
|
for (const event of transaction.events ?? []) {
|
|
228
104
|
if (!('ExercisedEvent' in event)) continue;
|
|
229
105
|
const { ExercisedEvent: exercised } = event;
|
|
230
|
-
if (exercised.contractId === contractId
|
|
231
|
-
return exercised
|
|
106
|
+
if (exercised.contractId === contractId) {
|
|
107
|
+
return exercised;
|
|
232
108
|
}
|
|
233
109
|
}
|
|
234
110
|
return undefined;
|
package/src/index.ts
CHANGED
package/src/provider.ts
CHANGED
|
@@ -14,39 +14,24 @@ import { Configuration } from './open-api/runtime';
|
|
|
14
14
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Composite provider for Canton — bundles both transport layers and shared auth.
|
|
18
|
-
*
|
|
19
17
|
* - {@link client} — JSON Ledger API (commands, queries, updates)
|
|
20
18
|
* - {@link grpcClient} — gRPC Admin API (topology transactions, namespace queries)
|
|
21
19
|
* - {@link tokenProvider} — Shared Bearer-token source used by both transports
|
|
20
|
+
* - {@link walletUrl} — Canton Network validator wallet (scan-proxy CIP-56 endpoints)
|
|
22
21
|
*/
|
|
23
|
-
export type
|
|
22
|
+
export type CantonChainProvider = {
|
|
24
23
|
client: ICantonClient;
|
|
25
24
|
grpcClient: CantonGrpcClient;
|
|
26
25
|
tokenProvider: TokenProvider;
|
|
27
26
|
/** Validator wallet URL for scan-proxy CIP-56 endpoints. Required. */
|
|
28
27
|
walletUrl: string;
|
|
29
|
-
/** LayerZero sequencer base URL (e.g. "http://localhost:8080"); vApp JSON-RPC at `/vapp`. Required. */
|
|
30
|
-
sequencerUrl: string;
|
|
31
|
-
/** Sequencer validator pubkeys plus quorum for verifying signed read responses. */
|
|
32
|
-
sequencerCommittee?: SequencerCommittee;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Client-side committee config for secp256k1 sequencer read verification.
|
|
37
|
-
* Pubkeys must be hex-encoded with `0x` prefix in the same format returned by
|
|
38
|
-
* `recoverSecp256k1PublicKey`.
|
|
39
|
-
*/
|
|
40
|
-
export type SequencerCommittee = {
|
|
41
|
-
publicKeys: `0x${string}`[];
|
|
42
|
-
quorum: number;
|
|
43
28
|
};
|
|
44
29
|
|
|
45
30
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
46
31
|
// URI Parsing
|
|
47
32
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
48
33
|
|
|
49
|
-
export type
|
|
34
|
+
export type CantonChainUriParams = {
|
|
50
35
|
/** Base URL of the JSON Ledger API (e.g. "http://localhost:5975") */
|
|
51
36
|
jsonApiUrl: string;
|
|
52
37
|
/** Host:port of the gRPC Admin API (e.g. "localhost:5076") */
|
|
@@ -55,25 +40,21 @@ export type CantonUriParams = {
|
|
|
55
40
|
userId: string;
|
|
56
41
|
/** Validator wallet URL for scan-proxy CIP-56 endpoints (e.g. "http://localhost:7201"). Required. */
|
|
57
42
|
walletUrl: string;
|
|
58
|
-
/** LayerZero sequencer base URL (e.g. "http://localhost:8080"); vApp JSON-RPC at `/vapp`. Required. */
|
|
59
|
-
sequencerUrl: string;
|
|
60
|
-
/** Sequencer committee config parsed from the provider URI. */
|
|
61
|
-
sequencerCommittee?: SequencerCommittee;
|
|
62
43
|
/** OAuth2 client credentials config — present only for non-local environments */
|
|
63
44
|
oauth2?: ClientCredentialsTokenProviderConfig;
|
|
64
45
|
};
|
|
65
46
|
|
|
66
47
|
/**
|
|
67
|
-
* Parse a Canton provider URI into its constituent parts.
|
|
48
|
+
* Parse a Canton chain provider URI into its constituent parts.
|
|
68
49
|
*
|
|
69
50
|
* URI format:
|
|
70
|
-
* `<json-api-base>?admin-api=<host:port>&user-id=<id>[&token-url=...&client-id=...&client-secret=...&scope=...&audience=...]`
|
|
51
|
+
* `<json-api-base>?admin-api=<host:port>&user-id=<id>&wallet-url=<url>[&token-url=...&client-id=...&client-secret=...&scope=...&audience=...]`
|
|
71
52
|
*
|
|
72
53
|
* The base URL (everything before the query string) is the JSON Ledger API endpoint.
|
|
73
54
|
* Canton-specific params are extracted and removed; any remaining query params are
|
|
74
55
|
* preserved on the JSON API URL.
|
|
75
56
|
*/
|
|
76
|
-
export function
|
|
57
|
+
export function parseCantonChainUri(uri: string): CantonChainUriParams {
|
|
77
58
|
const url = new URL(uri);
|
|
78
59
|
|
|
79
60
|
const adminApiHost = url.searchParams.get('admin-api');
|
|
@@ -86,19 +67,12 @@ export function parseCantonUri(uri: string): CantonUriParams {
|
|
|
86
67
|
if (!walletUrl) {
|
|
87
68
|
throw new Error('Canton provider URI missing required "wallet-url" query parameter');
|
|
88
69
|
}
|
|
89
|
-
const sequencerUrl = url.searchParams.get('sequencer-url');
|
|
90
|
-
if (!sequencerUrl) {
|
|
91
|
-
throw new Error('Canton provider URI missing required "sequencer-url" query parameter');
|
|
92
|
-
}
|
|
93
|
-
const sequencerValidatorsParam = url.searchParams.get('sequencer-validators');
|
|
94
|
-
const sequencerQuorumParam = url.searchParams.get('sequencer-quorum');
|
|
95
70
|
const tokenUrl = url.searchParams.get('token-url') ?? undefined;
|
|
96
71
|
const clientId = url.searchParams.get('client-id') ?? undefined;
|
|
97
72
|
const clientSecret = url.searchParams.get('client-secret') ?? undefined;
|
|
98
73
|
const scope = url.searchParams.get('scope') ?? undefined;
|
|
99
74
|
const audience = url.searchParams.get('audience') ?? undefined;
|
|
100
75
|
|
|
101
|
-
// Remove Canton params to get clean JSON API URL
|
|
102
76
|
for (const key of [
|
|
103
77
|
'admin-api',
|
|
104
78
|
'user-id',
|
|
@@ -115,46 +89,6 @@ export function parseCantonUri(uri: string): CantonUriParams {
|
|
|
115
89
|
url.searchParams.delete(key);
|
|
116
90
|
}
|
|
117
91
|
|
|
118
|
-
let sequencerCommittee: SequencerCommittee | undefined;
|
|
119
|
-
if (sequencerValidatorsParam !== null && sequencerQuorumParam !== null) {
|
|
120
|
-
const publicKeys = sequencerValidatorsParam
|
|
121
|
-
.split(',')
|
|
122
|
-
.map((s) => s.trim().toLowerCase())
|
|
123
|
-
.filter((s) => s.length > 0);
|
|
124
|
-
if (publicKeys.length === 0) {
|
|
125
|
-
throw new Error(
|
|
126
|
-
'Canton provider URI sequencer-validators must contain at least one pubkey',
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
for (const publicKey of publicKeys) {
|
|
130
|
-
if (!publicKey.startsWith('0x')) {
|
|
131
|
-
throw new Error(
|
|
132
|
-
`Canton provider URI sequencer-validators pubkey must start with 0x, got: ${publicKey.slice(0, 10)}...`,
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const quorum = Number(sequencerQuorumParam);
|
|
138
|
-
if (!Number.isInteger(quorum) || quorum < 1) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`Canton provider URI sequencer-quorum must be a positive integer, got: ${sequencerQuorumParam}`,
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
if (quorum > publicKeys.length) {
|
|
144
|
-
throw new Error(
|
|
145
|
-
`Canton provider URI sequencer-quorum=${quorum} exceeds committee size ${publicKeys.length}`,
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
sequencerCommittee = {
|
|
149
|
-
publicKeys: publicKeys as `0x${string}`[],
|
|
150
|
-
quorum,
|
|
151
|
-
};
|
|
152
|
-
} else if (sequencerValidatorsParam !== null || sequencerQuorumParam !== null) {
|
|
153
|
-
throw new Error(
|
|
154
|
-
'Canton provider URI must set both sequencer-validators and sequencer-quorum together',
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
92
|
const jsonApiUrl = url.toString().replace(/\/$/, '');
|
|
159
93
|
|
|
160
94
|
const oauth2 =
|
|
@@ -167,8 +101,6 @@ export function parseCantonUri(uri: string): CantonUriParams {
|
|
|
167
101
|
adminApiHost,
|
|
168
102
|
userId,
|
|
169
103
|
walletUrl,
|
|
170
|
-
sequencerUrl,
|
|
171
|
-
sequencerCommittee,
|
|
172
104
|
oauth2,
|
|
173
105
|
};
|
|
174
106
|
}
|
|
@@ -177,7 +109,7 @@ export function parseCantonUri(uri: string): CantonUriParams {
|
|
|
177
109
|
// Provider Factory
|
|
178
110
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
179
111
|
|
|
180
|
-
export type
|
|
112
|
+
export type CreateCantonChainProviderOptions = {
|
|
181
113
|
/** Raw provider URI (from provider config) */
|
|
182
114
|
uri: string;
|
|
183
115
|
/** When true, uses LocalTokenProvider (self-signed JWTs) instead of OAuth2 */
|
|
@@ -185,17 +117,17 @@ export type CreateCantonProviderOptions = {
|
|
|
185
117
|
};
|
|
186
118
|
|
|
187
119
|
/**
|
|
188
|
-
* Create a {@link
|
|
120
|
+
* Create a {@link CantonChainProvider} from a provider URI.
|
|
189
121
|
*
|
|
190
122
|
* - **isLocal=true**: Uses {@link LocalTokenProvider} with admin-level access (self-signed JWTs).
|
|
191
123
|
* - **isLocal=false**: Uses {@link ClientCredentialsTokenProvider} with OAuth2 client
|
|
192
124
|
* credentials extracted from the URI query parameters.
|
|
193
125
|
*/
|
|
194
|
-
export function
|
|
126
|
+
export function createCantonChainProvider({
|
|
195
127
|
uri,
|
|
196
128
|
isLocal,
|
|
197
|
-
}:
|
|
198
|
-
const params =
|
|
129
|
+
}: CreateCantonChainProviderOptions): CantonChainProvider {
|
|
130
|
+
const params = parseCantonChainUri(uri);
|
|
199
131
|
|
|
200
132
|
const tokenProvider = createTokenProvider(params, isLocal);
|
|
201
133
|
|
|
@@ -217,12 +149,10 @@ export function createCantonProvider({
|
|
|
217
149
|
grpcClient,
|
|
218
150
|
tokenProvider,
|
|
219
151
|
walletUrl: params.walletUrl,
|
|
220
|
-
sequencerUrl: params.sequencerUrl,
|
|
221
|
-
sequencerCommittee: params.sequencerCommittee,
|
|
222
152
|
};
|
|
223
153
|
}
|
|
224
154
|
|
|
225
|
-
function createTokenProvider(params:
|
|
155
|
+
function createTokenProvider(params: CantonChainUriParams, isLocal: boolean): TokenProvider {
|
|
226
156
|
if (isLocal) {
|
|
227
157
|
return new LocalTokenProvider({
|
|
228
158
|
userId: params.userId,
|
package/src/sdk/canton-sdk.ts
CHANGED
|
@@ -452,6 +452,19 @@ export class CantonSdk {
|
|
|
452
452
|
return this.#client.queryActiveContractsByParties(parties, offset, templateIds, opts);
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
/**
|
|
456
|
+
* Query active contracts visible to any of the given parties that implement
|
|
457
|
+
* the given interfaces (single API call).
|
|
458
|
+
*/
|
|
459
|
+
queryActiveContractsByInterface(
|
|
460
|
+
parties: string[],
|
|
461
|
+
offset: number,
|
|
462
|
+
interfaceIds: string[],
|
|
463
|
+
opts?: { limit?: number },
|
|
464
|
+
): Promise<JsActiveContract[]> {
|
|
465
|
+
return this.#client.queryActiveContractsByInterface(parties, offset, interfaceIds, opts);
|
|
466
|
+
}
|
|
467
|
+
|
|
455
468
|
/**
|
|
456
469
|
* Query active contracts visible to any of the given parties, deduplicated by contractId.
|
|
457
470
|
* Returns normalized contracts (flat shape). Use when you need a merged view across multiple parties.
|
package/src/transaction.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
assignedTransactionSchema,
|
|
5
|
+
buildTransactionWithResultSchema,
|
|
5
6
|
minedTransactionSchema,
|
|
6
7
|
signedTransactionSchema,
|
|
7
8
|
transactionSchema,
|
|
@@ -71,6 +72,9 @@ export const cantonTransactionSchema = transactionSchema.extend({
|
|
|
71
72
|
}),
|
|
72
73
|
});
|
|
73
74
|
|
|
75
|
+
export const cantonTransactionWithResultSchema =
|
|
76
|
+
buildTransactionWithResultSchema(cantonTransactionSchema);
|
|
77
|
+
|
|
74
78
|
const cantonInflightTransactionSchema = z.object({
|
|
75
79
|
/** The prepared transaction blob */
|
|
76
80
|
preparedTransaction: z.string(),
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
derEncodeSignature,
|
|
5
|
+
publicKeyToSpkiDer,
|
|
6
|
+
SECP256K1_SPKI_DER_HEADER,
|
|
7
|
+
SECP256K1_SPKI_DER_HEADER_HEX,
|
|
8
|
+
} from '../src/crypto';
|
|
9
|
+
|
|
10
|
+
// A throwaway uncompressed secp256k1 public key shape (0x04 ‖ X ‖ Y, 65 bytes).
|
|
11
|
+
const UNCOMPRESSED_KEY = `04${'ab'.repeat(64)}`;
|
|
12
|
+
|
|
13
|
+
describe('SECP256K1_SPKI_DER_HEADER', () => {
|
|
14
|
+
it('is the canonical 23-byte secp256k1 SPKI-DER header', () => {
|
|
15
|
+
expect(SECP256K1_SPKI_DER_HEADER).toHaveLength(23);
|
|
16
|
+
expect(SECP256K1_SPKI_DER_HEADER_HEX).toBe(
|
|
17
|
+
'3056301006072a8648ce3d020106052b8104000a034200',
|
|
18
|
+
);
|
|
19
|
+
expect(SECP256K1_SPKI_DER_HEADER_HEX).toHaveLength(46);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('publicKeyToSpkiDer', () => {
|
|
24
|
+
it('prepends the SPKI-DER header to the uncompressed key body', () => {
|
|
25
|
+
const spki = publicKeyToSpkiDer(`0x${UNCOMPRESSED_KEY}`);
|
|
26
|
+
expect(spki).toBe(SECP256K1_SPKI_DER_HEADER_HEX + UNCOMPRESSED_KEY);
|
|
27
|
+
expect(spki).toHaveLength(46 + 130);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('lowercases and tolerates a missing 0x prefix', () => {
|
|
31
|
+
expect(publicKeyToSpkiDer(UNCOMPRESSED_KEY.toUpperCase())).toBe(
|
|
32
|
+
SECP256K1_SPKI_DER_HEADER_HEX + UNCOMPRESSED_KEY,
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('rejects a non-uncompressed (wrong-length or non-0x04) key', () => {
|
|
37
|
+
expect(() => publicKeyToSpkiDer('0x02abcd')).toThrow();
|
|
38
|
+
expect(() => publicKeyToSpkiDer(`03${'ab'.repeat(64)}`)).toThrow();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('derEncodeSignature', () => {
|
|
43
|
+
it('DER-encodes (r, s) as SEQUENCE { INTEGER r, INTEGER s } with minimal integers', () => {
|
|
44
|
+
// r = 1, s = 2 → 30 06 02 01 01 02 01 02
|
|
45
|
+
expect(derEncodeSignature('01', '02')).toBe('3006020101020102');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('prepends 0x00 to an integer whose high bit is set', () => {
|
|
49
|
+
// r = 0xff (high bit set, needs a leading 0x00), s = 1
|
|
50
|
+
expect(derEncodeSignature('ff', '01')).toBe('3007020200ff020101');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
assertHexLength,
|
|
5
|
+
codePointLength,
|
|
6
|
+
formatDamlDecimal,
|
|
7
|
+
IntLikeSchema,
|
|
8
|
+
intToHex,
|
|
9
|
+
keccak256Hex,
|
|
10
|
+
padLeft,
|
|
11
|
+
showBool,
|
|
12
|
+
showInt,
|
|
13
|
+
textToHex,
|
|
14
|
+
timeToUnixSeconds,
|
|
15
|
+
toBigInt,
|
|
16
|
+
} from '../src/daml-serde';
|
|
17
|
+
|
|
18
|
+
describe('toBigInt', () => {
|
|
19
|
+
it('passes through bigints and converts safe-integer numbers', () => {
|
|
20
|
+
expect(toBigInt(5)).toBe(5n);
|
|
21
|
+
expect(toBigInt(7n)).toBe(7n);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('rejects unsafe-integer numbers', () => {
|
|
25
|
+
expect(() => toBigInt(2 ** 53)).toThrow(/safe integer/);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('IntLikeSchema', () => {
|
|
30
|
+
it('accepts bigints and safe-integer numbers (incl. negatives)', () => {
|
|
31
|
+
expect(IntLikeSchema.parse(5)).toBe(5);
|
|
32
|
+
expect(IntLikeSchema.parse(-5)).toBe(-5);
|
|
33
|
+
expect(IntLikeSchema.parse(2n ** 64n)).toBe(2n ** 64n);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('rejects unsafe-integer numbers (must use a bigint)', () => {
|
|
37
|
+
expect(IntLikeSchema.safeParse(2 ** 53).success).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('textToHex', () => {
|
|
42
|
+
it('encodes ASCII two hex digits per char', () => {
|
|
43
|
+
expect(textToHex('01')).toBe('3031');
|
|
44
|
+
expect(textToHex('setSeed')).toBe('73657453656564');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('rejects non-ASCII code points', () => {
|
|
48
|
+
expect(() => textToHex('café')).toThrow(/Non-ASCII/);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('intToHex', () => {
|
|
53
|
+
it('is minimal and lowercase', () => {
|
|
54
|
+
expect(intToHex(0)).toBe('0');
|
|
55
|
+
expect(intToHex(255)).toBe('ff');
|
|
56
|
+
expect(intToHex(2n ** 63n - 1n)).toBe('7fffffffffffffff');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('rejects negative values', () => {
|
|
60
|
+
expect(() => intToHex(-1)).toThrow(/negative/);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('padLeft', () => {
|
|
65
|
+
it('left-pads with zeros and rejects oversize values', () => {
|
|
66
|
+
expect(padLeft(16, '15180')).toBe('0000000000015180');
|
|
67
|
+
expect(() => padLeft(2, 'abc')).toThrow(/exceeds pad width/);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('assertHexLength', () => {
|
|
72
|
+
it('returns stripped lowercase hex of the exact length', () => {
|
|
73
|
+
expect(assertHexLength(4, '0xABcd')).toBe('abcd');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('rejects wrong length, odd length, and non-hex', () => {
|
|
77
|
+
expect(() => assertHexLength(4, 'abc')).toThrow();
|
|
78
|
+
expect(() => assertHexLength(4, 'abcde')).toThrow();
|
|
79
|
+
expect(() => assertHexLength(4, 'zzzz')).toThrow();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('show helpers', () => {
|
|
84
|
+
it('showBool capitalizes like DAML', () => {
|
|
85
|
+
expect(showBool(true)).toBe('True');
|
|
86
|
+
expect(showBool(false)).toBe('False');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('showInt is base-10', () => {
|
|
90
|
+
expect(showInt(0)).toBe('0');
|
|
91
|
+
expect(showInt(-5)).toBe('-5');
|
|
92
|
+
expect(showInt(123n)).toBe('123');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('formatDamlDecimal', () => {
|
|
97
|
+
it('keeps at least one fractional digit and strips trailing zeros', () => {
|
|
98
|
+
expect(formatDamlDecimal('1.50')).toBe('1.5');
|
|
99
|
+
expect(formatDamlDecimal('100')).toBe('100.0');
|
|
100
|
+
expect(formatDamlDecimal('0')).toBe('0.0');
|
|
101
|
+
expect(formatDamlDecimal('-1.5')).toBe('-1.5');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('rejects more than 10 fractional digits and exponent notation', () => {
|
|
105
|
+
expect(() => formatDamlDecimal('0.123456789012')).toThrow();
|
|
106
|
+
expect(() => formatDamlDecimal(1e-12)).toThrow();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('keccak256Hex', () => {
|
|
111
|
+
it('hashes the bytes denoted by the hex string', () => {
|
|
112
|
+
// keccak256 of the empty byte string.
|
|
113
|
+
expect(keccak256Hex('')).toBe(
|
|
114
|
+
'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470',
|
|
115
|
+
);
|
|
116
|
+
// keccak256 of the single byte 0x00.
|
|
117
|
+
expect(keccak256Hex('00')).toBe(
|
|
118
|
+
'bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a',
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('rejects malformed hex rather than hashing raw text', () => {
|
|
123
|
+
expect(() => keccak256Hex('xyz')).toThrow(/valid hex/);
|
|
124
|
+
expect(() => keccak256Hex('abc')).toThrow(/valid hex/);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('codePointLength', () => {
|
|
129
|
+
it('counts Unicode code points', () => {
|
|
130
|
+
expect(codePointLength('alice')).toBe(5);
|
|
131
|
+
expect(codePointLength('𝔸')).toBe(1);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('timeToUnixSeconds', () => {
|
|
136
|
+
it('returns whole seconds since the unix epoch', () => {
|
|
137
|
+
expect(timeToUnixSeconds(new Date('1970-01-01T00:00:01.000Z'))).toBe(1n);
|
|
138
|
+
expect(timeToUnixSeconds(new Date('2021-01-01T00:00:00.000Z'))).toBe(1609459200n);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -19,8 +19,11 @@
|
|
|
19
19
|
import type { ContractId } from '@daml/types';
|
|
20
20
|
import * as path from 'path';
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
|
|
22
|
+
import {
|
|
23
|
+
MnemonicSignerAdapter,
|
|
24
|
+
SignatureFormat,
|
|
25
|
+
SignatureType,
|
|
26
|
+
} from '@layerzerolabs/ver-signer-adapter';
|
|
24
27
|
import { CantonClient } from '../../../src/client/index.js';
|
|
25
28
|
// Import test contracts
|
|
26
29
|
import { packageId as testPackageId } from '../../../src/daml/sdk-test-0.0.1/lib/index.js';
|