@layerzerolabs/common-canton 0.2.109 → 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.
Files changed (230) hide show
  1. package/.turbo/turbo-build.log +808 -794
  2. package/.turbo/turbo-test.log +16 -12
  3. package/dist/{PVONQCBG.js → 2UKHG3HM.js} +3 -3
  4. package/dist/{PVONQCBG.js.map → 2UKHG3HM.js.map} +1 -1
  5. package/dist/{GMQEAD3I.js → 34I4OGQK.js} +3 -3
  6. package/dist/{GMQEAD3I.js.map → 34I4OGQK.js.map} +1 -1
  7. package/dist/{NLMU226B.js → 45YQNHS2.js} +3 -3
  8. package/dist/{NLMU226B.js.map → 45YQNHS2.js.map} +1 -1
  9. package/dist/{KK2NBQSG.js → 4GYCD3G4.js} +3 -3
  10. package/dist/{KK2NBQSG.js.map → 4GYCD3G4.js.map} +1 -1
  11. package/dist/{EIXIZULE.js → 5HH7PZW3.js} +37 -37
  12. package/dist/{EIXIZULE.js.map → 5HH7PZW3.js.map} +1 -1
  13. package/dist/{OQ2FVATH.js → 5OA7RJHH.js} +3 -3
  14. package/dist/{OQ2FVATH.js.map → 5OA7RJHH.js.map} +1 -1
  15. package/dist/{QYQCAXCB.js → 73I7CBP3.js} +3 -3
  16. package/dist/{QYQCAXCB.js.map → 73I7CBP3.js.map} +1 -1
  17. package/dist/{MDEOBKC3.js → 7GI6QGS6.js} +3 -3
  18. package/dist/{MDEOBKC3.js.map → 7GI6QGS6.js.map} +1 -1
  19. package/dist/{IOTRKLGP.js → 7STDATVZ.js} +3 -3
  20. package/dist/{IOTRKLGP.js.map → 7STDATVZ.js.map} +1 -1
  21. package/dist/{T4AL4FJH.js → ALC4M7WK.js} +3 -3
  22. package/dist/{T4AL4FJH.js.map → ALC4M7WK.js.map} +1 -1
  23. package/dist/{TWUSN2QY.js → AMLJTQWB.js} +3 -3
  24. package/dist/{TWUSN2QY.js.map → AMLJTQWB.js.map} +1 -1
  25. package/dist/{YCNGW63C.js → BL22CZFA.js} +3 -3
  26. package/dist/{YCNGW63C.js.map → BL22CZFA.js.map} +1 -1
  27. package/dist/{ZN2CNFMH.js → BLWJ63ZZ.js} +3 -3
  28. package/dist/{ZN2CNFMH.js.map → BLWJ63ZZ.js.map} +1 -1
  29. package/dist/CJ2WHLYC.js +116 -0
  30. package/dist/CJ2WHLYC.js.map +1 -0
  31. package/dist/{4AKFOZFY.js → CLAE73FR.js} +3 -3
  32. package/dist/{4AKFOZFY.js.map → CLAE73FR.js.map} +1 -1
  33. package/dist/CMDFOA7C.js +47 -0
  34. package/dist/CMDFOA7C.js.map +1 -0
  35. package/dist/CXTOAS7N.js +18 -0
  36. package/dist/CXTOAS7N.js.map +1 -0
  37. package/dist/{NX67V54F.js → DAP5XJGH.js} +3 -3
  38. package/dist/{NX67V54F.js.map → DAP5XJGH.js.map} +1 -1
  39. package/dist/{PSB6BQFH.js → EYLCZMM4.js} +5 -4
  40. package/dist/EYLCZMM4.js.map +1 -0
  41. package/dist/FPRRDKT5.js +52 -0
  42. package/dist/FPRRDKT5.js.map +1 -0
  43. package/dist/{KJRFPBY4.js → FXR2Z54G.js} +3 -3
  44. package/dist/{KJRFPBY4.js.map → FXR2Z54G.js.map} +1 -1
  45. package/dist/{HNZRSDTR.js → GOPXFEBJ.js} +11 -46
  46. package/dist/GOPXFEBJ.js.map +1 -0
  47. package/dist/{MJCI4XZ2.js → HLWLB4AY.js} +3 -3
  48. package/dist/{MJCI4XZ2.js.map → HLWLB4AY.js.map} +1 -1
  49. package/dist/{XSEEW6TB.js → HRZTWWO4.js} +3 -3
  50. package/dist/{XSEEW6TB.js.map → HRZTWWO4.js.map} +1 -1
  51. package/dist/{QHEXOE3Y.js → JI3XNGXO.js} +3 -27
  52. package/dist/JI3XNGXO.js.map +1 -0
  53. package/dist/{EPHLTMJZ.js → JSG7MGHH.js} +3 -3
  54. package/dist/{EPHLTMJZ.js.map → JSG7MGHH.js.map} +1 -1
  55. package/dist/{JH2I4JGJ.js → JXKVPYCE.js} +3 -3
  56. package/dist/{JH2I4JGJ.js.map → JXKVPYCE.js.map} +1 -1
  57. package/dist/{4VLNRPYZ.js → K2SFNQJC.js} +5 -5
  58. package/dist/{4VLNRPYZ.js.map → K2SFNQJC.js.map} +1 -1
  59. package/dist/{H7FW24YI.js → KBZ6YXX4.js} +3 -3
  60. package/dist/{H7FW24YI.js.map → KBZ6YXX4.js.map} +1 -1
  61. package/dist/{ZF6ASBPX.js → KH7RP6Q4.js} +3 -3
  62. package/dist/{ZF6ASBPX.js.map → KH7RP6Q4.js.map} +1 -1
  63. package/dist/{AV2FN74T.js → KTNEUXUI.js} +4 -4
  64. package/dist/{AV2FN74T.js.map → KTNEUXUI.js.map} +1 -1
  65. package/dist/{D3AV6J7R.js → LL7Z56UN.js} +3 -3
  66. package/dist/{D3AV6J7R.js.map → LL7Z56UN.js.map} +1 -1
  67. package/dist/{AYAFFPBV.js → MFVQ6EHM.js} +3 -3
  68. package/dist/{AYAFFPBV.js.map → MFVQ6EHM.js.map} +1 -1
  69. package/dist/{U7ISOK6B.js → NKXEPWZV.js} +3 -3
  70. package/dist/{U7ISOK6B.js.map → NKXEPWZV.js.map} +1 -1
  71. package/dist/{P3A2XIFY.js → NS763D57.js} +3 -3
  72. package/dist/{P3A2XIFY.js.map → NS763D57.js.map} +1 -1
  73. package/dist/{3UD2GTXW.js → PGQCNYFD.js} +3 -3
  74. package/dist/{3UD2GTXW.js.map → PGQCNYFD.js.map} +1 -1
  75. package/dist/{2UWZKS5E.js → Q3RL6UXD.js} +4 -4
  76. package/dist/{2UWZKS5E.js.map → Q3RL6UXD.js.map} +1 -1
  77. package/dist/{5KFMHJZB.js → QYQWHMQM.js} +3 -3
  78. package/dist/{5KFMHJZB.js.map → QYQWHMQM.js.map} +1 -1
  79. package/dist/{BJXKGZ5Y.js → QZJONP6W.js} +3 -3
  80. package/dist/{BJXKGZ5Y.js.map → QZJONP6W.js.map} +1 -1
  81. package/dist/{X66QZBHT.js → SLTAV2JF.js} +9 -2
  82. package/dist/SLTAV2JF.js.map +1 -0
  83. package/dist/{JV2DWUBW.js → SMLSAWCA.js} +48 -4
  84. package/dist/SMLSAWCA.js.map +1 -0
  85. package/dist/{RQXQCK22.js → UBHUOSK3.js} +3 -3
  86. package/dist/{RQXQCK22.js.map → UBHUOSK3.js.map} +1 -1
  87. package/dist/{OMRF3GH2.js → UO7AO4TN.js} +3 -3
  88. package/dist/{OMRF3GH2.js.map → UO7AO4TN.js.map} +1 -1
  89. package/dist/{R26MH4UH.js → UQMKPY2S.js} +3 -3
  90. package/dist/{R26MH4UH.js.map → UQMKPY2S.js.map} +1 -1
  91. package/dist/{HU7X7G5L.js → WSLU3JGO.js} +3 -3
  92. package/dist/{HU7X7G5L.js.map → WSLU3JGO.js.map} +1 -1
  93. package/dist/{WIQFSNER.js → XINHRIWA.js} +3 -3
  94. package/dist/{WIQFSNER.js.map → XINHRIWA.js.map} +1 -1
  95. package/dist/{PHVMGN7T.js → ZGWBZC76.js} +3 -3
  96. package/dist/{PHVMGN7T.js.map → ZGWBZC76.js.map} +1 -1
  97. package/dist/{AE56UC5I.js → ZPOY5WJ2.js} +3 -3
  98. package/dist/{AE56UC5I.js.map → ZPOY5WJ2.js.map} +1 -1
  99. package/dist/amulet.d.ts +7 -0
  100. package/dist/amulet.d.ts.map +1 -0
  101. package/dist/amulet.js +4 -0
  102. package/dist/amulet.js.map +1 -0
  103. package/dist/auth/index.js +1 -1
  104. package/dist/client/canton-client.d.ts +12 -0
  105. package/dist/client/canton-client.d.ts.map +1 -1
  106. package/dist/client/canton-client.js +138 -138
  107. package/dist/client/index.js +140 -139
  108. package/dist/client/interface.d.ts +12 -0
  109. package/dist/client/interface.d.ts.map +1 -1
  110. package/dist/client/utils.d.ts.map +1 -1
  111. package/dist/client/utils.js +2 -1
  112. package/dist/crypto.d.ts +23 -0
  113. package/dist/crypto.d.ts.map +1 -0
  114. package/dist/crypto.js +4 -0
  115. package/dist/crypto.js.map +1 -0
  116. package/dist/daml-serde.d.ts +73 -0
  117. package/dist/daml-serde.d.ts.map +1 -0
  118. package/dist/daml-serde.js +4 -0
  119. package/dist/daml-serde.js.map +1 -0
  120. package/dist/events.d.ts +9 -36
  121. package/dist/events.d.ts.map +1 -1
  122. package/dist/events.js +2 -2
  123. package/dist/index.d.ts +3 -0
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +150 -147
  126. package/dist/open-api/apis/DefaultApi.js +137 -137
  127. package/dist/open-api/apis/index.js +137 -137
  128. package/dist/open-api/index.js +137 -137
  129. package/dist/open-api/models/Command.js +3 -3
  130. package/dist/open-api/models/Command1.js +2 -2
  131. package/dist/open-api/models/Completion.js +3 -3
  132. package/dist/open-api/models/Completion1.js +3 -3
  133. package/dist/open-api/models/CompletionResponse.js +7 -7
  134. package/dist/open-api/models/CompletionResponseOneOf.js +3 -3
  135. package/dist/open-api/models/CompletionStreamResponse.js +7 -7
  136. package/dist/open-api/models/CreateUserRequest.js +10 -10
  137. package/dist/open-api/models/CumulativeFilter.js +2 -2
  138. package/dist/open-api/models/DeduplicationPeriod.js +1 -1
  139. package/dist/open-api/models/DeduplicationPeriod1.js +1 -1
  140. package/dist/open-api/models/DeduplicationPeriod2.js +3 -3
  141. package/dist/open-api/models/EventFormat.js +2 -2
  142. package/dist/open-api/models/FeaturesDescriptor.js +2 -2
  143. package/dist/open-api/models/Filters.js +2 -2
  144. package/dist/open-api/models/GetActiveContractsRequest.js +2 -2
  145. package/dist/open-api/models/GetEventsByContractIdRequest.js +2 -2
  146. package/dist/open-api/models/GetLedgerApiVersionResponse.js +3 -3
  147. package/dist/open-api/models/GetTransactionByIdRequest.js +2 -2
  148. package/dist/open-api/models/GetTransactionByOffsetRequest.js +2 -2
  149. package/dist/open-api/models/GetUpdateByIdRequest.js +2 -2
  150. package/dist/open-api/models/GetUpdateByOffsetRequest.js +2 -2
  151. package/dist/open-api/models/GetUpdatesRequest.js +2 -2
  152. package/dist/open-api/models/GrantUserRightsRequest.js +10 -10
  153. package/dist/open-api/models/GrantUserRightsResponse.js +10 -10
  154. package/dist/open-api/models/IdentifierFilter.js +2 -2
  155. package/dist/open-api/models/JsCommands.js +5 -5
  156. package/dist/open-api/models/JsContractEntry.js +3 -3
  157. package/dist/open-api/models/JsExecuteSubmissionAndWaitForTransactionRequest.js +7 -7
  158. package/dist/open-api/models/JsExecuteSubmissionAndWaitRequest.js +5 -5
  159. package/dist/open-api/models/JsExecuteSubmissionRequest.js +5 -5
  160. package/dist/open-api/models/JsGetActiveContractsResponse.js +4 -4
  161. package/dist/open-api/models/JsGetUpdateResponse.js +13 -13
  162. package/dist/open-api/models/JsGetUpdateTreesResponse.js +5 -5
  163. package/dist/open-api/models/JsGetUpdatesResponse.js +13 -13
  164. package/dist/open-api/models/JsPrepareSubmissionRequest.js +5 -5
  165. package/dist/open-api/models/JsReassignment.js +1 -1
  166. package/dist/open-api/models/JsReassignmentEvent.js +1 -1
  167. package/dist/open-api/models/JsSubmitAndWaitForReassignmentResponse.js +1 -1
  168. package/dist/open-api/models/JsSubmitAndWaitForTransactionRequest.js +9 -9
  169. package/dist/open-api/models/JsTopologyTransaction.js +6 -6
  170. package/dist/open-api/models/Kind.js +8 -8
  171. package/dist/open-api/models/ListUserRightsResponse.js +10 -10
  172. package/dist/open-api/models/Operation.js +4 -4
  173. package/dist/open-api/models/Reassignment.js +1 -1
  174. package/dist/open-api/models/Reassignment1.js +1 -1
  175. package/dist/open-api/models/ReassignmentCommand.js +3 -3
  176. package/dist/open-api/models/ReassignmentCommands.js +4 -4
  177. package/dist/open-api/models/RevokeUserRightsRequest.js +10 -10
  178. package/dist/open-api/models/RevokeUserRightsResponse.js +10 -10
  179. package/dist/open-api/models/Right.js +9 -9
  180. package/dist/open-api/models/SubmitAndWaitForReassignmentRequest.js +7 -7
  181. package/dist/open-api/models/SubmitReassignmentRequest.js +5 -5
  182. package/dist/open-api/models/TopologyEvent.js +5 -5
  183. package/dist/open-api/models/TopologyEventEvent.js +4 -4
  184. package/dist/open-api/models/TopologyTransaction.js +7 -7
  185. package/dist/open-api/models/TransactionFilter.js +2 -2
  186. package/dist/open-api/models/TransactionFormat.js +2 -2
  187. package/dist/open-api/models/Update.js +12 -12
  188. package/dist/open-api/models/Update1.js +4 -4
  189. package/dist/open-api/models/Update1OneOf1.js +1 -1
  190. package/dist/open-api/models/UpdateFormat.js +2 -2
  191. package/dist/open-api/models/UpdateOneOf1.js +1 -1
  192. package/dist/open-api/models/UpdateOneOf2.js +8 -8
  193. package/dist/open-api/models/UpdateVettedPackagesRequest.js +6 -6
  194. package/dist/open-api/models/VettedPackagesChange.js +5 -5
  195. package/dist/open-api/models/index.js +136 -136
  196. package/dist/provider.d.ts +9 -27
  197. package/dist/provider.d.ts.map +1 -1
  198. package/dist/provider.js +140 -140
  199. package/dist/sdk/canton-sdk.d.ts +7 -0
  200. package/dist/sdk/canton-sdk.d.ts.map +1 -1
  201. package/dist/sdk/canton-sdk.js +1 -1
  202. package/dist/sdk/index.js +1 -1
  203. package/dist/transaction.d.ts +64 -6
  204. package/dist/transaction.d.ts.map +1 -1
  205. package/dist/transaction.js +1 -1
  206. package/package.json +13 -10
  207. package/src/amulet.ts +28 -0
  208. package/src/client/canton-client.ts +60 -1
  209. package/src/client/interface.ts +16 -0
  210. package/src/client/utils.ts +1 -6
  211. package/src/crypto.ts +42 -0
  212. package/src/daml-serde.ts +182 -0
  213. package/src/events.ts +31 -155
  214. package/src/index.ts +3 -0
  215. package/src/provider.ts +12 -82
  216. package/src/sdk/canton-sdk.ts +13 -0
  217. package/src/transaction.ts +4 -0
  218. package/test/crypto.test.ts +52 -0
  219. package/test/daml-serde.test.ts +140 -0
  220. package/test/mocks/scripts/capture-disclosure.ts +5 -2
  221. package/test/mocks/scripts/capture-external-requests.ts +5 -2
  222. package/test/mocks/scripts/capture-unhappy-paths.ts +5 -2
  223. package/test/provider.test.ts +47 -36
  224. package/dist/GXJEEYPP.js +0 -104
  225. package/dist/GXJEEYPP.js.map +0 -1
  226. package/dist/HNZRSDTR.js.map +0 -1
  227. package/dist/JV2DWUBW.js.map +0 -1
  228. package/dist/PSB6BQFH.js.map +0 -1
  229. package/dist/QHEXOE3Y.js.map +0 -1
  230. package/dist/X66QZBHT.js.map +0 -1
