@arkiv-network/sdk 0.7.0 → 0.8.0-advanced.2

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 (237) hide show
  1. package/README.md +71 -16
  2. package/dist/actions/advanced/index.cjs +13 -0
  3. package/dist/actions/advanced/index.d.cts +8 -0
  4. package/dist/actions/advanced/index.d.ts +9 -0
  5. package/dist/actions/advanced/index.js +9 -0
  6. package/dist/advanced-8ZPTmmaN.js +598 -0
  7. package/dist/advanced-8ZPTmmaN.js.map +1 -0
  8. package/dist/advanced-fPImL6lW.cjs +656 -0
  9. package/dist/arkivTransactions-D74gERHG.d.cts +571 -0
  10. package/dist/arkivTransactions-D74gERHG.d.cts.map +1 -0
  11. package/dist/arkivTransactions-NtGD6uk5.d.ts +571 -0
  12. package/dist/arkivTransactions-NtGD6uk5.d.ts.map +1 -0
  13. package/dist/attr/index.cjs +35 -0
  14. package/dist/attr/index.d.cts +2 -0
  15. package/dist/attr/index.d.ts +3 -0
  16. package/dist/attr/index.js +4 -0
  17. package/dist/attributes-BhY5DwJp.js +809 -0
  18. package/dist/attributes-BhY5DwJp.js.map +1 -0
  19. package/dist/attributes-Df9IsCVs.cjs +1029 -0
  20. package/dist/chains/index.cjs +2 -3
  21. package/dist/chains/index.d.cts +39 -77
  22. package/dist/chains/index.d.cts.map +1 -1
  23. package/dist/chains/index.d.ts +22 -60
  24. package/dist/chains/index.d.ts.map +1 -1
  25. package/dist/chains/index.js +2 -2
  26. package/dist/chains-BJrZs6sG.js +57 -0
  27. package/dist/chains-BJrZs6sG.js.map +1 -0
  28. package/dist/chains-BYRTTipa.cjs +73 -0
  29. package/dist/codec-Car3KkFF.d.ts +102 -0
  30. package/dist/codec-Car3KkFF.d.ts.map +1 -0
  31. package/dist/entity/index.cjs +20 -0
  32. package/dist/entity/index.d.cts +4 -0
  33. package/dist/entity/index.d.ts +5 -0
  34. package/dist/entity/index.js +7 -0
  35. package/dist/entity-B00Li04j.cjs +71 -0
  36. package/dist/entity-B7G8q_Eo.cjs +0 -0
  37. package/dist/entity-BYQR1uoe.js +67 -0
  38. package/dist/entity-BYQR1uoe.js.map +1 -0
  39. package/dist/entity-CD_qFF2M.js +0 -0
  40. package/dist/errors-BVbjKVMN.cjs +56 -0
  41. package/dist/errors-LTe7bOYS.js +33 -0
  42. package/dist/errors-LTe7bOYS.js.map +1 -0
  43. package/dist/expiry-BELY2Kpw.cjs +135 -0
  44. package/dist/expiry-BjpxZZQU.js +95 -0
  45. package/dist/expiry-BjpxZZQU.js.map +1 -0
  46. package/dist/expiry-DRV29F1r.d.cts +79 -0
  47. package/dist/expiry-DRV29F1r.d.cts.map +1 -0
  48. package/dist/expiry-nDbCgZX4.d.ts +79 -0
  49. package/dist/expiry-nDbCgZX4.d.ts.map +1 -0
  50. package/dist/flags-5dTNRnow.d.ts +41 -0
  51. package/dist/flags-5dTNRnow.d.ts.map +1 -0
  52. package/dist/flags-CRjPpoh3.d.cts +41 -0
  53. package/dist/flags-CRjPpoh3.d.cts.map +1 -0
  54. package/dist/flags-DFBs2J94.js +33 -0
  55. package/dist/flags-DFBs2J94.js.map +1 -0
  56. package/dist/flags-kr276q6i.cjs +43 -0
  57. package/dist/index-B5j76711.d.ts +184 -0
  58. package/dist/index-B5j76711.d.ts.map +1 -0
  59. package/dist/index-BF7Mq9Is.d.cts +280 -0
  60. package/dist/index-BF7Mq9Is.d.cts.map +1 -0
  61. package/dist/index-CA4V7Pdh.d.cts +167 -0
  62. package/dist/index-CA4V7Pdh.d.cts.map +1 -0
  63. package/dist/index-CV5zCdpa.d.cts +417 -0
  64. package/dist/index-CV5zCdpa.d.cts.map +1 -0
  65. package/dist/index-DHDud2hC.d.ts +280 -0
  66. package/dist/index-DHDud2hC.d.ts.map +1 -0
  67. package/dist/index-DvYvrkdk.d.ts +320 -0
  68. package/dist/index-DvYvrkdk.d.ts.map +1 -0
  69. package/dist/index-EeUO0nnJ.d.cts +184 -0
  70. package/dist/index-EeUO0nnJ.d.cts.map +1 -0
  71. package/dist/index-fX_XyqLp.d.ts +167 -0
  72. package/dist/index-fX_XyqLp.d.ts.map +1 -0
  73. package/dist/index.cjs +422 -399
  74. package/dist/index.d.cts +364 -204
  75. package/dist/index.d.cts.map +1 -1
  76. package/dist/index.d.ts +365 -204
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +357 -389
  79. package/dist/index.js.map +1 -1
  80. package/dist/key-D1Bh7MF0.js +70 -0
  81. package/dist/key-D1Bh7MF0.js.map +1 -0
  82. package/dist/key-WKhXnXuU.cjs +98 -0
  83. package/dist/logger-CVpvU8ik.js +153 -0
  84. package/dist/logger-CVpvU8ik.js.map +1 -0
  85. package/dist/logger-Cr15lPAQ.cjs +188 -0
  86. package/dist/payload-BNoud2KZ.d.cts +94 -0
  87. package/dist/payload-BNoud2KZ.d.cts.map +1 -0
  88. package/dist/payload-Bz6651ax.js +67 -0
  89. package/dist/payload-Bz6651ax.js.map +1 -0
  90. package/dist/payload-CcUj85ys.d.ts +94 -0
  91. package/dist/payload-CcUj85ys.d.ts.map +1 -0
  92. package/dist/payload-KC14SDlN.cjs +83 -0
  93. package/dist/query/index.cjs +26 -149
  94. package/dist/query/index.d.cts +5 -3
  95. package/dist/query/index.d.ts +6 -3
  96. package/dist/query/index.js +8 -137
  97. package/dist/queryBuilder-DM6AWCBa.js +820 -0
  98. package/dist/queryBuilder-DM6AWCBa.js.map +1 -0
  99. package/dist/queryBuilder-DbUMEZln.cjs +944 -0
  100. package/dist/selection-C4UhsMOO.d.ts +434 -0
  101. package/dist/selection-C4UhsMOO.d.ts.map +1 -0
  102. package/dist/selection-DPg0uyJf.d.cts +433 -0
  103. package/dist/selection-DPg0uyJf.d.cts.map +1 -0
  104. package/dist/types/index.cjs +4 -1
  105. package/dist/types/index.d.cts +7 -2
  106. package/dist/types/index.d.ts +8 -2
  107. package/dist/types/index.js +4 -1
  108. package/dist/types-Bu_ZwMbZ.js +0 -0
  109. package/dist/types-NRqymi6S.cjs +0 -0
  110. package/dist/utils/index.cjs +4 -17
  111. package/dist/utils/index.d.cts +3 -17
  112. package/dist/utils/index.d.ts +3 -17
  113. package/dist/utils/index.js +4 -18
  114. package/package.json +26 -5
  115. package/src/actions/advanced/advanced.test.ts +258 -0
  116. package/src/actions/advanced/buildMutation.ts +104 -0
  117. package/src/actions/advanced/getMutationResult.ts +112 -0
  118. package/src/actions/advanced/index.ts +21 -0
  119. package/src/actions/advanced/pingTransaction.ts +43 -0
  120. package/src/actions/advanced/sendMutation.ts +59 -0
  121. package/src/actions/public/getEntity.ts +27 -43
  122. package/src/actions/public/getEntityNonce.ts +30 -0
  123. package/src/actions/public/predictEntityKeys.test.ts +143 -0
  124. package/src/actions/public/predictEntityKeys.ts +138 -0
  125. package/src/actions/public/query.ts +35 -67
  126. package/src/actions/public/watchEntityEvents.test.ts +414 -0
  127. package/src/actions/public/watchEntityEvents.ts +225 -0
  128. package/src/actions/wallet/changeOwnership.ts +8 -8
  129. package/src/actions/wallet/createEntity.ts +104 -26
  130. package/src/actions/wallet/deleteEntity.ts +2 -3
  131. package/src/actions/wallet/extendEntity.ts +39 -16
  132. package/src/actions/wallet/mutateEntities.test.ts +76 -0
  133. package/src/actions/wallet/mutateEntities.ts +23 -83
  134. package/src/actions/wallet/patchEntity.ts +90 -0
  135. package/src/attr/attributes.test.ts +120 -0
  136. package/src/attr/attributes.ts +190 -0
  137. package/src/attr/codec.test.ts +162 -0
  138. package/src/attr/codec.ts +220 -0
  139. package/src/attr/decimal.ts +86 -0
  140. package/src/attr/errors.ts +145 -0
  141. package/src/attr/index.ts +50 -0
  142. package/src/attr/names.ts +106 -0
  143. package/src/attr/types.ts +150 -0
  144. package/src/attr/values.test.ts +271 -0
  145. package/src/attr/values.ts +502 -0
  146. package/src/chains/cheesecake.ts +22 -0
  147. package/src/chains/index.ts +1 -2
  148. package/src/clients/createPublicClient.test.ts +3 -5
  149. package/src/clients/createPublicClient.ts +5 -4
  150. package/src/clients/createWalletClient.test.ts +2 -2
  151. package/src/clients/createWalletClient.ts +13 -7
  152. package/src/clients/decorators/arkivAdvanced.ts +144 -0
  153. package/src/clients/decorators/arkivPublic.ts +172 -139
  154. package/src/clients/decorators/arkivWallet.ts +106 -95
  155. package/src/consts.ts +2 -1
  156. package/src/entity/create.test.ts +184 -0
  157. package/src/entity/errors.ts +38 -0
  158. package/src/entity/events.ts +16 -0
  159. package/src/entity/expiry.test.ts +195 -0
  160. package/src/entity/expiry.ts +181 -0
  161. package/src/entity/flags.ts +65 -0
  162. package/src/entity/index.ts +15 -0
  163. package/src/entity/key.ts +68 -0
  164. package/src/entity/operations.ts +134 -0
  165. package/src/entity/params.ts +15 -0
  166. package/src/errors.ts +33 -28
  167. package/src/index.ts +19 -0
  168. package/src/query/engine.ts +68 -132
  169. package/src/query/errors.ts +151 -0
  170. package/src/query/expression.test.ts +286 -0
  171. package/src/query/expression.ts +493 -0
  172. package/src/query/index.ts +40 -4
  173. package/src/query/injection.test.ts +151 -0
  174. package/src/query/queryBuilder.test.ts +342 -637
  175. package/src/query/queryBuilder.ts +133 -359
  176. package/src/query/queryResult.ts +54 -51
  177. package/src/query/selection.test.ts +48 -98
  178. package/src/query/selection.ts +77 -65
  179. package/src/types/entity.test.ts +44 -155
  180. package/src/types/entity.ts +62 -44
  181. package/src/types/events.ts +64 -23
  182. package/src/types/index.ts +20 -16
  183. package/src/types/rpcSchema.ts +70 -33
  184. package/src/utils/arkivTransactions.test.ts +681 -38
  185. package/src/utils/arkivTransactions.ts +400 -115
  186. package/src/utils/chains.test.ts +21 -24
  187. package/src/utils/compression.ts +1 -1
  188. package/src/utils/entities.test.ts +62 -0
  189. package/src/utils/entities.ts +111 -29
  190. package/src/utils/expirationTime.ts +114 -10
  191. package/src/utils/logger.ts +2 -2
  192. package/src/utils/payload.ts +4 -2
  193. package/src/utils/revert.test.ts +99 -0
  194. package/src/utils/revert.ts +145 -0
  195. package/dist/chains-BifaY04V.cjs +0 -108
  196. package/dist/chains-CrtkVbBh.js +0 -86
  197. package/dist/chains-CrtkVbBh.js.map +0 -1
  198. package/dist/consts-AUoL-v42.cjs +0 -18
  199. package/dist/consts-BV4PR-de.js +0 -7
  200. package/dist/consts-BV4PR-de.js.map +0 -1
  201. package/dist/entity-B6g_EPm1.d.cts +0 -382
  202. package/dist/entity-B6g_EPm1.d.cts.map +0 -1
  203. package/dist/entity-qoLMAuuk.d.ts +0 -382
  204. package/dist/entity-qoLMAuuk.d.ts.map +0 -1
  205. package/dist/index-DdspxNAA.d.ts +0 -481
  206. package/dist/index-DdspxNAA.d.ts.map +0 -1
  207. package/dist/index-Ds9dwqb5.d.cts +0 -481
  208. package/dist/index-Ds9dwqb5.d.cts.map +0 -1
  209. package/dist/payload-B2kFJyah.d.ts +0 -6
  210. package/dist/payload-B2kFJyah.d.ts.map +0 -1
  211. package/dist/payload-CJN8fTvX.d.cts +0 -6
  212. package/dist/payload-CJN8fTvX.d.cts.map +0 -1
  213. package/dist/payload-Claf2jUO.js +0 -13
  214. package/dist/payload-Claf2jUO.js.map +0 -1
  215. package/dist/payload-DpA5PA-w.cjs +0 -23
  216. package/dist/query/index.js.map +0 -1
  217. package/dist/queryBuilder-D2uwP9jI.js +0 -704
  218. package/dist/queryBuilder-D2uwP9jI.js.map +0 -1
  219. package/dist/queryBuilder-UW2MCaX4.cjs +0 -792
  220. package/dist/utils/index.d.cts.map +0 -1
  221. package/dist/utils/index.d.ts.map +0 -1
  222. package/dist/utils/index.js.map +0 -1
  223. package/src/actions/public/buildQuery.ts +0 -10
  224. package/src/actions/public/subscribeEntityEvents.ts +0 -119
  225. package/src/actions/wallet/updateEntity.ts +0 -60
  226. package/src/chains/braga.ts +0 -26
  227. package/src/chains/kaolin.ts +0 -29
  228. package/src/query/engine.test.ts +0 -526
  229. package/src/query/predicate.test.ts +0 -461
  230. package/src/query/predicate.ts +0 -144
  231. package/src/query/queryResult.test.ts +0 -270
  232. package/src/query/selectQueryBuilder.test.ts +0 -210
  233. package/src/types/attributes.ts +0 -16
  234. package/src/utils/validation.test.ts +0 -60
  235. package/src/utils/validation.ts +0 -32
  236. /package/dist/{types-H-TSmRpm.js → attr-DlmErkvR.cjs} +0 -0
  237. /package/dist/{types-o3zWarRp.cjs → attr-GcQKatub.js} +0 -0
