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

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 (240) 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-Ds2XRflQ.cjs +656 -0
  7. package/dist/advanced-FZy7WoYR.js +598 -0
  8. package/dist/advanced-FZy7WoYR.js.map +1 -0
  9. package/dist/arkivTransactions-BpARZOsH.d.cts +584 -0
  10. package/dist/arkivTransactions-BpARZOsH.d.cts.map +1 -0
  11. package/dist/arkivTransactions-DfZ_yVDD.d.ts +584 -0
  12. package/dist/arkivTransactions-DfZ_yVDD.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-BJGuhsqp.js +819 -0
  18. package/dist/attributes-BJGuhsqp.js.map +1 -0
  19. package/dist/attributes-CMJlp1wn.cjs +1039 -0
  20. package/dist/chains/index.cjs +2 -3
  21. package/dist/chains/index.d.cts +41 -76
  22. package/dist/chains/index.d.cts.map +1 -1
  23. package/dist/chains/index.d.ts +24 -59
  24. package/dist/chains/index.d.ts.map +1 -1
  25. package/dist/chains/index.js +2 -2
  26. package/dist/chains-BfNQliHU.cjs +76 -0
  27. package/dist/chains-Du8tql7I.js +60 -0
  28. package/dist/chains-Du8tql7I.js.map +1 -0
  29. package/dist/codec-M74UR1_0.d.ts +101 -0
  30. package/dist/codec-M74UR1_0.d.ts.map +1 -0
  31. package/dist/entity/index.cjs +21 -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-51znTSB1.cjs +56 -0
  41. package/dist/errors-CwPJ16bn.js +33 -0
  42. package/dist/errors-CwPJ16bn.js.map +1 -0
  43. package/dist/expiry-CZErShX6.js +95 -0
  44. package/dist/expiry-CZErShX6.js.map +1 -0
  45. package/dist/expiry-DRV29F1r.d.cts +79 -0
  46. package/dist/expiry-DRV29F1r.d.cts.map +1 -0
  47. package/dist/expiry-nDbCgZX4.d.ts +79 -0
  48. package/dist/expiry-nDbCgZX4.d.ts.map +1 -0
  49. package/dist/expiry-xr6mx4f7.cjs +135 -0
  50. package/dist/flags-5dTNRnow.d.ts +41 -0
  51. package/dist/flags-5dTNRnow.d.ts.map +1 -0
  52. package/dist/flags-Bh0HJldY.cjs +43 -0
  53. package/dist/flags-CRjPpoh3.d.cts +41 -0
  54. package/dist/flags-CRjPpoh3.d.cts.map +1 -0
  55. package/dist/flags-Cr76ldwF.js +33 -0
  56. package/dist/flags-Cr76ldwF.js.map +1 -0
  57. package/dist/index-CMeD8uEq.d.cts +280 -0
  58. package/dist/index-CMeD8uEq.d.cts.map +1 -0
  59. package/dist/index-CTDI5pXZ.d.ts +184 -0
  60. package/dist/index-CTDI5pXZ.d.ts.map +1 -0
  61. package/dist/index-CekGjkRx.d.cts +184 -0
  62. package/dist/index-CekGjkRx.d.cts.map +1 -0
  63. package/dist/index-D0IzFwMX.d.ts +328 -0
  64. package/dist/index-D0IzFwMX.d.ts.map +1 -0
  65. package/dist/index-DZxwz2Ky.d.cts +177 -0
  66. package/dist/index-DZxwz2Ky.d.cts.map +1 -0
  67. package/dist/index-Dz4SkQWg.d.ts +280 -0
  68. package/dist/index-Dz4SkQWg.d.ts.map +1 -0
  69. package/dist/index-JP31fjyM.d.ts +177 -0
  70. package/dist/index-JP31fjyM.d.ts.map +1 -0
  71. package/dist/index-NbfnXxgr.d.cts +424 -0
  72. package/dist/index-NbfnXxgr.d.cts.map +1 -0
  73. package/dist/index.cjs +434 -412
  74. package/dist/index.d.cts +401 -235
  75. package/dist/index.d.cts.map +1 -1
  76. package/dist/index.d.ts +402 -235
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +370 -404
  79. package/dist/index.js.map +1 -1
  80. package/dist/key-E7mv0BYf.cjs +116 -0
  81. package/dist/key-XQkpw2xh.js +82 -0
  82. package/dist/key-XQkpw2xh.js.map +1 -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-Cvrir3Q-.cjs +108 -0
  87. package/dist/payload-D53ms-Mg.d.ts +153 -0
  88. package/dist/payload-D53ms-Mg.d.ts.map +1 -0
  89. package/dist/payload-DaPzUvEh.js +92 -0
  90. package/dist/payload-DaPzUvEh.js.map +1 -0
  91. package/dist/payload-hluX1rc0.d.cts +153 -0
  92. package/dist/payload-hluX1rc0.d.cts.map +1 -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-BXR-HxD9.cjs +947 -0
  98. package/dist/queryBuilder-BwauPaGO.js +823 -0
  99. package/dist/queryBuilder-BwauPaGO.js.map +1 -0
  100. package/dist/selection-C6KeWJ7g.d.cts +441 -0
  101. package/dist/selection-C6KeWJ7g.d.cts.map +1 -0
  102. package/dist/selection-mjiiAGDO.d.ts +442 -0
  103. package/dist/selection-mjiiAGDO.d.ts.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 -9
  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 +28 -43
  122. package/src/actions/public/getEntityNonce.ts +30 -0
  123. package/src/actions/public/predictEntityKeys.test.ts +159 -0
  124. package/src/actions/public/predictEntityKeys.ts +152 -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 +105 -26
  130. package/src/actions/wallet/deleteEntity.ts +2 -3
  131. package/src/actions/wallet/executeBatch.test.ts +76 -0
  132. package/src/actions/wallet/executeBatch.ts +51 -0
  133. package/src/actions/wallet/extendEntity.ts +40 -16
  134. package/src/actions/wallet/patchEntity.ts +90 -0
  135. package/src/attr/attributes.test.ts +120 -0
  136. package/src/attr/attributes.ts +196 -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 +506 -0
  146. package/src/chains/cheesecake.ts +22 -0
  147. package/src/chains/index.ts +1 -2
  148. package/src/chains/localhost.ts +3 -0
  149. package/src/clients/createPublicClient.test.ts +4 -6
  150. package/src/clients/createPublicClient.ts +9 -8
  151. package/src/clients/createWalletClient.test.ts +4 -4
  152. package/src/clients/createWalletClient.ts +13 -9
  153. package/src/clients/decorators/arkivAdvanced.ts +144 -0
  154. package/src/clients/decorators/arkivPublic.ts +181 -147
  155. package/src/clients/decorators/arkivWallet.ts +141 -122
  156. package/src/consts.ts +2 -1
  157. package/src/entity/create.test.ts +203 -0
  158. package/src/entity/errors.ts +39 -0
  159. package/src/entity/events.ts +16 -0
  160. package/src/entity/expiry.test.ts +195 -0
  161. package/src/entity/expiry.ts +182 -0
  162. package/src/entity/flags.ts +65 -0
  163. package/src/entity/index.ts +16 -0
  164. package/src/entity/key.ts +88 -0
  165. package/src/entity/operations.ts +135 -0
  166. package/src/entity/params.ts +15 -0
  167. package/src/errors.ts +33 -28
  168. package/src/index.ts +19 -0
  169. package/src/query/engine.ts +68 -132
  170. package/src/query/errors.ts +151 -0
  171. package/src/query/expression.test.ts +286 -0
  172. package/src/query/expression.ts +500 -0
  173. package/src/query/index.ts +40 -4
  174. package/src/query/injection.test.ts +151 -0
  175. package/src/query/queryBuilder.test.ts +342 -637
  176. package/src/query/queryBuilder.ts +133 -359
  177. package/src/query/queryResult.ts +54 -51
  178. package/src/query/selection.test.ts +48 -98
  179. package/src/query/selection.ts +77 -65
  180. package/src/types/entity.test.ts +44 -155
  181. package/src/types/entity.ts +62 -44
  182. package/src/types/events.ts +64 -23
  183. package/src/types/index.ts +24 -20
  184. package/src/types/rpcSchema.ts +71 -33
  185. package/src/types/txParams.ts +8 -0
  186. package/src/utils/arkivTransactions.test.ts +687 -38
  187. package/src/utils/arkivTransactions.ts +400 -115
  188. package/src/utils/chains.test.ts +21 -24
  189. package/src/utils/compression.ts +1 -1
  190. package/src/utils/entities.test.ts +62 -0
  191. package/src/utils/entities.ts +111 -29
  192. package/src/utils/expirationTime.ts +153 -10
  193. package/src/utils/logger.ts +2 -2
  194. package/src/utils/payload.ts +29 -2
  195. package/src/utils/revert.test.ts +99 -0
  196. package/src/utils/revert.ts +145 -0
  197. package/dist/chains-BifaY04V.cjs +0 -108
  198. package/dist/chains-CrtkVbBh.js +0 -86
  199. package/dist/chains-CrtkVbBh.js.map +0 -1
  200. package/dist/consts-AUoL-v42.cjs +0 -18
  201. package/dist/consts-BV4PR-de.js +0 -7
  202. package/dist/consts-BV4PR-de.js.map +0 -1
  203. package/dist/entity-B6g_EPm1.d.cts +0 -382
  204. package/dist/entity-B6g_EPm1.d.cts.map +0 -1
  205. package/dist/entity-qoLMAuuk.d.ts +0 -382
  206. package/dist/entity-qoLMAuuk.d.ts.map +0 -1
  207. package/dist/index-DdspxNAA.d.ts +0 -481
  208. package/dist/index-DdspxNAA.d.ts.map +0 -1
  209. package/dist/index-Ds9dwqb5.d.cts +0 -481
  210. package/dist/index-Ds9dwqb5.d.cts.map +0 -1
  211. package/dist/payload-B2kFJyah.d.ts +0 -6
  212. package/dist/payload-B2kFJyah.d.ts.map +0 -1
  213. package/dist/payload-CJN8fTvX.d.cts +0 -6
  214. package/dist/payload-CJN8fTvX.d.cts.map +0 -1
  215. package/dist/payload-Claf2jUO.js +0 -13
  216. package/dist/payload-Claf2jUO.js.map +0 -1
  217. package/dist/payload-DpA5PA-w.cjs +0 -23
  218. package/dist/query/index.js.map +0 -1
  219. package/dist/queryBuilder-D2uwP9jI.js +0 -704
  220. package/dist/queryBuilder-D2uwP9jI.js.map +0 -1
  221. package/dist/queryBuilder-UW2MCaX4.cjs +0 -792
  222. package/dist/utils/index.d.cts.map +0 -1
  223. package/dist/utils/index.d.ts.map +0 -1
  224. package/dist/utils/index.js.map +0 -1
  225. package/src/actions/public/buildQuery.ts +0 -10
  226. package/src/actions/public/subscribeEntityEvents.ts +0 -119
  227. package/src/actions/wallet/mutateEntities.ts +0 -111
  228. package/src/actions/wallet/updateEntity.ts +0 -60
  229. package/src/chains/braga.ts +0 -26
  230. package/src/chains/kaolin.ts +0 -29
  231. package/src/query/engine.test.ts +0 -526
  232. package/src/query/predicate.test.ts +0 -461
  233. package/src/query/predicate.ts +0 -144
  234. package/src/query/queryResult.test.ts +0 -270
  235. package/src/query/selectQueryBuilder.test.ts +0 -210
  236. package/src/types/attributes.ts +0 -16
  237. package/src/utils/validation.test.ts +0 -60
  238. package/src/utils/validation.ts +0 -32
  239. /package/dist/{types-H-TSmRpm.js → attr-DlmErkvR.cjs} +0 -0
  240. /package/dist/{types-o3zWarRp.cjs → attr-GcQKatub.js} +0 -0
