@arkiv-network/sdk 0.8.0-advanced.2 → 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 (152) hide show
  1. package/dist/actions/advanced/index.cjs +6 -6
  2. package/dist/actions/advanced/index.d.cts +5 -5
  3. package/dist/actions/advanced/index.d.ts +6 -6
  4. package/dist/actions/advanced/index.js +6 -6
  5. package/dist/{advanced-fPImL6lW.cjs → advanced-Ds2XRflQ.cjs} +6 -6
  6. package/dist/{advanced-8ZPTmmaN.js → advanced-FZy7WoYR.js} +7 -7
  7. package/dist/advanced-FZy7WoYR.js.map +1 -0
  8. package/dist/{arkivTransactions-D74gERHG.d.cts → arkivTransactions-BpARZOsH.d.cts} +60 -47
  9. package/dist/arkivTransactions-BpARZOsH.d.cts.map +1 -0
  10. package/dist/{arkivTransactions-NtGD6uk5.d.ts → arkivTransactions-DfZ_yVDD.d.ts} +60 -47
  11. package/dist/arkivTransactions-DfZ_yVDD.d.ts.map +1 -0
  12. package/dist/attr/index.cjs +1 -1
  13. package/dist/attr/index.d.cts +1 -1
  14. package/dist/attr/index.d.ts +2 -2
  15. package/dist/attr/index.js +1 -1
  16. package/dist/{attributes-BhY5DwJp.js → attributes-BJGuhsqp.js} +14 -4
  17. package/dist/attributes-BJGuhsqp.js.map +1 -0
  18. package/dist/{attributes-Df9IsCVs.cjs → attributes-CMJlp1wn.cjs} +13 -3
  19. package/dist/chains/index.cjs +1 -1
  20. package/dist/chains/index.d.cts +3 -0
  21. package/dist/chains/index.d.cts.map +1 -1
  22. package/dist/chains/index.d.ts +3 -0
  23. package/dist/chains/index.d.ts.map +1 -1
  24. package/dist/chains/index.js +1 -1
  25. package/dist/{chains-BYRTTipa.cjs → chains-BfNQliHU.cjs} +3 -0
  26. package/dist/{chains-BJrZs6sG.js → chains-Du8tql7I.js} +4 -1
  27. package/dist/chains-Du8tql7I.js.map +1 -0
  28. package/dist/{codec-Car3KkFF.d.ts → codec-M74UR1_0.d.ts} +3 -4
  29. package/dist/codec-M74UR1_0.d.ts.map +1 -0
  30. package/dist/entity/index.cjs +5 -4
  31. package/dist/entity/index.d.cts +2 -2
  32. package/dist/entity/index.d.ts +3 -3
  33. package/dist/entity/index.js +5 -5
  34. package/dist/{errors-BVbjKVMN.cjs → errors-51znTSB1.cjs} +1 -1
  35. package/dist/{errors-LTe7bOYS.js → errors-CwPJ16bn.js} +2 -2
  36. package/dist/errors-CwPJ16bn.js.map +1 -0
  37. package/dist/{expiry-BjpxZZQU.js → expiry-CZErShX6.js} +2 -2
  38. package/dist/expiry-CZErShX6.js.map +1 -0
  39. package/dist/expiry-DRV29F1r.d.cts.map +1 -1
  40. package/dist/expiry-nDbCgZX4.d.ts.map +1 -1
  41. package/dist/{expiry-BELY2Kpw.cjs → expiry-xr6mx4f7.cjs} +1 -1
  42. package/dist/{flags-kr276q6i.cjs → flags-Bh0HJldY.cjs} +1 -1
  43. package/dist/{flags-DFBs2J94.js → flags-Cr76ldwF.js} +2 -2
  44. package/dist/{flags-DFBs2J94.js.map → flags-Cr76ldwF.js.map} +1 -1
  45. package/dist/{index-BF7Mq9Is.d.cts → index-CMeD8uEq.d.cts} +4 -4
  46. package/dist/{index-BF7Mq9Is.d.cts.map → index-CMeD8uEq.d.cts.map} +1 -1
  47. package/dist/{index-B5j76711.d.ts → index-CTDI5pXZ.d.ts} +5 -5
  48. package/dist/{index-B5j76711.d.ts.map → index-CTDI5pXZ.d.ts.map} +1 -1
  49. package/dist/{index-EeUO0nnJ.d.cts → index-CekGjkRx.d.cts} +5 -5
  50. package/dist/{index-EeUO0nnJ.d.cts.map → index-CekGjkRx.d.cts.map} +1 -1
  51. package/dist/{index-DvYvrkdk.d.ts → index-D0IzFwMX.d.ts} +12 -4
  52. package/dist/index-D0IzFwMX.d.ts.map +1 -0
  53. package/dist/{index-fX_XyqLp.d.ts → index-DZxwz2Ky.d.cts} +53 -43
  54. package/dist/index-DZxwz2Ky.d.cts.map +1 -0
  55. package/dist/{index-DHDud2hC.d.ts → index-Dz4SkQWg.d.ts} +4 -4
  56. package/dist/{index-DHDud2hC.d.ts.map → index-Dz4SkQWg.d.ts.map} +1 -1
  57. package/dist/{index-CA4V7Pdh.d.cts → index-JP31fjyM.d.ts} +53 -43
  58. package/dist/index-JP31fjyM.d.ts.map +1 -0
  59. package/dist/{index-CV5zCdpa.d.cts → index-NbfnXxgr.d.cts} +13 -6
  60. package/dist/index-NbfnXxgr.d.cts.map +1 -0
  61. package/dist/index.cjs +41 -42
  62. package/dist/index.d.cts +58 -52
  63. package/dist/index.d.cts.map +1 -1
  64. package/dist/index.d.ts +59 -53
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +41 -43
  67. package/dist/index.js.map +1 -1
  68. package/dist/{key-WKhXnXuU.cjs → key-E7mv0BYf.cjs} +23 -5
  69. package/dist/{key-D1Bh7MF0.js → key-XQkpw2xh.js} +17 -5
  70. package/dist/key-XQkpw2xh.js.map +1 -0
  71. package/dist/{payload-KC14SDlN.cjs → payload-Cvrir3Q-.cjs} +27 -2
  72. package/dist/{payload-CcUj85ys.d.ts → payload-D53ms-Mg.d.ts} +60 -1
  73. package/dist/payload-D53ms-Mg.d.ts.map +1 -0
  74. package/dist/{payload-Bz6651ax.js → payload-DaPzUvEh.js} +28 -3
  75. package/dist/payload-DaPzUvEh.js.map +1 -0
  76. package/dist/{payload-BNoud2KZ.d.cts → payload-hluX1rc0.d.cts} +60 -1
  77. package/dist/payload-hluX1rc0.d.cts.map +1 -0
  78. package/dist/query/index.cjs +4 -4
  79. package/dist/query/index.d.cts +3 -3
  80. package/dist/query/index.d.ts +4 -4
  81. package/dist/query/index.js +4 -4
  82. package/dist/{queryBuilder-DbUMEZln.cjs → queryBuilder-BXR-HxD9.cjs} +13 -10
  83. package/dist/{queryBuilder-DM6AWCBa.js → queryBuilder-BwauPaGO.js} +14 -11
  84. package/dist/queryBuilder-BwauPaGO.js.map +1 -0
  85. package/dist/{selection-DPg0uyJf.d.cts → selection-C6KeWJ7g.d.cts} +16 -8
  86. package/dist/selection-C6KeWJ7g.d.cts.map +1 -0
  87. package/dist/{selection-C4UhsMOO.d.ts → selection-mjiiAGDO.d.ts} +17 -9
  88. package/dist/selection-mjiiAGDO.d.ts.map +1 -0
  89. package/dist/types/index.d.cts +5 -5
  90. package/dist/types/index.d.ts +6 -6
  91. package/dist/utils/index.cjs +3 -3
  92. package/dist/utils/index.d.cts +1 -1
  93. package/dist/utils/index.d.ts +1 -1
  94. package/dist/utils/index.js +3 -3
  95. package/package.json +1 -5
  96. package/src/actions/advanced/index.ts +1 -1
  97. package/src/actions/advanced/sendMutation.ts +1 -1
  98. package/src/actions/public/getEntity.ts +1 -0
  99. package/src/actions/public/predictEntityKeys.test.ts +17 -1
  100. package/src/actions/public/predictEntityKeys.ts +28 -14
  101. package/src/actions/wallet/createEntity.ts +7 -6
  102. package/src/actions/wallet/{mutateEntities.test.ts → executeBatch.test.ts} +7 -7
  103. package/src/actions/wallet/{mutateEntities.ts → executeBatch.ts} +9 -9
  104. package/src/actions/wallet/extendEntity.ts +1 -0
  105. package/src/actions/wallet/patchEntity.ts +3 -3
  106. package/src/attr/attributes.ts +8 -2
  107. package/src/attr/errors.ts +2 -2
  108. package/src/attr/types.ts +3 -3
  109. package/src/attr/values.ts +5 -1
  110. package/src/chains/localhost.ts +3 -0
  111. package/src/clients/createPublicClient.test.ts +1 -1
  112. package/src/clients/createPublicClient.ts +5 -5
  113. package/src/clients/createWalletClient.test.ts +2 -2
  114. package/src/clients/createWalletClient.ts +3 -5
  115. package/src/clients/decorators/arkivAdvanced.ts +1 -1
  116. package/src/clients/decorators/arkivPublic.ts +13 -12
  117. package/src/clients/decorators/arkivWallet.ts +40 -32
  118. package/src/entity/create.test.ts +20 -1
  119. package/src/entity/errors.ts +4 -3
  120. package/src/entity/expiry.ts +1 -0
  121. package/src/entity/index.ts +2 -1
  122. package/src/entity/key.ts +23 -3
  123. package/src/entity/operations.ts +1 -0
  124. package/src/query/errors.ts +1 -1
  125. package/src/query/expression.ts +14 -7
  126. package/src/query/queryBuilder.ts +1 -1
  127. package/src/types/index.ts +4 -4
  128. package/src/types/rpcSchema.ts +1 -0
  129. package/src/types/txParams.ts +8 -0
  130. package/src/utils/arkivTransactions.test.ts +7 -1
  131. package/src/utils/arkivTransactions.ts +2 -2
  132. package/src/utils/expirationTime.ts +39 -0
  133. package/src/utils/payload.ts +25 -0
  134. package/dist/advanced-8ZPTmmaN.js.map +0 -1
  135. package/dist/arkivTransactions-D74gERHG.d.cts.map +0 -1
  136. package/dist/arkivTransactions-NtGD6uk5.d.ts.map +0 -1
  137. package/dist/attributes-BhY5DwJp.js.map +0 -1
  138. package/dist/chains-BJrZs6sG.js.map +0 -1
  139. package/dist/codec-Car3KkFF.d.ts.map +0 -1
  140. package/dist/errors-LTe7bOYS.js.map +0 -1
  141. package/dist/expiry-BjpxZZQU.js.map +0 -1
  142. package/dist/index-CA4V7Pdh.d.cts.map +0 -1
  143. package/dist/index-CV5zCdpa.d.cts.map +0 -1
  144. package/dist/index-DvYvrkdk.d.ts.map +0 -1
  145. package/dist/index-fX_XyqLp.d.ts.map +0 -1
  146. package/dist/key-D1Bh7MF0.js.map +0 -1
  147. package/dist/payload-BNoud2KZ.d.cts.map +0 -1
  148. package/dist/payload-Bz6651ax.js.map +0 -1
  149. package/dist/payload-CcUj85ys.d.ts.map +0 -1
  150. package/dist/queryBuilder-DM6AWCBa.js.map +0 -1
  151. package/dist/selection-C4UhsMOO.d.ts.map +0 -1
  152. package/dist/selection-DPg0uyJf.d.cts.map +0 -1