@@ -24,18 +24,19 @@ import type {
24
24
  MutateEntitiesReturnType,
25
25
  } from "../../actions/wallet/mutateEntities"
26
26
  import { mutateEntities } from "../../actions/wallet/mutateEntities"
27
- import type {
28
- UpdateEntityParameters,
29
- UpdateEntityReturnType,
30
- } from "../../actions/wallet/updateEntity"
31
- import { updateEntity } from "../../actions/wallet/updateEntity"
27
+ import type { PatchEntityParameters, PatchEntityReturnType } from "../../actions/wallet/patchEntity"
28
+ import { patchEntity } from "../../actions/wallet/patchEntity"
32
29
  import type { TxParams } from "../../types"
30
+ import { type WalletAdvancedActions, walletAdvancedActions } from "./arkivAdvanced"
33
31
 
34
32
  export type WalletArkivActions<
35
33
  transport extends Transport = Transport,
36
34
  chain extends Chain | undefined = Chain | undefined,
37
35
  account extends Account | undefined = Account | undefined,
38
- > = Pick<PublicActions<transport, chain, account>, "waitForTransactionReceipt" | "call"> &
36
+ > = Pick<
37
+ PublicActions<transport, chain, account>,
38
+ "waitForTransactionReceipt" | "call" | "simulateContract" | "readContract" | "getBlockNumber"
39
+ > &
39
40
  Pick<