@@ -14,28 +14,29 @@ import type {
14
14
  DeleteEntityReturnType,
15
15
  } from "../../actions/wallet/deleteEntity"
16
16
  import { deleteEntity } from "../../actions/wallet/deleteEntity"
17
+ import type {
18
+ ExecuteBatchParameters,
19
+ ExecuteBatchReturnType,
20
+ } from "../../actions/wallet/executeBatch"
21
+ import { executeBatch } from "../../actions/wallet/executeBatch"
17
22
  import type {
18
23
  ExtendEntityParameters,
19
24
  ExtendEntityReturnType,
20
25
  } from "../../actions/wallet/extendEntity"
21
26
  import { extendEntity } from "../../actions/wallet/extendEntity"
22
- import type {
23
- MutateEntitiesParameters,
24
- MutateEntitiesReturnType,
25
- } from "../../actions/wallet/mutateEntities"
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,41 +46,41 @@ export type WalletArkivActions<
45
46
  | "sendRawTransaction"
46
47
  | "signMessage"
47
48
  | "signTransaction"
49
+ | "writeContract"
48
50
  > & {
49
51
  /**
50
52
  * Creates a new entity.
51
53
  *
52
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/createEntity
53
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
54
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
54
55
  *
55
56
  * @param data - The entity creation parameters
56
57
  * @param txParams - Optional transaction parameters
57
- * @returns The created entity with transaction hash
58
+ * @returns The new entity's key, the transaction hash, and the block it is expected to
59
+ * expire at. {@link CreateEntityReturnType}
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"
71
+ * import { privateKeyToAccount } from "viem/accounts"
67
72
  *
68
- * const client = createPublicClient({
69
- * chain: braga,
73
+ * const client = createWalletClient({
74
+ * account: privateKeyToAccount("0x..."),
75
+ * chain: cheesecake,
70
76
  * transport: http(),
71
77
  * })
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,
78
+ * const { entityKey, txHash, expiresAt } = await client.createEntity({
79
+ * payload: jsonToPayload({ entityType: "testType", entityId: "testId" }),
80
+ * contentType: "application/json",
81
+ * attributes: { testKey: "testValue", level: i32(3) },
82
+ * expires: ExpirationTime.fromDays(30),
76
83
  * })
77
- * console.log("entityKey", entityKey)
78
- * console.log("txHash", txHash)
79
- * // {
80
- * // entityKey: "0x123",
81
- * // txHash: "0x123",
82
- * // }
83
84
  */
84
85
  createEntity: (
85
86
  data: CreateEntityParameters,
@@ -87,59 +88,67 @@ export type WalletArkivActions<
87
88
  ) => Promise<CreateEntityReturnType>
88
89
 
89
90
  /**
90
- * Updates the entity with the given key.
91
+ * Applies a patch to the entity with the given key: sets some fields, unsets others, and leaves
92
+ * everything it does not name alone.
91
93
  *
92
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/updateEntity
93
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
94
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
94
95
  *
95
- * @param data - The entity update parameters
96
+ * @param data - The entity key and the mutations to apply
96
97
  * @param txParams - Optional transaction parameters
97
- * @returns The updated entity with transaction hash
98
+ * @returns The patched entity's key and the transaction hash. {@link PatchEntityReturnType}
98
99
  *
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.
100
+ * @throws {EmptyPatchError} If the patch has nothing to apply.
101
+ * @throws {ConflictingMutationError} If a name appears in both `set` and `unset`.
102
+ * @throws {InvalidValueError} If an attribute value does not fit the type it names.
103
+ * @throws {InvalidAttributeNameError} If a name violates the attribute-name grammar.
103
104
  *
104
105
  * @example
105
- * import { createWalletClient, http } from 'arkiv'
106
- * import { braga } from 'arkiv/chains'
106
+ * import { createWalletClient, jsonToPayload } from "@arkiv-network/sdk"
107
+ * import { i32 } from "@arkiv-network/sdk/attr"
108
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
109
+ * import { http } from "viem"
110
+ * import { privateKeyToAccount } from "viem/accounts"
107
111
  *
108
112
  * const client = createWalletClient({
109
- * chain: braga,
113
+ * account: privateKeyToAccount("0x..."),
114
+ * chain: cheesecake,
110
115
  * transport: http(),
111
116
  * })
117
+ * // Publish the entity: one attribute changes, one goes away, the payload is replaced.
118
+ * const { txHash } = await client.patchEntity({
119
+ * entityKey,
120
+ * set: { status: "published", revision: i32(2) },
121
+ * unset: ["draft"],
122
+ * payload: jsonToPayload({ title: "Hello" }),
123
+ * })
112
124
  */
113
- updateEntity: (
114
- data: UpdateEntityParameters,
125
+ patchEntity: (
126
+ data: PatchEntityParameters,
115
127
  txParams?: TxParams,
116
- ) => Promise<UpdateEntityReturnType>
128
+ ) => Promise<PatchEntityReturnType>
117
129
 
118
130
  /**
119
131
  * Deletes the entity with the given key.
120
132
  *
121
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/deleteEntity
122
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
133
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
123
134
  *
124
135
  * @param data - The entity deletion parameters
125
136
  * @param txParams - Optional transaction parameters
126
- * @returns The deleted entity with transaction hash
137
+ * @returns The deleted entity's key and the transaction hash. {@link DeleteEntityReturnType}
127
138
  *
128
139
  * @example
129
- * import { createWalletClient, http } from 'arkiv'
130
- * import { braga } from 'arkiv/chains'
140
+ * import { createWalletClient } from "@arkiv-network/sdk"
141
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
142
+ * import { http } from "viem"
143
+ * import { privateKeyToAccount } from "viem/accounts"
131
144
  *
132
145
  * const client = createWalletClient({
133
- * chain: braga,
146
+ * account: privateKeyToAccount("0x..."),
147
+ * chain: cheesecake,
134
148
  * transport: http(),
135
149
  * })
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
- * // }
150
+ * // entityKey is the bytes32 key returned by createEntity.
151
+ * const { txHash } = await client.deleteEntity({ entityKey })
143
152
  */
144
153
  deleteEntity: (
145
154
  data: DeleteEntityParameters,
@@ -147,35 +156,37 @@ export type WalletArkivActions<
147
156
  ) => Promise<DeleteEntityReturnType>
148
157
 
149
158
  /**
150
- * Extends the entity with the given key.
159
+ * Sets a new expiry on the entity with the given key.
160
+ *
161
+ * The new lifetime is resolved the same way a create's is: a duration counts from now rather
162
+ * than adding to what the entity has left, and `atBlock` / `atDate` pin an absolute deadline.
163
+ * The engine rejects an extension that would not move the expiry later.
151
164
  *
152
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/extendEntity
153
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
165
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
154
166
  *
155
- * @param data - The entity update parameters
167
+ * @param data - The entity key and its new lifetime
156
168
  * @param txParams - Optional transaction parameters
157
- * @returns The updated entity with transaction hash
169
+ * @returns The entity's key, the transaction hash, and the block it is now expected to expire
170
+ * at. {@link ExtendEntityReturnType}
158
171
  *
159
- * @throws {InvalidExpirationError} If `expiresIn` is not a positive integer
160
- * that is a multiple of the block time (2 seconds).
172
+ * @throws {InvalidExpiryError} If the expiry is malformed, exceeds a protocol bound, or would
173
+ * leave the entity dead on arrival.
161
174
  *
162
175
  * @example
163
- * import { createWalletClient, http } from 'arkiv'
164
- * import { braga } from 'arkiv/chains'
176
+ * import { createWalletClient, ExpirationTime } from "@arkiv-network/sdk"
177
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
178
+ * import { http } from "viem"
179
+ * import { privateKeyToAccount } from "viem/accounts"
165
180
  *
166
181
  * const client = createWalletClient({
167
- * chain: braga,
182
+ * account: privateKeyToAccount("0x..."),
183
+ * chain: cheesecake,
168
184
  * transport: http(),
169
185
  * })
170
- * const { entityKey, txHash } = await client.extendEntity("0x123", {
171
- * expiresIn: 1000,
186
+ * const { txHash, expiresAt } = await client.extendEntity({
187
+ * entityKey,
188
+ * expires: ExpirationTime.fromDays(30),
172
189
  * })
173
- * console.log("entityKey", entityKey)
174
- * console.log("txHash", txHash)
175
- * // {
176
- * // entityKey: "0x123",
177
- * // txHash: "0x123",
178
- * // }
179
190
  */
180
191
  extendEntity: (
181
192
  data: ExtendEntityParameters,
@@ -183,14 +194,13 @@ export type WalletArkivActions<
183
194
  ) => Promise<ExtendEntityReturnType>
184
195
 
185
196
  /**
186
- * Changes the ownership of the entity with the given address.
197
+ * Hands the entity with the given key to a new owner.
187
198
  *
188
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/changeOwnership
189
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
199
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
190
200
  *
191
201
  * @param data - The ownership change parameters
192
202
  * @param txParams - Optional transaction parameters
193
- * @returns The entity with updated ownership and transaction hash
203
+ * @returns The entity's key and the transaction hash. {@link ChangeOwnershipReturnType}
194
204
  */
195
205
  changeOwnership: (
196
206
  data: ChangeOwnershipParameters,
@@ -198,59 +208,67 @@ export type WalletArkivActions<
198
208
  ) => Promise<ChangeOwnershipReturnType>
199
209
 
200
210
  /**
201
- * Mutates the entities with the given keys.
211
+ * Applies a batch of entity operations — creates, patches, deletes, extensions and ownership
212
+ * transfers — in one transaction.
213
+ *
214
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
202
215
  *
203
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/mutateEntities
204
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
216
+ * Every operation lands in one transaction, so the whole batch applies or none of it does.
217
+ * At least one operation is required.
205
218
  *
206
- * @param data - The mutation parameters (creates, updates, deletes, extensions)
219
+ * @param data - The batch parameters (creates, patches, deletes, extensions, ownershipChanges)
207
220
  * @param txParams - Optional transaction parameters
208
- * @returns The mutation result with transaction hash
221
+ * @returns The transaction hash, plus the keys touched by each kind of operation.
222
+ * {@link ExecuteBatchReturnType}
209
223
  *
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.
224
+ * @throws {InvalidExpiryError} If an expiry exceeds a protocol bound or would leave the entity
225
+ * dead on arrival.
226
+ * @throws {InvalidValueError} If an attribute value does not fit the type it names.
214
227
  *
215
228
  * @example
216
- * import { createWalletClient, http } from 'arkiv'
217
- * import { braga } from 'arkiv/chains'
229
+ * import { createWalletClient, ExpirationTime, jsonToPayload } from "@arkiv-network/sdk"
230
+ * import { cheesecake } from "@arkiv-network/sdk/chains"
231
+ * import { http } from "viem"
232
+ * import { privateKeyToAccount } from "viem/accounts"
218
233
  *
219
234
  * const client = createWalletClient({
220
- * chain: braga,
235
+ * account: privateKeyToAccount("0x..."),
236
+ * chain: cheesecake,
221
237
  * transport: http(),
222
238
  * })
223
- * const { entityKey, txHash } = await client.mutateEntities({
239
+ * const { txHash, createdEntities } = await client.executeBatch({
224
240
  * 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",
241
+ * payload: jsonToPayload({ entityType: "testType", entityId: "testId" }),
242
+ * contentType: "application/json",
243
+ * attributes: { testKey: "testValue" },
244
+ * expires: ExpirationTime.fromDays(30),
237
245
  * }],
246
+ * patches: [{ entityKey: keyToRevise, set: { status: "archived" }, unset: ["draft"] }],
247
+ * deletes: [{ entityKey: staleKey }],
238
248
  * extensions: [{
239
- * entityKey: "0x1234",
240
- * expiresIn: 1000,
249
+ * entityKey: keyToKeepAlive,
250
+ * expires: ExpirationTime.atBlock(1_200_000n),
241
251
  * }],
252
+ * ownershipChanges: [{ entityKey: keyToHandOver, newOwner }],
242
253
  * })
243
- * console.log("entityKey", entityKey)
244
- * console.log("txHash", txHash)
245
- * // {
246
- * // entityKey: "0x123",
247
- * // txHash: "0x123",
248
- * // }
249
254
  */
250
- mutateEntities: (
251
- data: MutateEntitiesParameters,
255
+ executeBatch: (
256
+ data: ExecuteBatchParameters,
252
257
  txParams?: TxParams,
253
- ) => Promise<MutateEntitiesReturnType>
258
+ ) => Promise<ExecuteBatchReturnType>
259
+
260
+ /**
261
+ * The advanced path: build, send, ping and read mutation results as separate steps, each
262
+ * costing the minimum number of RPC calls. Use it when you are optimising your RPC budget;
263
+ * the everyday actions above remain the right default. {@link WalletAdvancedActions}
264
+ *
265
+ * @example
266
+ * const { txHash } = await client.advanced.sendMutation({ creates: [entity] }) // no waiting
267
+ * // ... later, one eth_getTransactionReceipt on your own schedule:
268
+ * const result = await client.advanced.getMutationResult(txHash)
269
+ * if (result.status === "success") console.log(result.createdEntities)
270
+ */
271
+ advanced: WalletAdvancedActions
254
272
  }