@@ -38,9 +38,9 @@ export type PatchEntityParameters = {
38
38
  * Attributes to write, keyed by name. An existing attribute is overwritten — including with a
39
39
  * different type — and a name the entity does not carry yet is added.
40
40
  *
41
- * Values are the tagged constructors from `@arkiv-network/sdk/attr` — `i32`, `u256`, `dec`, `str`,
42
- * `addr`, `key`, `bytes32`, `bool` — or a bare `boolean`, `number`, `bigint` or `string` where the
43
- * type is unambiguous.
41
+ * Values are the tagged constructors from `@arkiv-network/sdk/attr` — `i32`, `u64`, `u256`,
42
+ * `dec`, `str`, `addr`, `key`, `bytes32`, `bool` — or a bare `boolean`, `number`, `bigint` or
43
+ * `string` where the type is unambiguous.
44
44
  *
45
45
  * @throws {InvalidAttributeNameError} If a name violates the attribute-name grammar.
46
46
  * @throws {InvalidValueError} If a value does not fit the type it names or defaults to.
@@ -10,6 +10,10 @@ import { str, toValue, type ValueInput } from "./values"
10
10
  */
11
11
  export const MAX_ATTRIBUTES = 32
12
12
 
13
+ /**
14
+ * The largest payload an entity may carry, in bytes. The payload travels as the `$payload` system
15
+ * cell, so this is a bound on that one attribute rather than on the operation as a whole.
16
+ */
13
17
  export const MAX_PAYLOAD_BYTES = 128 * 1024
14
18
 
15
19
  /** The system attribute holding an entity's opaque payload. */
@@ -20,7 +24,7 @@ const CONTENT_TYPE_CELL = "$contentType"
20
24
  /**
21
25
  * The attributes written on an entity, as a plain object keyed by attribute name.
22
26
  *
23
- * Values may be tagged ({@link ValueInput}) or bare where the type is unambiguous — see
27
+ * Values may be tagged ({@link ArkivValue}) or bare where the type is unambiguous — see
24
28
  * {@link toValue} for the bare-form defaults.
25
29
  *
26
30
  * @example
@@ -141,8 +145,10 @@ export type MutationInputs = SystemCells & {
141
145
  *
142
146
  * @throws {ConflictingMutationError} If a name appears in both `set` and `unset`.
143
147
  * @throws {InvalidAttributeNameError} If a name violates the attribute-name grammar.
148
+ * @throws {MissingValueError} If a value is `undefined` or `null`.
144
149
  * @throws {InvalidValueError} If a value does not fit its type.
145
- * @throws {TooManyAttributesError} If `set` names more than {@link MAX_ATTRIBUTES} attributes.
150
+ * @throws {TooManyAttributesError} If the mutations exceed {@link MAX_ATTRIBUTES}.
151
+ * @throws {TypeError} If `unset` is not an array.
146
152
  */
147
153
  export function encodeMutations({
148
154
  set,
@@ -32,8 +32,8 @@ export class UntypedValueError extends Error {
32
32
  constructor(input: unknown) {
33
33
  super(
34
34
  `Cannot infer an Arkiv type for ${describe(input)}. ` +
35
- "Attribute values must be a tagged value (i32, u256, dec, str, addr, key, bytes32, bool) " +
36
- "or a bare boolean, number, bigint or string.",
35
+ "Attribute values must be a tagged value (i32, u64, u256, dec, str, addr, key, bytes32, " +
36
+ "bool) or a bare boolean, number, bigint or string.",
37
37
  )
38
38
  this.name = "UntypedValueError"
39
39
  this.input = input
package/src/attr/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Address, Hex } from "viem"
2
+ import type { decUnits } from "./values"
2
3
 
3
4
  /**
4
5
  * The protocol's attribute type vocabulary, mapping each short **tag** (the name used by the
@@ -54,8 +55,7 @@ export function isUserSettable(tag: TypeTag): tag is UserTypeTag {
54
55
  /**
55
56
  * Marks a value as having passed its constructor's validation. The symbol is module-private, so a
56
57
  * hand-written object literal is not assignable to {@link ArkivValue} — every value crossing the
57
- * wire has been range-, length- and format-checked by {@link i32}, {@link u256}, {@link dec}, and
58
- * friends.
58
+ * wire has been range-, length- and format-checked by `i32`, `u256`, `dec`, and friends.
59
59
  */
60
60
  declare const validated: unique symbol
61
61
 
@@ -89,7 +89,7 @@ export type U256Value = Value<"u256", bigint>
89
89
  export type DecValue = Value<"dec", string>
90
90
  /** 32 opaque bytes, as lowercase `0x` hex. */
91
91
  export type Bytes32Value = Value<"bytes32", Hex>
92
- /** A UTF-8 string of at most 128 bytes. */
92
+ /** A UTF-8 string of at most 128 bytes, free of C0 control characters and DEL. */
93
93
  export type StrValue = Value<"str", string>
94
94
  /** A 20-byte Ethereum address, as EIP-55 checksummed hex. */
95
95
  export type AddrValue = Value<"addr", Address>
@@ -271,7 +271,11 @@ export function decFromUnits(units: bigint): DecValue {
271
271
  *
272
272
  * The limit is on **bytes**, not characters: a string of 128 emoji does not fit.
273
273
  *
274
- * @throws {InvalidValueError} If the string exceeds 128 UTF-8 bytes.
274
+ * C0 control characters and DEL are rejected: the query language writes a `str` as `str('...')`,
275
+ * whose only escape is a doubled quote, so a control character could be stored but never queried.
276
+ *
277
+ * @throws {InvalidValueError} If the value is not a string, holds a control character, or exceeds
278
+ * 128 UTF-8 bytes.
275
279
  *
276
280
  * @example
277
281
  * str("Bob")
@@ -1,5 +1,8 @@
1
1
  import { defineChain } from "viem"
2
2
 
3
+ /**
4
+ * A node running on the local machine
5
+ */
3
6
  export const localhost = defineChain({
4
7
  id: 1337,
5
8
  name: "Localhost",
@@ -35,5 +35,5 @@ test("creates", () => {
35
35
  expect(client.deleteEntity).not.toBeDefined()
36
36
  expect(client.extendEntity).not.toBeDefined()
37
37
  expect(client.changeOwnership).not.toBeDefined()
38
- expect(client.mutateEntities).not.toBeDefined()
38
+ expect(client.executeBatch).not.toBeDefined()
39
39
  })
@@ -23,14 +23,14 @@ export type PublicArkivClient<
23
23
  >
24
24
 
25
25
  /**
26
- * Creates a Public Client with a given [Transport](https://viem.sh/docs/clients/intro) configured for a [Chain](https://viem.sh/docs/clients/chains).
26
+ * Creates a Public Client with a given [Transport](https://viem.sh/docs/clients/intro) configured for a [Chain](https://viem.sh/docs/chains/introduction).
27
27
  *
28
- * - Docs: https://docs.arkiv.network/ts-sdk/clients/public
29
- *
30
- * A Public Client is an interface to "public" [Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/), [Arkiv JSON-RPC API](https://docs.arkiv.network/json-rpc/), and [Cheesecake JSON-RPC API](https://rpc.cheesecake.db-chain.devnet.gobas.me) methods such as retrieving block numbers, transactions, reading from smart contracts, etc through [Public Actions](/docs/actions/public/introduction).
28
+ * A Public Client is an interface to the "public" [Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/)
29
+ * and the Arkiv JSON-RPC API — retrieving block numbers and transactions, reading from
30
+ * contracts, and querying entities — through {@link PublicArkivActions}.
31
31
  *
32
32
  * @param parameters - Configuration object for the public client (chain, transport, etc.)
33
- * @returns A Arkiv Public Client. {@link PublicArkivClient}
33
+ * @returns An Arkiv Public Client. {@link PublicArkivClient}
34
34
  *
35
35
  * @example
36
36
  * import { createPublicClient } from "@arkiv-network/sdk"
@@ -29,6 +29,6 @@ test("creates", () => {
29
29
  expect(typeof client.extendEntity).toBe("function")
30
30
  expect(client.changeOwnership).toBeDefined()
31
31
  expect(typeof client.changeOwnership).toBe("function")
32
- expect(client.mutateEntities).toBeDefined()
33
- expect(typeof client.mutateEntities).toBe("function")
32
+ expect(client.executeBatch).toBeDefined()
33
+ expect(typeof client.executeBatch).toBe("function")
34
34
  })
@@ -24,16 +24,14 @@ export type WalletArkivClient<
24
24
 
25
25
  /**
26
26
  * Creates a Wallet Client with a given [Transport](https://viem.sh/docs/clients/intro) configured
27
- * for a [Chain](https://viem.sh/docs/clients/chains) and an account.
28
- *
29
- * - Docs: https://docs.arkiv.network/ts-sdk/clients/wallet
27
+ * for a [Chain](https://viem.sh/docs/chains/introduction) and an account.
30
28
  *
31
29
  * A Wallet Client signs and sends transactions, so it carries an `account` and exposes everything
32
30
  * that mutates state — `createEntity`, `patchEntity`, `deleteEntity`, `extendEntity`,
33
- * `changeOwnership` and `mutateEntities` — on top of the read actions a Public Client has.
31
+ * `changeOwnership` and `executeBatch` — on top of the read actions a Public Client has.
34
32
  *
35
33
  * @param parameters - Configuration object for the wallet client (chain, transport, account, etc.)
36
- * @returns A Arkiv Wallet Client. {@link WalletArkivClient}
34
+ * @returns An Arkiv Wallet Client. {@link WalletArkivClient}
37
35
  *
38
36
  * @example
39
37
  * import { createWalletClient } from "@arkiv-network/sdk"
@@ -70,7 +70,7 @@ export type PublicAdvancedActions = {
70
70
  * costing the minimum number of RPC calls — down to one per transaction when you supply what the
71
71
  * SDK would otherwise look up.
72
72
  *
73
- * The everyday actions (`createEntity`, `mutateEntities`, …) bundle send + wait + decode and are
73
+ * The everyday actions (`createEntity`, `executeBatch`, …) bundle send + wait + decode and are
74
74
  * the right default. This namespace unbundles them for callers optimising their RPC budget.
75
75
  *
76
76
  * @example Minimal-call flow: one send, one ping, done.
@@ -1,5 +1,5 @@
1
1
  import type { Account, Address, Chain, Client, Hex, PublicActions, Transport } from "viem"
2
- import { getBlockTiming } from "../../actions/public/getBlockTiming"
2
+ import { type GetBlockTimingReturnType, getBlockTiming } from "../../actions/public/getBlockTiming"
3
3
  import { getEntity } from "../../actions/public/getEntity"
4
4
  import { getEntityCount } from "../../actions/public/getEntityCount"
5
5
  import { getEntityNonce } from "../../actions/public/getEntityNonce"
@@ -13,6 +13,7 @@ import {
13
13
  type WatchEntityEventsParameters,
14
14
  watchEntityEvents,
15
15
  } from "../../actions/public/watchEntityEvents"
16
+ import type { SaltInput } from "../../entity/key"
16
17
  import type { Expression } from "../../query/expression"
17
18
  import { SelectQueryBuilder } from "../../query/queryBuilder"
18
19
  import type { EntitySelection, FullEntity, ProjectedEntity, SelectArg } from "../../query/selection"
@@ -39,11 +40,15 @@ export type PublicArkivActions<
39
40
  /**
40
41
  * Returns the entity with the given key.
41
42
  *
42
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/public/getEntity
43
+ * - JSON-RPC Methods: `arkiv_query`
43
44
  *
44
45
  * @param key - The entity key (hex string)
45
46
  * @returns The entity with the given key, with every field populated. {@link FullEntity}
46
47
  *
48
+ * @throws {InvalidValueError} If the key is not 32 bytes.
49
+ * @throws {NoEntityFoundError} If no live entity has that key — it never existed, or it was
50
+ * deleted or has expired.
51
+ *
47
52
  * @example
48
53
  * import { createPublicClient } from "@arkiv-network/sdk"
49
54
  * import { cheesecake } from "@arkiv-network/sdk/chains"
@@ -70,7 +75,7 @@ export type PublicArkivActions<
70
75
  * read entities. You declare up front which parts of an entity you want returned, so results
71
76
  * always contain exactly the data you asked for.
72
77
  *
73
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/public/query
78
+ * - JSON-RPC Methods: `arkiv_query`
74
79
  *
75
80
  * @param selection - What to include in the results. Omit it (or pass `"*"`) to select everything,
76
81
  * or pass an object to select specific parts (at least one field is required). Every part is
@@ -131,7 +136,7 @@ export type PublicArkivActions<
131
136
  /**
132
137
  * Runs one query and returns one page, with no builder in between.
133
138
  *
134
- * Use {@link select} for anything typed — this returns full {@link Entity} objects whatever the
139
+ * Use {@link PublicArkivActions.select} for anything typed — this returns full {@link Entity} objects whatever the
135
140
  * selection, and takes a raw string as an escape hatch for a query built elsewhere. A raw string
136
141
  * goes to the node exactly as written, with none of the name, type or operator checks the
137
142
  * expression combinators apply.
@@ -225,7 +230,7 @@ export type PublicArkivActions<
225
230
  * import { key } from "@arkiv-network/sdk/attr"
226
231
  *
227
232
  * const [parent, child] = await client.predictEntityKeys({ owner: account.address, count: 2 })
228
- * await wallet.mutateEntities({
233
+ * await wallet.executeBatch({
229
234
  * creates: [
230
235
  * { payload, contentType, expires, salt: parent.salt },
231
236
  * {
@@ -240,7 +245,7 @@ export type PublicArkivActions<
240
245
  */
241
246
  predictEntityKeys: <
242
247
  const TCount extends number = number,
243
- const TSalts extends readonly bigint[] | undefined = undefined,
248
+ const TSalts extends readonly SaltInput[] | undefined = undefined,
244
249
  >(
245
250
  parameters: PredictEntityKeysParameters<TCount, TSalts>,
246
251
  ) => Promise<PredictEntityKeysReturnType<TCount, TSalts>>
@@ -265,11 +270,7 @@ export type PublicArkivActions<
265
270
  * // blockDuration: 2, // in seconds
266
271
  * // }
267
272
  */
268
- getBlockTiming: () => Promise<{
269
- currentBlock: bigint
270
- currentBlockTime: number
271
- blockDuration: number
272
- }>
273
+ getBlockTiming: () => Promise<GetBlockTimingReturnType>
273
274
 
274
275
  /**
275
276
  * Watches entity events, calling the handlers you pass as they arrive.
@@ -325,7 +326,7 @@ export function publicArkivActions<
325
326
  getEntityNonce: (owner: Address) => getEntityNonce(client, owner),
326
327
  predictEntityKeys: <
327
328
  const TCount extends number = number,
328
- const TSalts extends readonly bigint[] | undefined = undefined,
329
+ const TSalts extends readonly SaltInput[] | undefined = undefined,
329
330
  >(
330
331
  parameters: PredictEntityKeysParameters<TCount, TSalts>,
331
332
  ) => predictEntityKeys(client, parameters),
@@ -14,16 +14,16 @@ 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
27
  import type { PatchEntityParameters, PatchEntityReturnType } from "../../actions/wallet/patchEntity"
28
28
  import { patchEntity } from "../../actions/wallet/patchEntity"
29
29
  import type { TxParams } from "../../types"
@@ -51,12 +51,12 @@ export type WalletArkivActions<
51
51
  /**
52
52
  * Creates a new entity.
53
53
  *
54
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/createEntity
55
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
54
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
56
55
  *
57
56
  * @param data - The entity creation parameters
58
57
  * @param txParams - Optional transaction parameters
59
- * @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}
60
60
  *
61
61
  * @throws {InvalidExpiryError} If the expiry exceeds a protocol bound or would leave the
62
62
  * entity dead on arrival.
@@ -68,8 +68,10 @@ export type WalletArkivActions<
68
68
  * import { i32 } from "@arkiv-network/sdk/attr"
69
69
  * import { cheesecake } from "@arkiv-network/sdk/chains"
70
70
  * import { http } from "viem"
71
+ * import { privateKeyToAccount } from "viem/accounts"
71
72
  *
72
73
  * const client = createWalletClient({
74
+ * account: privateKeyToAccount("0x..."),
73
75
  * chain: cheesecake,
74
76
  * transport: http(),
75
77
  * })
@@ -89,12 +91,11 @@ export type WalletArkivActions<
89
91
  * Applies a patch to the entity with the given key: sets some fields, unsets others, and leaves
90
92
  * everything it does not name alone.
91
93
  *
92
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/patchEntity
93
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
94
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
94
95
  *
95
96
  * @param data - The entity key and the mutations to apply
96
97
  * @param txParams - Optional transaction parameters
97
- * @returns The entity key and the transaction hash
98
+ * @returns The patched entity's key and the transaction hash. {@link PatchEntityReturnType}
98
99
  *
99
100
  * @throws {EmptyPatchError} If the patch has nothing to apply.
100
101
  * @throws {ConflictingMutationError} If a name appears in both `set` and `unset`.
@@ -106,8 +107,10 @@ export type WalletArkivActions<
106
107
  * import { i32 } from "@arkiv-network/sdk/attr"
107
108
  * import { cheesecake } from "@arkiv-network/sdk/chains"
108
109
  * import { http } from "viem"
110
+ * import { privateKeyToAccount } from "viem/accounts"
109
111
  *
110
112
  * const client = createWalletClient({
113
+ * account: privateKeyToAccount("0x..."),
111
114
  * chain: cheesecake,
112
115
  * transport: http(),
113
116
  * })
@@ -127,19 +130,20 @@ export type WalletArkivActions<
127
130
  /**
128
131
  * Deletes the entity with the given key.
129
132
  *
130
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/deleteEntity
131
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
133
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
132
134
  *
133
135
  * @param data - The entity deletion parameters
134
136
  * @param txParams - Optional transaction parameters
135
- * @returns The deleted entity with transaction hash
137
+ * @returns The deleted entity's key and the transaction hash. {@link DeleteEntityReturnType}
136
138
  *
137
139
  * @example
138
140
  * import { createWalletClient } from "@arkiv-network/sdk"
139
141
  * import { cheesecake } from "@arkiv-network/sdk/chains"
140
142
  * import { http } from "viem"
143
+ * import { privateKeyToAccount } from "viem/accounts"
141
144
  *
142
145
  * const client = createWalletClient({
146
+ * account: privateKeyToAccount("0x..."),
143
147
  * chain: cheesecake,
144
148
  * transport: http(),
145
149
  * })
@@ -158,12 +162,12 @@ export type WalletArkivActions<
158
162
  * than adding to what the entity has left, and `atBlock` / `atDate` pin an absolute deadline.
159
163
  * The engine rejects an extension that would not move the expiry later.
160
164
  *
161
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/extendEntity
162
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
165
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
163
166
  *
164
167
  * @param data - The entity key and its new lifetime
165
168
  * @param txParams - Optional transaction parameters
166
- * @returns The entity key, transaction hash, and the block it is now expected to expire at
169
+ * @returns The entity's key, the transaction hash, and the block it is now expected to expire
170
+ * at. {@link ExtendEntityReturnType}
167
171
  *
168
172
  * @throws {InvalidExpiryError} If the expiry is malformed, exceeds a protocol bound, or would
169
173
  * leave the entity dead on arrival.
@@ -172,8 +176,10 @@ export type WalletArkivActions<
172
176
  * import { createWalletClient, ExpirationTime } from "@arkiv-network/sdk"
173
177
  * import { cheesecake } from "@arkiv-network/sdk/chains"
174
178
  * import { http } from "viem"
179
+ * import { privateKeyToAccount } from "viem/accounts"
175
180
  *
176
181
  * const client = createWalletClient({
182
+ * account: privateKeyToAccount("0x..."),
177
183
  * chain: cheesecake,
178
184
  * transport: http(),
179
185
  * })
@@ -188,14 +194,13 @@ export type WalletArkivActions<
188
194
  ) => Promise<ExtendEntityReturnType>
189
195
 
190
196
  /**
191
- * Changes the ownership of the entity with the given address.
197
+ * Hands the entity with the given key to a new owner.
192
198
  *
193
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/changeOwnership
194
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
199
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
195
200
  *
196
201
  * @param data - The ownership change parameters
197
202
  * @param txParams - Optional transaction parameters
198
- * @returns The entity with updated ownership and transaction hash
203
+ * @returns The entity's key and the transaction hash. {@link ChangeOwnershipReturnType}
199
204
  */
200
205
  changeOwnership: (
201
206
  data: ChangeOwnershipParameters,
@@ -203,17 +208,18 @@ export type WalletArkivActions<
203
208
  ) => Promise<ChangeOwnershipReturnType>
204
209
 
205
210
  /**
206
- * 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.
207
213
  *
208
- * - Docs: https://docs.arkiv.network/ts-sdk/actions/wallet/mutateEntities
209
- * - JSON-RPC Methods: [`eth_sendRawTransaction`](https://docs.arkiv.network/dev/json-rpc-api/#mutateEntities)
214
+ * - JSON-RPC Methods: `eth_sendRawTransaction`
210
215
  *
211
216
  * Every operation lands in one transaction, so the whole batch applies or none of it does.
212
217
  * At least one operation is required.
213
218
  *
214
- * @param data - The mutation parameters (creates, patches, deletes, extensions, ownershipChanges)
219
+ * @param data - The batch parameters (creates, patches, deletes, extensions, ownershipChanges)
215
220
  * @param txParams - Optional transaction parameters
216
- * @returns The mutation result with transaction hash
221
+ * @returns The transaction hash, plus the keys touched by each kind of operation.
222
+ * {@link ExecuteBatchReturnType}
217
223
  *
218
224
  * @throws {InvalidExpiryError} If an expiry exceeds a protocol bound or would leave the entity
219
225
  * dead on arrival.
@@ -223,12 +229,14 @@ export type WalletArkivActions<
223
229
  * import { createWalletClient, ExpirationTime, jsonToPayload } from "@arkiv-network/sdk"
224
230
  * import { cheesecake } from "@arkiv-network/sdk/chains"
225
231
  * import { http } from "viem"
232
+ * import { privateKeyToAccount } from "viem/accounts"
226
233
  *
227
234
  * const client = createWalletClient({
235
+ * account: privateKeyToAccount("0x..."),
228
236
  * chain: cheesecake,
229
237
  * transport: http(),
230
238
  * })
231
- * const { txHash, createdEntities } = await client.mutateEntities({
239
+ * const { txHash, createdEntities } = await client.executeBatch({
232
240
  * creates: [{
233
241
  * payload: jsonToPayload({ entityType: "testType", entityId: "testId" }),
234
242
  * contentType: "application/json",
@@ -244,10 +252,10 @@ export type WalletArkivActions<
244
252
  * ownershipChanges: [{ entityKey: keyToHandOver, newOwner }],
245
253
  * })
246
254
  */
247
- mutateEntities: (
248
- data: MutateEntitiesParameters,
255
+ executeBatch: (
256
+ data: ExecuteBatchParameters,
249
257
  txParams?: TxParams,
250
- ) => Promise<MutateEntitiesReturnType>
258
+ ) => Promise<ExecuteBatchReturnType>
251
259
 
252
260
  /**
253
261
  * The advanced path: build, send, ping and read mutation results as separate steps, each
@@ -279,8 +287,8 @@ export function walletArkivActions<
279
287
  extendEntity(client, data, txParams),
280
288
  changeOwnership: (data: ChangeOwnershipParameters, txParams?: TxParams) =>
281
289
  changeOwnership(client, data, txParams),
282
- mutateEntities: (data: MutateEntitiesParameters, txParams?: TxParams) =>
283
- mutateEntities(client, data, txParams),
290
+ executeBatch: (data: ExecuteBatchParameters, txParams?: TxParams) =>
291
+ executeBatch(client, data, txParams),
284
292
  advanced: walletAdvancedActions(client),
285
293
  }
286
294
  }
@@ -4,7 +4,7 @@ import { encodeAttributes } from "../attr/attributes"
4
4
  import { dec, i32, str, u256 } from "../attr/values"
5
5
  import { InvalidCreationFlagsError } from "./errors"
6
6
  import { decodeCreationFlags, encodeCreationFlags } from "./flags"
7
- import { MAX_SALT, predictEntityKey, randomSalt, validateSalt } from "./key"
7
+ import { MAX_SALT, NO_SALT, predictEntityKey, randomSalt, resolveSalt, validateSalt } from "./key"
8
8
  import { createOperation } from "./operations"
9
9
  import { OperationType } from "./params"
10
10
 
@@ -73,6 +73,18 @@ describe("salt", () => {
73
73
  expect(() => validateSalt(MAX_SALT + 1n)).toThrow(/uint128/)
74
74
  expect(() => validateSalt(-1n)).toThrow(/uint128/)
75
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
+ })
76
88
  })
77
89
 
78
90
  describe("predictEntityKey", () => {
@@ -86,6 +98,13 @@ describe("predictEntityKey", () => {
86
98
  ).not.toBe(predictEntityKey(base))
87
99
  })
88
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
+
89
108
  it("separates chains through the domain", () => {
90
109
  const base = { owner: OWNER, nonce: 0n, salt: 0n } as const
91
110
  expect(predictEntityKey({ ...base, chainId: 1 })).not.toBe(
@@ -16,10 +16,11 @@ export class InvalidCreationFlagsError extends Error {
16
16
 
17
17
  /** A key salt was outside the `uint128` the wire carries. */
18
18
  export class InvalidSaltError extends Error {
19
- constructor(salt: bigint) {
19
+ constructor(salt: unknown) {
20
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.`,
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.`,
23
24
  )
24
25
  this.name = "InvalidSaltError"
25
26
  }
@@ -1,4 +1,5 @@
1
1
  import { BLOCK_TIME } from "../consts"
2
+ import type { ExpirationTime } from "../utils/expirationTime"
2
3
  import { InvalidExpiryError } from "./errors"
3
4
  import { MAX_EXPIRES_AT } from "./params"
4
5
 
@@ -10,6 +10,7 @@ export type { EncodedExpiry, Expiry, ExpiryContext, Lifetime, ResolvedExpiry } f
10
10
  export { resolveExpiry, toBlocks } from "./expiry"
11
11
  export type { CreationFlags, ResolvedCreationFlags } from "./flags"
12
12
  export { decodeCreationFlags, encodeCreationFlags } from "./flags"
13
- export { MAX_SALT, predictEntityKey, randomSalt } from "./key"
13
+ export type { SaltInput } from "./key"
14
+ export { MAX_SALT, NO_SALT, predictEntityKey, randomSalt } from "./key"
14
15
  export type { Operation } from "./operations"
15
16
  export { MAX_EXPIRES_AT, OperationType } from "./params"
package/src/entity/key.ts CHANGED
@@ -1,10 +1,23 @@
1
1
  import { type Address, encodePacked, type Hex, keccak256 } from "viem"
2
+ import type { CreateEntityParameters } from "../actions/wallet/createEntity"
2
3
  import { ARKIV_ADDRESS } from "../consts"
3
4
  import { InvalidSaltError, NoRandomSourceError } from "./errors"
4
5
 
5
6
  /** The widest salt the `uint128` wire field carries. */
6
7
  export const MAX_SALT = 2n ** 128n - 1n
7
8
 
9
+ /**
10
+ * Opts a create out of salting, as {@link CreateEntityParameters.salt}: the key is then derived
11
+ * from the owner and its nonce alone, so anyone who knows both can work it out before the create
12
+ * lands.
13
+ */
14
+ export const NO_SALT = Symbol("NO_SALT")
15
+
16
+ /**
17
+ * A salt as a caller expresses it: an explicit `uint128`, or {@link NO_SALT} to opt out of salting.
18
+ */
19
+ export type SaltInput = bigint | typeof NO_SALT
20
+
8
21
  /**
9
22
  * A cryptographically random 128-bit salt — what {@link CreateEntityParameters.salt} defaults to.
10
23
  *
@@ -25,6 +38,13 @@ export function randomSalt(): bigint {
25
38
  return salt
26
39
  }
27
40
 
41
+ /**
42
+ * Resolves a caller-supplied salt to a `uint128`.
43
+ */
44
+ export function resolveSalt(salt: SaltInput): bigint {
45
+ return salt === NO_SALT ? 0n : validateSalt(salt)
46
+ }
47
+
28
48
  /** Validates a caller-supplied salt against the `uint128` wire field. */
29
49
  export function validateSalt(salt: bigint): bigint {
30
50
  if (typeof salt !== "bigint" || salt < 0n || salt > MAX_SALT) {
@@ -54,15 +74,15 @@ export function predictEntityKey({
54
74
  * create in a batch uses `nonce + n`.
55
75
  */
56
76
  nonce: bigint
57
- /** The salt the create will carry. */
58
- salt: bigint
77
+ /** The salt the create will carry, or {@link NO_SALT} if it carries none. */
78
+ salt: SaltInput
59
79
  /** The chain the entity is being created on — half of the derivation domain. */
60
80
  chainId: number
61
81
  }): Hex {
62
82
  return keccak256(
63
83
  encodePacked(
64
84
  ["uint256", "address", "address", "uint64", "uint128"],
65
- [BigInt(chainId), ARKIV_ADDRESS, owner, nonce, validateSalt(salt)],
85
+ [BigInt(chainId), ARKIV_ADDRESS, owner, nonce, resolveSalt(salt)],
66
86
  ),
67
87
  )
68
88
  }
@@ -1,6 +1,7 @@
1
1
  import { type Address, encodeAbiParameters, type Hex, parseAbi, parseAbiParameters } from "viem"
2
2
  import type { AbiAttribute } from "../attr/codec"
3
3
  import type { EncodedExpiry } from "./expiry"
4
+ import type { predictEntityKey } from "./key"
4
5
  import { OperationType } from "./params"
5
6
 
6
7
  /**
@@ -10,7 +10,7 @@ import type { TypeTag } from "../attr"
10
10
  * | | `=` `!=` | `<` `<=` `>` `>=` | `STARTSWITH` |
11
11
  * | --- | --- | --- | --- |
12
12
  * | `bool` | yes | no | no |
13
- * | `i32` `u256` `dec` | yes | yes | no |
13
+ * | `i32` `u64` `u256` `dec` | yes | yes | no |
14
14
  * | `str` | yes | no | yes |
15
15
  * | `addr` `key` `bytes32` | yes | no | no |
16
16
  */