40
41
  WalletActions<chain, account>,
41
42
  | "addChain"
@@ -45,6 +46,7 @@ export type WalletArkivActions<
45
46
  | "sendRawTransaction"
46
47
  | "signMessage"
47
48
  | "signTransaction"
49
+ | "writeContract"
48
50
  > & {
49
51
  /**
50
52
  * Creates a new entity.
@@ -56,30 +58,27 @@ export type WalletArkivActions<
56
58
  * @param txParams - Optional transaction parameters
57
59
  * @returns The created entity with transaction hash
58
60
  *
59
- * @throws {InvalidExpirationError} If `expiresIn` is not a positive integer
60
- * that is a multiple of the block time (2 seconds).
61
- * @throws {InvalidAttributeError} If a numeric attribute value is not an
62
- * integer.
61
+ * @throws {InvalidExpiryError} If the expiry exceeds a protocol bound or would leave the
62
+ * entity dead on arrival.
63
+ * @throws {InvalidValueError} If an attribute value does not fit the type it names.
64
+ * @throws {InvalidAttributeNameError} If a name violates the attribute-name grammar.
63
65
  *
64
66
  * @example
65
- * import { createPublicClient, http } from 'arkiv'
66
- * import { braga } from 'arkiv/chains'
67
+ * import { createWalletClient, ExpirationTime, jsonToPayload } from "@arkiv-network/sdk"
68
+ * import { i32 } from "@arkiv-network/sdk/attr"
69
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
70
+ * import { http } from "viem"
67
71
  *
68
- * const client = createPublicClient({
69
- * chain: braga,
72
+ * const client = createWalletClient({
73
+ * chain: cheesecake,
70
74
  * transport: http(),
71
75
  * })
72
- * const { entityKey, txHash } = await client.createEntity({
73
- * payload: toBytes(JSON.stringify({ entity: { entityType: "testType", entityId: "testId" } })),
74
- * attributes: [{ key: "testKey", value: "testValue" }],
75
- * expiresIn: 1000,
76
+ * const { entityKey, txHash, expiresAt } = await client.createEntity({
77
+ * payload: jsonToPayload({ entityType: "testType", entityId: "testId" }),
78
+ * contentType: "application/json",
79
+ * attributes: { testKey: "testValue", level: i32(3) },
80
+ * expires: ExpirationTime.fromDays(30),
76
81
  * })
77
- * console.log("entityKey", entityKey)
78
- * console.log("txHash", txHash)
79
- * // {
80
- * // entityKey: "0x123",
81
- * // txHash: "0x123",
82
- * // }
83
82
  */
84
83
  createEntity: (
85
84
  data: CreateEntityParameters,
@@ -87,33 +86,43 @@ export type WalletArkivActions<
87
86
  ) => Promise<CreateEntityReturnType>
88
87
 
89
88
  /**
90
- * Updates the entity with the given key.
89
+ * Applies a patch to the entity with the given key: sets some fields, unsets others, and leaves
90
+ * everything it does not name alone.
91
91
  *
92
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/updateEntity
92
+ * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/patchEntity
93
93
  * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
94
94
  *
95
- * @param data - The entity update parameters
95
+ * @param data - The entity key and the mutations to apply
96
96
  * @param txParams - Optional transaction parameters
97
- * @returns The updated entity with transaction hash
97
+ * @returns The entity key and the transaction hash
98
98
  *
99
- * @throws {InvalidExpirationError} If `expiresIn` is not a positive integer
100
- * that is a multiple of the block time (2 seconds).
101
- * @throws {InvalidAttributeError} If a numeric attribute value is not an
102
- * integer.
99
+ * @throws {EmptyPatchError} If the patch has nothing to apply.
100
+ * @throws {ConflictingMutationError} If a name appears in both `set` and `unset`.
101
+ * @throws {InvalidValueError} If an attribute value does not fit the type it names.
102
+ * @throws {InvalidAttributeNameError} If a name violates the attribute-name grammar.
103
103
  *
104
104
  * @example
105
- * import { createWalletClient, http } from 'arkiv'
106
- * import { braga } from 'arkiv/chains'
105
+ * import { createWalletClient, jsonToPayload } from "@arkiv-network/sdk"
106
+ * import { i32 } from "@arkiv-network/sdk/attr"
107
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
108
+ * import { http } from "viem"
107
109
  *
108
110
  * const client = createWalletClient({
109
- * chain: braga,
111
+ * chain: cheesecake,
110
112
  * transport: http(),
111
113
  * })
114
+ * // Publish the entity: one attribute changes, one goes away, the payload is replaced.
115
+ * const { txHash } = await client.patchEntity({
116
+ * entityKey,
117
+ * set: { status: "published", revision: i32(2) },
118
+ * unset: ["draft"],
119
+ * payload: jsonToPayload({ title: "Hello" }),
120
+ * })
112
121
  */
113
- updateEntity: (
114
- data: UpdateEntityParameters,
122
+ patchEntity: (
123
+ data: PatchEntityParameters,
115
124
  txParams?: TxParams,
116
- ) => Promise<UpdateEntityReturnType>
125
+ ) => Promise<PatchEntityReturnType>
117
126
 
118
127
  /**
119
128
  * Deletes the entity with the given key.
@@ -126,20 +135,16 @@ export type WalletArkivActions<
126
135
  * @returns The deleted entity with transaction hash
127
136
  *
128
137
  * @example
129
- * import { createWalletClient, http } from 'arkiv'
130
- * import { braga } from 'arkiv/chains'
138
+ * import { createWalletClient } from "@arkiv-network/sdk"
139
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
140
+ * import { http } from "viem"
131
141
  *
132
142
  * const client = createWalletClient({
133
- * chain: braga,
143
+ * chain: cheesecake,
134
144
  * transport: http(),
135
145
  * })
136
- * const { entityKey, txHash } = await client.deleteEntity({ entityKey: "0x123" })
137
- * console.log("entityKey", entityKey)
138
- * console.log("txHash", txHash)
139
- * // {
140
- * // entityKey: "0x123",
141
- * // txHash: "0x123",
142
- * // }
146
+ * // entityKey is the bytes32 key returned by createEntity.
147
+ * const { txHash } = await client.deleteEntity({ entityKey })
143
148
  */
144
149
  deleteEntity: (
145
150
  data: DeleteEntityParameters,
@@ -147,35 +152,35 @@ export type WalletArkivActions<
147
152
  ) => Promise<DeleteEntityReturnType>
148
153
 
149
154
  /**
150
- * Extends the entity with the given key.
155
+ * Sets a new expiry on the entity with the given key.
156
+ *
157
+ * The new lifetime is resolved the same way a create's is: a duration counts from now rather
158
+ * than adding to what the entity has left, and `atBlock` / `atDate` pin an absolute deadline.
159
+ * The engine rejects an extension that would not move the expiry later.
151
160
  *
152
161
  * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/extendEntity
153
162
  * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
154
163
  *
155
- * @param data - The entity update parameters
164
+ * @param data - The entity key and its new lifetime
156
165
  * @param txParams - Optional transaction parameters
157
- * @returns The updated entity with transaction hash
166
+ * @returns The entity key, transaction hash, and the block it is now expected to expire at
158
167
  *
159
- * @throws {InvalidExpirationError} If `expiresIn` is not a positive integer
160
- * that is a multiple of the block time (2 seconds).
168
+ * @throws {InvalidExpiryError} If the expiry is malformed, exceeds a protocol bound, or would
169
+ * leave the entity dead on arrival.
161
170
  *
162
171
  * @example
163
- * import { createWalletClient, http } from 'arkiv'
164
- * import { braga } from 'arkiv/chains'
172
+ * import { createWalletClient, ExpirationTime } from "@arkiv-network/sdk"
173
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
174
+ * import { http } from "viem"
165
175
  *
166
176
  * const client = createWalletClient({
167
- * chain: braga,
177
+ * chain: cheesecake,
168
178
  * transport: http(),
169
179
  * })
170
- * const { entityKey, txHash } = await client.extendEntity("0x123", {
171
- * expiresIn: 1000,
180
+ * const { txHash, expiresAt } = await client.extendEntity({
181
+ * entityKey,
182
+ * expires: ExpirationTime.fromDays(30),
172
183
  * })
173
- * console.log("entityKey", entityKey)
174
- * console.log("txHash", txHash)
175
- * // {
176
- * // entityKey: "0x123",
177
- * // txHash: "0x123",
178
- * // }
179
184
  */
180
185
  extendEntity: (
181
186
  data: ExtendEntityParameters,
@@ -203,54 +208,59 @@ export type WalletArkivActions<
203
208
  * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/mutateEntities
204
209
  * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
205
210
  *
206
- * @param data - The mutation parameters (creates, updates, deletes, extensions)
211
+ * Every operation lands in one transaction, so the whole batch applies or none of it does.
212
+ * At least one operation is required.
213
+ *
214
+ * @param data - The mutation parameters (creates, patches, deletes, extensions, ownershipChanges)
207
215
  * @param txParams - Optional transaction parameters
208
216
  * @returns The mutation result with transaction hash
209
217
  *
210
- * @throws {InvalidExpirationError} If any create/update/extension `expiresIn`
211
- * is not a positive integer that is a multiple of the block time (2 seconds).
212
- * @throws {InvalidAttributeError} If a numeric attribute value is not an
213
- * integer.
218
+ * @throws {InvalidExpiryError} If an expiry exceeds a protocol bound or would leave the entity
219
+ * dead on arrival.
220
+ * @throws {InvalidValueError} If an attribute value does not fit the type it names.
214
221
  *
215
222
  * @example
216
- * import { createWalletClient, http } from 'arkiv'
217
- * import { braga } from 'arkiv/chains'
223
+ * import { createWalletClient, ExpirationTime, jsonToPayload } from "@arkiv-network/sdk"
224
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
225
+ * import { http } from "viem"
218
226
  *
219
227
  * const client = createWalletClient({
220
- * chain: braga,
228
+ * chain: cheesecake,
221
229
  * transport: http(),
222
230
  * })
223
- * const { entityKey, txHash } = await client.mutateEntities({
231
+ * const { txHash, createdEntities } = await client.mutateEntities({
224
232
  * creates: [{
225
- * payload: toBytes(JSON.stringify({ entity: { entityType: "testType", entityId: "testId" } })),
226
- * attriubutes: [{ key: "testKey", value: "testValue" }],
227
- * expiresIn: 1000,
228
- * }],
229
- * updates: [{
230
- * entityKey: "0x123",
231
- * payload: toBytes(JSON.stringify({ entity: { entityType: "testType", entityId: "testId" } })),
232
- * attributes: [{ key: "testKey", value: "testValue" }],
233
- * expiresIn: 1000,
234
- * }],
235
- * deletes: [{
236
- * entityKey: "0x321",
233
+ * payload: jsonToPayload({ entityType: "testType", entityId: "testId" }),
234
+ * contentType: "application/json",
235
+ * attributes: { testKey: "testValue" },
236
+ * expires: ExpirationTime.fromDays(30),
237
237
  * }],
238
+ * patches: [{ entityKey: keyToRevise, set: { status: "archived" }, unset: ["draft"] }],
239
+ * deletes: [{ entityKey: staleKey }],
238
240
  * extensions: [{
239
- * entityKey: "0x1234",
240
- * expiresIn: 1000,
241
+ * entityKey: keyToKeepAlive,
242
+ * expires: ExpirationTime.atBlock(1_200_000n),
241
243
  * }],
244
+ * ownershipChanges: [{ entityKey: keyToHandOver, newOwner }],
242
245
  * })
243
- * console.log("entityKey", entityKey)
244
- * console.log("txHash", txHash)
245
- * // {
246
- * // entityKey: "0x123",
247
- * // txHash: "0x123",
248
- * // }
249
246
  */
250
247
  mutateEntities: (
251
248
  data: MutateEntitiesParameters,
252
249
  txParams?: TxParams,
253
250
  ) => Promise<MutateEntitiesReturnType>
251
+
252
+ /**
253
+ * The advanced path: build, send, ping and read mutation results as separate steps, each
254
+ * costing the minimum number of RPC calls. Use it when you are optimising your RPC budget;
255
+ * the everyday actions above remain the right default. {@link WalletAdvancedActions}
256
+ *
257
+ * @example
258
+ * const { txHash } = await client.advanced.sendMutation({ creates: [entity] }) // no waiting
259
+ * // ... later, one eth_getTransactionReceipt on your own schedule:
260
+ * const result = await client.advanced.getMutationResult(txHash)
261
+ * if (result.status === "success") console.log(result.createdEntities)
262
+ */
263
+ advanced: WalletAdvancedActions
254
264
  }
255
265
 
256
266
  export function walletArkivActions<
@@ -261,8 +271,8 @@ export function walletArkivActions<
261
271
  return {
262
272
  createEntity: (data: CreateEntityParameters, txParams?: TxParams) =>
263
273
  createEntity(client, data, txParams),
264
- updateEntity: (data: UpdateEntityParameters, txParams?: TxParams) =>
265
- updateEntity(client, data, txParams),
274
+ patchEntity: (data: PatchEntityParameters, txParams?: TxParams) =>
275
+ patchEntity(client, data, txParams),
266
276
  deleteEntity: (data: DeleteEntityParameters, txParams?: TxParams) =>
267
277
  deleteEntity(client, data, txParams),
268
278
  extendEntity: (data: ExtendEntityParameters, txParams?: TxParams) =>
@@ -271,5 +281,6 @@ export function walletArkivActions<
271
281
  changeOwnership(client, data, txParams),
272
282
  mutateEntities: (data: MutateEntitiesParameters, txParams?: TxParams) =>
273
283
  mutateEntities(client, data, txParams),
284
+ advanced: walletAdvancedActions(client),
274
285
  }
275
286
  }
package/src/consts.ts CHANGED
@@ -1,2 +1,3 @@
1
- export const ARKIV_ADDRESS = "0x00000000000000000000000000000061726b6976" as `0x${string}`
1
+ //export const ARKIV_ADDRESS = "0x00000000000000000000000000000061726b6976" as `0x${string}`
2
+ export const ARKIV_ADDRESS = "0x4400000000000000000000000000000000000044" as `0x${string}`
2
3
  export const BLOCK_TIME = 2 // 2 seconds
@@ -0,0 +1,184 @@
1
+ import { describe, expect, it } from "bun:test"
2
+ import { decodeAbiParameters, parseAbiParameters, toHex } from "viem"
3
+ import { encodeAttributes } from "../attr/attributes"
4
+ import { dec, i32, str, u256 } from "../attr/values"
5
+ import { InvalidCreationFlagsError } from "./errors"
6
+ import { decodeCreationFlags, encodeCreationFlags } from "./flags"
7
+ import { MAX_SALT, predictEntityKey, randomSalt, validateSalt } from "./key"
8
+ import { createOperation } from "./operations"
9
+ import { OperationType } from "./params"
10
+
11
+ const OWNER = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
12
+
13
+ const CREATE_PARAMS = parseAbiParameters(
14
+ "(uint128 salt, uint64 expiresAt, uint64 minLifetime, uint8 creationFlags, (bytes32 name, uint8 typeId, bytes value)[] attributes)",
15
+ )
16
+
17
+ /** Decodes an operation's payload back into the Create struct it encodes. */
18
+ function decodeCreate(operationData: `0x${string}`) {
19
+ const [create] = decodeAbiParameters(CREATE_PARAMS, operationData)
20
+ return create
21
+ }
22
+
23
+ describe("creation flags", () => {
24
+ it("packs each flag into its documented bit", () => {
25
+ expect(encodeCreationFlags({})).toBe(0b00)
26
+ expect(encodeCreationFlags({ readonly: true })).toBe(0b01)
27
+ expect(encodeCreationFlags({ permissionlessExtension: true })).toBe(0b10)
28
+ expect(encodeCreationFlags({ readonly: true, permissionlessExtension: true })).toBe(0b11)
29
+ expect(encodeCreationFlags()).toBe(0)
30
+ })
31
+
32
+ it("round-trips through the byte", () => {
33
+ for (const readonly of [true, false]) {
34
+ for (const permissionlessExtension of [true, false]) {
35
+ const decoded = decodeCreationFlags(
36
+ encodeCreationFlags({ readonly, permissionlessExtension }),
37
+ )
38
+ expect(decoded.readonly).toBe(readonly)
39
+ expect(decoded.permissionlessExtension).toBe(permissionlessExtension)
40
+ }
41
+ }
42
+ })
43
+
44
+ it("refuses a value that is not a byte at all", () => {
45
+ // Malformed input
46
+ expect(() => decodeCreationFlags(256)).toThrow(InvalidCreationFlagsError)
47
+ expect(() => decodeCreationFlags(256)).toThrow(/not a byte/)
48
+ expect(() => decodeCreationFlags(-1)).toThrow(/not a byte/)
49
+ })
50
+
51
+ it("keeps the raw byte, so unknown state stays visible", () => {
52
+ expect(decodeCreationFlags(0b11).raw).toBe(3)
53
+ })
54
+ })
55
+
56
+ describe("salt", () => {
57
+ it("generates 128 bits within the wire field", () => {
58
+ for (let i = 0; i < 32; i++) {
59
+ const salt = randomSalt()
60
+ expect(salt).toBeGreaterThanOrEqual(0n)
61
+ expect(salt).toBeLessThanOrEqual(MAX_SALT)
62
+ }
63
+ })
64
+
65
+ it("does not repeat", () => {
66
+ const seen = new Set(Array.from({ length: 64 }, () => randomSalt().toString()))
67
+ expect(seen.size).toBe(64)
68
+ })
69
+
70
+ it("bounds a caller-supplied salt to the uint128 field", () => {
71
+ expect(validateSalt(0n)).toBe(0n)
72
+ expect(validateSalt(MAX_SALT)).toBe(MAX_SALT)
73
+ expect(() => validateSalt(MAX_SALT + 1n)).toThrow(/uint128/)
74
+ expect(() => validateSalt(-1n)).toThrow(/uint128/)
75
+ })
76
+ })
77
+
78
+ describe("predictEntityKey", () => {
79
+ it("is deterministic in owner, nonce and salt", () => {
80
+ const base = { owner: OWNER, nonce: 0n, salt: 7n, chainId: 1 } as const
81
+ expect(predictEntityKey(base)).toBe(predictEntityKey(base))
82
+ expect(predictEntityKey({ ...base, nonce: 1n })).not.toBe(predictEntityKey(base))
83
+ expect(predictEntityKey({ ...base, salt: 8n })).not.toBe(predictEntityKey(base))
84
+ expect(
85
+ predictEntityKey({ ...base, owner: "0x1111111111111111111111111111111111111111" }),
86
+ ).not.toBe(predictEntityKey(base))
87
+ })
88
+
89
+ it("separates chains through the domain", () => {
90
+ const base = { owner: OWNER, nonce: 0n, salt: 0n } as const
91
+ expect(predictEntityKey({ ...base, chainId: 1 })).not.toBe(
92
+ predictEntityKey({ ...base, chainId: 2 }),
93
+ )
94
+ })
95
+
96
+ it("returns a 32-byte key", () => {
97
+ expect(predictEntityKey({ owner: OWNER, nonce: 0n, salt: 0n, chainId: 1 })).toMatch(
98
+ /^0x[0-9a-f]{64}$/,
99
+ )
100
+ })
101
+ })
102
+
103
+ describe("the Create operation", () => {
104
+ const build = (over: Partial<Parameters<typeof createOperation>[0]> = {}) =>
105
+ createOperation({
106
+ salt: 42n,
107
+ expiry: { expiresAt: 0n, minLifetime: 43_200n },
108
+ creationFlags: 0,
109
+ attributes: [],
110
+ ...over,
111
+ })
112
+
113
+ it("carries the CREATE tag", () => {
114
+ expect(build().operation).toBe(OperationType.Create)
115
+ })
116
+
117
+ it("round-trips its fields through the ABI", () => {
118
+ const create = decodeCreate(
119
+ build({
120
+ salt: 42n,
121
+ expiry: { expiresAt: 1_200_000n, minLifetime: 100n },
122
+ creationFlags: 0b11,
123
+ }).operationData,
124
+ )
125
+ expect(create.salt).toBe(42n)
126
+ expect(create.expiresAt).toBe(1_200_000n)
127
+ expect(create.minLifetime).toBe(100n)
128
+ expect(create.creationFlags).toBe(3)
129
+ })
130
+
131
+ it("carries attributes as (name, typeId, bytes), sorted ascending by name", () => {
132
+ const create = decodeCreate(
133
+ build({
134
+ attributes: encodeAttributes({ zebra: i32(1), apple: str("x"), mango: u256(2n) }),
135
+ }).operationData,
136
+ )
137
+ const names = create.attributes.map((a) => a.name)
138
+ expect(names).toEqual([...names].sort())
139
+ expect(create.attributes.map((a) => a.typeId)).toEqual([8, 4, 2]) // apple str, mango u256, zebra i32
140
+ })
141
+
142
+ it("sends the payload and content type as system cells, ahead of user attributes", () => {
143
+ const create = decodeCreate(
144
+ build({
145
+ attributes: encodeAttributes(
146
+ { level: i32(10) },
147
+ { payload: new Uint8Array([0xde, 0xad]), contentType: "application/json" },
148
+ ),
149
+ }).operationData,
150
+ )
151
+ const byName = Object.fromEntries(create.attributes.map((a) => [a.name, a]))
152
+
153
+ // "$" sorts below every letter, so the system cells lead.
154
+ expect(create.attributes[0].name).toBe(toHex("$contentType", { size: 32 }))
155
+ expect(create.attributes[1].name).toBe(toHex("$payload", { size: 32 }))
156
+
157
+ expect(byName[toHex("$payload", { size: 32 })]).toMatchObject({ typeId: 7, value: "0xdead" })
158
+ expect(byName[toHex("$contentType", { size: 32 })].typeId).toBe(8)
159
+ expect(byName[toHex("level", { size: 32 })].typeId).toBe(2)
160
+ })
161
+
162
+ it("omits the system cells entirely when there is no payload or content type", () => {
163
+ const create = decodeCreate(
164
+ build({ attributes: encodeAttributes({ level: i32(10) }) }).operationData,
165
+ )
166
+ expect(create.attributes).toHaveLength(1)
167
+ expect(create.attributes[0].name).toBe(toHex("level", { size: 32 }))
168
+ })
169
+
170
+ it("encodes each value type at its documented width", () => {
171
+ const create = decodeCreate(
172
+ build({
173
+ attributes: encodeAttributes({ n: i32(-42), big: u256(1n), d: dec("1.5"), s: str("hi") }),
174
+ }).operationData,
175
+ )
176
+ const byName = Object.fromEntries(create.attributes.map((a) => [a.name, a.value]))
177
+ // Word types are exactly 32 bytes; i32 is sign-extended.
178
+ expect(byName[toHex("n", { size: 32 })]).toBe(`0x${"ff".repeat(31)}d6`)
179
+ expect(byName[toHex("big", { size: 32 })]).toBe(`0x${"00".repeat(31)}01`)
180
+ expect(BigInt(byName[toHex("d", { size: 32 })])).toBe(1_500_000_000_000_000_000n)
181
+ // str is raw UTF-8, not padded.
182
+ expect(byName[toHex("s", { size: 32 })]).toBe("0x6869")
183
+ })
184
+ })
@@ -0,0 +1,38 @@
1
+ /** An expiry could not be expressed, or would be rejected by the engine. */
2
+ export class InvalidExpiryError extends Error {
3
+ constructor(reason: string) {
4
+ super(`Invalid expiry: ${reason}`)
5
+ this.name = "InvalidExpiryError"
6
+ }
7
+ }
8
+
9
+ /** A `creationFlags` byte could not be built or read. */
10
+ export class InvalidCreationFlagsError extends Error {
11
+ constructor(reason: string) {
12
+ super(`Invalid creation flags: ${reason}`)
13
+ this.name = "InvalidCreationFlagsError"
14
+ }
15
+ }
16
+
17
+ /** A key salt was outside the `uint128` the wire carries. */
18
+ export class InvalidSaltError extends Error {
19
+ constructor(salt: bigint) {
20
+ super(
21
+ `Invalid salt ${salt}. A salt is a uint128, so it must be between 0 and ` +
22
+ `${2n ** 128n - 1n}. Omit it to get 128 random bits, or pass 0 for a predictable key.`,
23
+ )
24
+ this.name = "InvalidSaltError"
25
+ }
26
+ }
27
+
28
+ /** No cryptographic randomness was available to generate a salt. */
29
+ export class NoRandomSourceError extends Error {
30
+ constructor() {
31
+ super(
32
+ "No cryptographic random source is available (globalThis.crypto.getRandomValues is " +
33
+ "missing), so a key salt cannot be generated. Upgrade to Node 18+ or a modern browser, " +
34
+ "or pass an explicit salt.",
35
+ )
36
+ this.name = "NoRandomSourceError"
37
+ }
38
+ }
@@ -0,0 +1,16 @@
1
+ import { parseAbi } from "viem"
2
+
3
+ /**
4
+ * The events the engine emits: one per applied operation, in application order, so an off-chain
5
+ * replica can replay a transaction operation by operation.
6
+ */
7
+ export const ENTITY_EVENTS_ABI = parseAbi([
8
+ "event EntityCreated(bytes32 indexed entityKey, address indexed owner, uint64 expiresAt, uint8 creationFlags)",
9
+ "event EntityPatched(bytes32 indexed entityKey, address indexed owner)",
10
+ "event ExpiryExtended(bytes32 indexed entityKey, address indexed owner, uint64 expiresAt)",
11
+ "event OwnershipTransferred(bytes32 indexed entityKey, address indexed previousOwner, address indexed newOwner)",
12
+ "event EntityDeleted(bytes32 indexed entityKey, address indexed owner)",
13
+ ])
14
+
15
+ /** The name of each event in {@link ENTITY_EVENTS_ABI}. */
16
+ export type EntityEventName = (typeof ENTITY_EVENTS_ABI)[number]["name"]