package/src/events.ts CHANGED
@@ -1,37 +1,8 @@
1
- import { buildTransactionWithResultSchema } from '@layerzerolabs/common-chain-model';
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 { cantonTransactionSchema } from './transaction';
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 = cantonTxWithResultSchema.safeParse(tx);
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
- * Success check for register-style deploys (an exercise on Uln302 that creates a
97
- * Request, which the validator then accepts or rejects in a later transaction).
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
- for (let attempt = 1; attempt <= REQUEST_ARCHIVAL_MAX_ATTEMPTS; attempt++) {
146
- const { archived } = await client.fetchEventsByContractId(contractId, parties);
147
- if (archived) {
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 NonRetryableError(
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
- /** Name of the consuming exercise on `contractId` in a transaction tree, if any. */
223
- const getConsumingChoice = (
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
- ): string | undefined => {
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 && exercised.consuming) {
231
- return exercised.choice;
106
+ if (exercised.contractId === contractId) {
107
+ return exercised;
232
108
  }
233
109
  }
234
110
  return undefined;
package/src/index.ts CHANGED
@@ -1,4 +1,7 @@
1
+ export * from './amulet';
1
2
  export * from './auth';
3
+ export * from './crypto';
4
+ export * from './daml-serde';
2
5
  export * from './deferredValue';
3
6
  export * from './events';
4
7
  export * from './provider';
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 CantonProvider = {
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 CantonUriParams = {
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 parseCantonUri(uri: string): CantonUriParams {
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 CreateCantonProviderOptions = {
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 CantonProvider} from a provider URI.
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 createCantonProvider({
126
+ export function createCantonChainProvider({
195
127
  uri,
196
128
  isLocal,
197
- }: CreateCantonProviderOptions): CantonProvider {
198
- const params = parseCantonUri(uri);
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: CantonUriParams, isLocal: boolean): TokenProvider {
155
+ function createTokenProvider(params: CantonChainUriParams, isLocal: boolean): TokenProvider {
226
156
  if (isLocal) {
227
157
  return new LocalTokenProvider({
228
158
  userId: params.userId,
@@ -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.
@@ -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 { MnemonicSignerAdapter } from '../../../../signer-adapter/src/mnemonicSignerAdapter.js';
23
- import { SignatureFormat, SignatureType } from '../../../../signer-adapter/src/model.js';
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';