255
273
 
256
274
  export function walletArkivActions<
@@ -261,15 +279,16 @@ export function walletArkivActions<
261
279
  return {
262
280
  createEntity: (data: CreateEntityParameters, txParams?: TxParams) =>
263
281
  createEntity(client, data, txParams),
264
- updateEntity: (data: UpdateEntityParameters, txParams?: TxParams) =>
265
- updateEntity(client, data, txParams),
282
+ patchEntity: (data: PatchEntityParameters, txParams?: TxParams) =>
283
+ patchEntity(client, data, txParams),
266
284
  deleteEntity: (data: DeleteEntityParameters, txParams?: TxParams) =>
267
285
  deleteEntity(client, data, txParams),
268
286
  extendEntity: (data: ExtendEntityParameters, txParams?: TxParams) =>
269
287
  extendEntity(client, data, txParams),
270
288
  changeOwnership: (data: ChangeOwnershipParameters, txParams?: TxParams) =>
271
289
  changeOwnership(client, data, txParams),
272
- mutateEntities: (data: MutateEntitiesParameters, txParams?: TxParams) =>
273
- mutateEntities(client, data, txParams),
290
+ executeBatch: (data: ExecuteBatchParameters, txParams?: TxParams) =>
291
+ executeBatch(client, data, txParams),
292
+ advanced: walletAdvancedActions(client),
274
293
  }
275
294
  }
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,203 @@
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, NO_SALT, predictEntityKey, randomSalt, resolveSalt, 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
+ it("resolves NO_SALT to the zero, and passes anything else through", () => {
78
+ expect(resolveSalt(NO_SALT)).toBe(0n)
79
+ expect(resolveSalt(0n)).toBe(0n)
80
+ expect(resolveSalt(42n)).toBe(42n)
81
+ expect(() => resolveSalt(MAX_SALT + 1n)).toThrow(/uint128/)
82
+ })
83
+
84
+ it("reports a salt it cannot use without choking on a symbol", () => {
85
+ // @ts-expect-error
86
+ expect(() => validateSalt(Symbol("nope"))).toThrow(/Invalid salt/)
87
+ })
88
+ })
89
+
90
+ describe("predictEntityKey", () => {
91
+ it("is deterministic in owner, nonce and salt", () => {
92
+ const base = { owner: OWNER, nonce: 0n, salt: 7n, chainId: 1 } as const
93
+ expect(predictEntityKey(base)).toBe(predictEntityKey(base))
94
+ expect(predictEntityKey({ ...base, nonce: 1n })).not.toBe(predictEntityKey(base))
95
+ expect(predictEntityKey({ ...base, salt: 8n })).not.toBe(predictEntityKey(base))
96
+ expect(
97
+ predictEntityKey({ ...base, owner: "0x1111111111111111111111111111111111111111" }),
98
+ ).not.toBe(predictEntityKey(base))
99
+ })
100
+
101
+ it("derives the same key from NO_SALT as from an explicit zero", () => {
102
+ const base = { owner: OWNER, nonce: 3n, chainId: 1 } as const
103
+ expect(predictEntityKey({ ...base, salt: NO_SALT })).toBe(
104
+ predictEntityKey({ ...base, salt: 0n }),
105
+ )
106
+ })
107
+
108
+ it("separates chains through the domain", () => {
109
+ const base = { owner: OWNER, nonce: 0n, salt: 0n } as const
110
+ expect(predictEntityKey({ ...base, chainId: 1 })).not.toBe(
111
+ predictEntityKey({ ...base, chainId: 2 }),
112
+ )
113
+ })
114
+
115
+ it("returns a 32-byte key", () => {
116
+ expect(predictEntityKey({ owner: OWNER, nonce: 0n, salt: 0n, chainId: 1 })).toMatch(
117
+ /^0x[0-9a-f]{64}$/,
118
+ )
119
+ })
120
+ })
121
+
122
+ describe("the Create operation", () => {
123
+ const build = (over: Partial<Parameters<typeof createOperation>[0]> = {}) =>
124
+ createOperation({
125
+ salt: 42n,
126
+ expiry: { expiresAt: 0n, minLifetime: 43_200n },
127
+ creationFlags: 0,
128
+ attributes: [],
129
+ ...over,
130
+ })
131
+
132
+ it("carries the CREATE tag", () => {
133
+ expect(build().operation).toBe(OperationType.Create)
134
+ })
135
+
136
+ it("round-trips its fields through the ABI", () => {
137
+ const create = decodeCreate(
138
+ build({
139
+ salt: 42n,
140
+ expiry: { expiresAt: 1_200_000n, minLifetime: 100n },
141
+ creationFlags: 0b11,
142
+ }).operationData,
143
+ )
144
+ expect(create.salt).toBe(42n)
145
+ expect(create.expiresAt).toBe(1_200_000n)
146
+ expect(create.minLifetime).toBe(100n)
147
+ expect(create.creationFlags).toBe(3)
148
+ })
149
+
150
+ it("carries attributes as (name, typeId, bytes), sorted ascending by name", () => {
151
+ const create = decodeCreate(
152
+ build({
153
+ attributes: encodeAttributes({ zebra: i32(1), apple: str("x"), mango: u256(2n) }),
154
+ }).operationData,
155
+ )
156
+ const names = create.attributes.map((a) => a.name)
157
+ expect(names).toEqual([...names].sort())
158
+ expect(create.attributes.map((a) => a.typeId)).toEqual([8, 4, 2]) // apple str, mango u256, zebra i32
159
+ })
160
+
161
+ it("sends the payload and content type as system cells, ahead of user attributes", () => {
162
+ const create = decodeCreate(
163
+ build({
164
+ attributes: encodeAttributes(
165
+ { level: i32(10) },
166
+ { payload: new Uint8Array([0xde, 0xad]), contentType: "application/json" },
167
+ ),
168
+ }).operationData,
169
+ )
170
+ const byName = Object.fromEntries(create.attributes.map((a) => [a.name, a]))
171
+
172
+ // "$" sorts below every letter, so the system cells lead.
173
+ expect(create.attributes[0].name).toBe(toHex("$contentType", { size: 32 }))
174
+ expect(create.attributes[1].name).toBe(toHex("$payload", { size: 32 }))
175
+
176
+ expect(byName[toHex("$payload", { size: 32 })]).toMatchObject({ typeId: 7, value: "0xdead" })
177
+ expect(byName[toHex("$contentType", { size: 32 })].typeId).toBe(8)
178
+ expect(byName[toHex("level", { size: 32 })].typeId).toBe(2)
179
+ })
180
+
181
+ it("omits the system cells entirely when there is no payload or content type", () => {
182
+ const create = decodeCreate(
183
+ build({ attributes: encodeAttributes({ level: i32(10) }) }).operationData,
184
+ )
185
+ expect(create.attributes).toHaveLength(1)
186
+ expect(create.attributes[0].name).toBe(toHex("level", { size: 32 }))
187
+ })
188
+
189
+ it("encodes each value type at its documented width", () => {
190
+ const create = decodeCreate(
191
+ build({
192
+ attributes: encodeAttributes({ n: i32(-42), big: u256(1n), d: dec("1.5"), s: str("hi") }),
193
+ }).operationData,
194
+ )
195
+ const byName = Object.fromEntries(create.attributes.map((a) => [a.name, a.value]))
196
+ // Word types are exactly 32 bytes; i32 is sign-extended.
197
+ expect(byName[toHex("n", { size: 32 })]).toBe(`0x${"ff".repeat(31)}d6`)
198
+ expect(byName[toHex("big", { size: 32 })]).toBe(`0x${"00".repeat(31)}01`)
199
+ expect(BigInt(byName[toHex("d", { size: 32 })])).toBe(1_500_000_000_000_000_000n)
200
+ // str is raw UTF-8, not padded.
201
+ expect(byName[toHex("s", { size: 32 })]).toBe("0x6869")
202
+ })
203
+ })
@@ -0,0 +1,39 @@
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: unknown) {
20
+ super(
21
+ `Invalid salt ${String(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 NO_SALT for a key derived ` +
23
+ `from the owner and nonce alone.`,
24
+ )
25
+ this.name = "InvalidSaltError"
26
+ }
27
+ }
28
+
29
+ /** No cryptographic randomness was available to generate a salt. */
30
+ export class NoRandomSourceError extends Error {
31
+ constructor() {
32
+ super(
33
+ "No cryptographic random source is available (globalThis.crypto.getRandomValues is " +
34
+ "missing), so a key salt cannot be generated. Upgrade to Node 18+ or a modern browser, " +
35
+ "or pass an explicit salt.",
36
+ )
37
+ this.name = "NoRandomSourceError"
38
+ }
39
+ }
@@ -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"]