@arkiv-network/sdk 0.8.0-advanced.2 → 0.8.0-dev.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.
- package/README.md +0 -42
- package/dist/attr/index.cjs +33 -34
- package/dist/attr/index.d.cts +1 -1
- package/dist/attr/index.d.ts +1 -2
- package/dist/attr/index.js +1 -2
- package/dist/{attributes-Df9IsCVs.cjs → attr-BXTkpnoc.cjs} +13 -3
- package/dist/{attributes-BhY5DwJp.js → attr-tmyREHtw.js} +14 -4
- package/dist/attr-tmyREHtw.js.map +1 -0
- package/dist/chains/index.cjs +1 -1
- package/dist/chains/index.d.cts +3 -0
- package/dist/chains/index.d.cts.map +1 -1
- package/dist/chains/index.d.ts +3 -0
- package/dist/chains/index.d.ts.map +1 -1
- package/dist/chains/index.js +1 -1
- package/dist/{chains-BYRTTipa.cjs → chains-BfNQliHU.cjs} +3 -0
- package/dist/{chains-BJrZs6sG.js → chains-Du8tql7I.js} +4 -1
- package/dist/chains-Du8tql7I.js.map +1 -0
- package/dist/{arkivTransactions-NtGD6uk5.d.ts → changeOwnership-B0ar96ub.d.ts} +74 -123
- package/dist/changeOwnership-B0ar96ub.d.ts.map +1 -0
- package/dist/{arkivTransactions-D74gERHG.d.cts → changeOwnership-BofkLnXJ.d.cts} +74 -123
- package/dist/changeOwnership-BofkLnXJ.d.cts.map +1 -0
- package/dist/entity/index.cjs +9 -9
- package/dist/entity/index.d.cts +3 -3
- package/dist/entity/index.d.ts +4 -5
- package/dist/entity/index.js +5 -6
- package/dist/{entity-BYQR1uoe.js → entity-BIp_df9u.js} +1 -1
- package/dist/{entity-BYQR1uoe.js.map → entity-BIp_df9u.js.map} +1 -1
- package/dist/{key-D1Bh7MF0.js → entity-CniBgBSd.js} +31 -19
- package/dist/entity-CniBgBSd.js.map +1 -0
- package/dist/{key-WKhXnXuU.cjs → entity-DJ0Gml9c.cjs} +37 -19
- package/dist/{errors-LTe7bOYS.js → errors-D2E1GRjw.js} +2 -2
- package/dist/errors-D2E1GRjw.js.map +1 -0
- package/dist/{errors-BVbjKVMN.cjs → errors-DhV00966.cjs} +1 -1
- package/dist/{expiry-nDbCgZX4.d.ts → expiry-BxOlvVWC.d.ts} +1 -1
- package/dist/{expiry-nDbCgZX4.d.ts.map → expiry-BxOlvVWC.d.ts.map} +1 -1
- package/dist/{expiry-BELY2Kpw.cjs → expiry-CG9GhVXv.cjs} +1 -1
- package/dist/{expiry-BjpxZZQU.js → expiry-Cgdb_dbq.js} +2 -2
- package/dist/expiry-Cgdb_dbq.js.map +1 -0
- package/dist/expiry-DRV29F1r.d.cts.map +1 -1
- package/dist/{flags-CRjPpoh3.d.cts → flags-0Vl5rrHq.d.cts} +1 -1
- package/dist/{flags-CRjPpoh3.d.cts.map → flags-0Vl5rrHq.d.cts.map} +1 -1
- package/dist/{flags-kr276q6i.cjs → flags-DCD26uhe.cjs} +1 -1
- package/dist/{flags-DFBs2J94.js → flags-DUT2gHEU.js} +2 -2
- package/dist/{flags-DFBs2J94.js.map → flags-DUT2gHEU.js.map} +1 -1
- package/dist/{flags-5dTNRnow.d.ts → flags-wmcMoAnN.d.ts} +1 -1
- package/dist/{flags-5dTNRnow.d.ts.map → flags-wmcMoAnN.d.ts.map} +1 -1
- package/dist/{index-fX_XyqLp.d.ts → index-BGp2plHs.d.cts} +15 -13
- package/dist/index-BGp2plHs.d.cts.map +1 -0
- package/dist/{index-DHDud2hC.d.ts → index-BKB875Um.d.ts} +5 -7
- package/dist/{index-DHDud2hC.d.ts.map → index-BKB875Um.d.ts.map} +1 -1
- package/dist/{index-DvYvrkdk.d.ts → index-CIcHPx2j.d.cts} +110 -6
- package/dist/index-CIcHPx2j.d.cts.map +1 -0
- package/dist/{index-CV5zCdpa.d.cts → index-Cd2aaWme.d.ts} +13 -6
- package/dist/index-Cd2aaWme.d.ts.map +1 -0
- package/dist/{index-CA4V7Pdh.d.cts → index-ezMZ2-JH.d.ts} +15 -13
- package/dist/index-ezMZ2-JH.d.ts.map +1 -0
- package/dist/{index-BF7Mq9Is.d.cts → index-rINthNuU.d.cts} +5 -7
- package/dist/{index-BF7Mq9Is.d.cts.map → index-rINthNuU.d.cts.map} +1 -1
- package/dist/index.cjs +433 -139
- package/dist/index.d.cts +58 -164
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +59 -166
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +376 -75
- package/dist/index.js.map +1 -1
- package/dist/{payload-CcUj85ys.d.ts → payload-CGvVPZQX.d.ts} +61 -2
- package/dist/payload-CGvVPZQX.d.ts.map +1 -0
- package/dist/{payload-Bz6651ax.js → payload-DE7wMztL.js} +28 -3
- package/dist/payload-DE7wMztL.js.map +1 -0
- package/dist/{payload-BNoud2KZ.d.cts → payload-Dfu30njp.d.cts} +60 -1
- package/dist/payload-Dfu30njp.d.cts.map +1 -0
- package/dist/{payload-KC14SDlN.cjs → payload-DqFweqiO.cjs} +27 -2
- package/dist/query/index.cjs +5 -7
- package/dist/query/index.d.cts +4 -4
- package/dist/query/index.d.ts +4 -5
- package/dist/query/index.js +5 -7
- package/dist/{queryBuilder-DM6AWCBa.js → queryBuilder-wvA8sn--.js} +169 -23
- package/dist/queryBuilder-wvA8sn--.js.map +1 -0
- package/dist/{queryBuilder-DbUMEZln.cjs → queryBuilder-zHGhDmaG.cjs} +215 -33
- package/dist/{selection-DPg0uyJf.d.cts → selection-Bv8_o-7i.d.cts} +76 -68
- package/dist/selection-Bv8_o-7i.d.cts.map +1 -0
- package/dist/{selection-C4UhsMOO.d.ts → selection-ox7zdtVm.d.ts} +76 -69
- package/dist/selection-ox7zdtVm.d.ts.map +1 -0
- package/dist/types/index.cjs +2 -2
- package/dist/types/index.d.cts +6 -6
- package/dist/types/index.d.ts +7 -8
- package/dist/types/index.js +2 -2
- package/dist/utils/index.cjs +3 -3
- package/dist/utils/index.d.cts +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +3 -3
- package/package.json +1 -5
- package/src/actions/public/getEntity.ts +1 -0
- package/src/actions/public/predictEntityKeys.test.ts +17 -1
- package/src/actions/public/predictEntityKeys.ts +28 -14
- package/src/actions/public/query.ts +1 -5
- package/src/actions/wallet/createEntity.ts +7 -6
- package/src/actions/wallet/{mutateEntities.test.ts → executeBatch.test.ts} +7 -7
- package/src/actions/wallet/{mutateEntities.ts → executeBatch.ts} +26 -11
- package/src/actions/wallet/extendEntity.ts +1 -0
- package/src/actions/wallet/patchEntity.ts +3 -3
- package/src/attr/attributes.ts +8 -2
- package/src/attr/errors.ts +2 -2
- package/src/attr/types.ts +3 -3
- package/src/attr/values.ts +5 -1
- package/src/chains/localhost.ts +3 -0
- package/src/clients/createPublicClient.test.ts +1 -1
- package/src/clients/createPublicClient.ts +5 -5
- package/src/clients/createWalletClient.test.ts +2 -2
- package/src/clients/createWalletClient.ts +3 -5
- package/src/clients/decorators/arkivPublic.ts +13 -24
- package/src/clients/decorators/arkivWallet.ts +40 -47
- package/src/entity/create.test.ts +20 -1
- package/src/entity/errors.ts +4 -3
- package/src/entity/expiry.ts +1 -0
- package/src/entity/index.ts +2 -2
- package/src/entity/key.ts +23 -3
- package/src/entity/operations.ts +1 -0
- package/src/index.ts +0 -13
- package/src/query/engine.ts +0 -11
- package/src/query/errors.ts +1 -1
- package/src/query/expression.ts +14 -7
- package/src/query/queryBuilder.test.ts +1 -21
- package/src/query/queryBuilder.ts +3 -10
- package/src/types/index.ts +4 -4
- package/src/types/rpcSchema.ts +1 -0
- package/src/types/txParams.ts +8 -0
- package/src/utils/arkivTransactions.test.ts +7 -1
- package/src/utils/arkivTransactions.ts +114 -254
- package/src/utils/expirationTime.ts +39 -0
- package/src/utils/payload.ts +25 -0
- package/dist/actions/advanced/index.cjs +0 -13
- package/dist/actions/advanced/index.d.cts +0 -8
- package/dist/actions/advanced/index.d.ts +0 -9
- package/dist/actions/advanced/index.js +0 -9
- package/dist/advanced-8ZPTmmaN.js +0 -598
- package/dist/advanced-8ZPTmmaN.js.map +0 -1
- package/dist/advanced-fPImL6lW.cjs +0 -656
- package/dist/arkivTransactions-D74gERHG.d.cts.map +0 -1
- package/dist/arkivTransactions-NtGD6uk5.d.ts.map +0 -1
- package/dist/attributes-BhY5DwJp.js.map +0 -1
- package/dist/chains-BJrZs6sG.js.map +0 -1
- package/dist/codec-Car3KkFF.d.ts +0 -102
- package/dist/codec-Car3KkFF.d.ts.map +0 -1
- package/dist/entity-B7G8q_Eo.cjs +0 -0
- package/dist/entity-CD_qFF2M.js +0 -0
- package/dist/errors-LTe7bOYS.js.map +0 -1
- package/dist/expiry-BjpxZZQU.js.map +0 -1
- package/dist/index-B5j76711.d.ts +0 -184
- package/dist/index-B5j76711.d.ts.map +0 -1
- package/dist/index-CA4V7Pdh.d.cts.map +0 -1
- package/dist/index-CV5zCdpa.d.cts.map +0 -1
- package/dist/index-DvYvrkdk.d.ts.map +0 -1
- package/dist/index-EeUO0nnJ.d.cts +0 -184
- package/dist/index-EeUO0nnJ.d.cts.map +0 -1
- package/dist/index-fX_XyqLp.d.ts.map +0 -1
- package/dist/key-D1Bh7MF0.js.map +0 -1
- package/dist/logger-CVpvU8ik.js +0 -153
- package/dist/logger-CVpvU8ik.js.map +0 -1
- package/dist/logger-Cr15lPAQ.cjs +0 -188
- package/dist/payload-BNoud2KZ.d.cts.map +0 -1
- package/dist/payload-Bz6651ax.js.map +0 -1
- package/dist/payload-CcUj85ys.d.ts.map +0 -1
- package/dist/queryBuilder-DM6AWCBa.js.map +0 -1
- package/dist/selection-C4UhsMOO.d.ts.map +0 -1
- package/dist/selection-DPg0uyJf.d.cts.map +0 -1
- package/dist/types-Bu_ZwMbZ.js +0 -0
- package/dist/types-NRqymi6S.cjs +0 -0
- package/src/actions/advanced/advanced.test.ts +0 -258
- package/src/actions/advanced/buildMutation.ts +0 -104
- package/src/actions/advanced/getMutationResult.ts +0 -112
- package/src/actions/advanced/index.ts +0 -21
- package/src/actions/advanced/pingTransaction.ts +0 -43
- package/src/actions/advanced/sendMutation.ts +0 -59
- package/src/clients/decorators/arkivAdvanced.ts +0 -144
- /package/dist/{entity-B00Li04j.cjs → entity-B0t6sN7F.cjs} +0 -0
- /package/dist/{attr-DlmErkvR.cjs → types-Bx1EKe0v.js} +0 -0
- /package/dist/{attr-GcQKatub.js → types-CKPddlfS.cjs} +0 -0
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
ContractFunctionExecutionError,
|
|
4
4
|
ContractFunctionRevertedError,
|
|
5
5
|
decodeEventLog,
|
|
6
|
-
type Hash,
|
|
7
6
|
type Hex,
|
|
8
7
|
parseAbi,
|
|
9
8
|
TransactionExecutionError,
|
|
@@ -20,9 +19,9 @@ import type { ArkivClient } from "../clients/baseClient"
|
|
|
20
19
|
import type { WalletArkivClient } from "../clients/createWalletClient"
|
|
21
20
|
import { ARKIV_ADDRESS } from "../consts"
|
|
22
21
|
import { ENTITY_EVENTS_ABI } from "../entity/events"
|
|
23
|
-
import {
|
|
22
|
+
import { resolveExpiry } from "../entity/expiry"
|
|
24
23
|
import { encodeCreationFlags } from "../entity/flags"
|
|
25
|
-
import { randomSalt,
|
|
24
|
+
import { randomSalt, resolveSalt } from "../entity/key"
|
|
26
25
|
import {
|
|
27
26
|
createOperation,
|
|
28
27
|
deleteOperation,
|
|
@@ -94,30 +93,92 @@ export type SendArkivTransactionResult = {
|
|
|
94
93
|
|
|
95
94
|
export async function sendArkivTransaction(
|
|
96
95
|
client: ArkivClient,
|
|
97
|
-
ops:
|
|
96
|
+
ops: {
|
|
97
|
+
creates?: CreateEntityParameters[]
|
|
98
|
+
patches?: PatchEntityParameters[]
|
|
99
|
+
deletes?: DeleteEntityParameters[]
|
|
100
|
+
extensions?: ExtendEntityParameters[]
|
|
101
|
+
ownershipChanges?: ChangeOwnershipParameters[]
|
|
102
|
+
},
|
|
98
103
|
txParams?: TxParams,
|
|
99
104
|
): Promise<SendArkivTransactionResult> {
|
|
100
105
|
if (!client.account) throw new Error("Account required")
|
|
101
106
|
if (!client.chain) throw new Error("Chain required")
|
|
102
107
|
const walletClient = client as WalletArkivClient
|
|
103
108
|
|
|
104
|
-
const { creates, extensions } = ops
|
|
109
|
+
const { creates, patches, deletes, extensions, ownershipChanges } = ops
|
|
105
110
|
|
|
106
111
|
// The block height, to resolve any relative expiry — which both creates and extensions carry.
|
|
107
|
-
// Skipped entirely for a batch with neither.
|
|
108
|
-
// absolute (where `mutationNeedsBlockNumber` would say no): the head is what makes the local
|
|
109
|
-
// dead-on-arrival check in `resolveExpiry` work, and the everyday path buys that check — the
|
|
110
|
-
// advanced path is where both are traded away for the saved call.
|
|
112
|
+
// Skipped entirely for a batch with neither.
|
|
111
113
|
const currentBlock =
|
|
112
114
|
creates?.length || extensions?.length ? await walletClient.getBlockNumber() : 0n
|
|
113
115
|
|
|
114
|
-
const
|
|
116
|
+
const expiryContext = { currentBlock }
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
const createOps = (creates ?? []).map((item) => {
|
|
119
|
+
validateContentType(item.contentType)
|
|
120
|
+
|
|
121
|
+
const salt = item.salt === undefined ? randomSalt() : resolveSalt(item.salt)
|
|
122
|
+
const expiry = resolveExpiry(item.expires, expiryContext)
|
|
123
|
+
|
|
124
|
+
return createOperation({
|
|
125
|
+
salt,
|
|
126
|
+
expiry,
|
|
127
|
+
creationFlags: encodeCreationFlags(item.flags),
|
|
128
|
+
attributes: encodeAttributes(item.attributes, {
|
|
129
|
+
payload: item.payload,
|
|
130
|
+
contentType: item.contentType,
|
|
131
|
+
}),
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const patchOps = (patches ?? []).map((item) => {
|
|
136
|
+
if (item.contentType !== undefined) validateContentType(item.contentType)
|
|
137
|
+
const mutations = encodeMutations(item)
|
|
138
|
+
if (mutations.length === 0) {
|
|
139
|
+
throw new EmptyPatchError(item.entityKey)
|
|
140
|
+
}
|
|
141
|
+
return patchOperation({ entityKey: item.entityKey, mutations })
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// An extension resolves its expiry exactly as a create does — the engine applies the same
|
|
145
|
+
// `max(expiresAt, currentBlock + minLifetime)` to both — so the same bounds are checked here
|
|
146
|
+
// rather than left to a revert.
|
|
147
|
+
const extendOps = (extensions ?? []).map((item) => {
|
|
148
|
+
const expiry = resolveExpiry(item.expires, expiryContext)
|
|
149
|
+
return extendExpiryOperation({ entityKey: item.entityKey, expiry })
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const operations: Operation[] = [
|
|
153
|
+
...createOps,
|
|
154
|
+
...patchOps,
|
|
155
|
+
...(deletes ?? []).map((item) => deleteOperation({ entityKey: item.entityKey })),
|
|
156
|
+
...extendOps,
|
|
157
|
+
...(ownershipChanges ?? []).map((item) =>
|
|
158
|
+
transferOwnershipOperation({
|
|
159
|
+
entityKey: item.entityKey,
|
|
160
|
+
newOwner: item.newOwner as Address,
|
|
161
|
+
}),
|
|
162
|
+
),
|
|
163
|
+
]
|
|
117
164
|
|
|
118
|
-
|
|
165
|
+
if (operations.length === 0) {
|
|
166
|
+
throw new Error("No operations to perform")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
logger("Sending execute with %d operations %o", operations.length, operations)
|
|
119
170
|
|
|
120
171
|
try {
|
|
172
|
+
const txHash = await walletClient.writeContract({
|
|
173
|
+
address: ARKIV_ADDRESS,
|
|
174
|
+
abi: EXECUTE_ABI,
|
|
175
|
+
functionName: "execute",
|
|
176
|
+
args: [operations],
|
|
177
|
+
account: client.account,
|
|
178
|
+
chain: client.chain,
|
|
179
|
+
...txParams,
|
|
180
|
+
})
|
|
181
|
+
|
|
121
182
|
const receipt = await walletClient.waitForTransactionReceipt({ hash: txHash })
|
|
122
183
|
logger("Tx receipt %o", receipt)
|
|
123
184
|
|
|
@@ -150,43 +211,34 @@ export async function sendArkivTransaction(
|
|
|
150
211
|
return {
|
|
151
212
|
receipt,
|
|
152
213
|
...readAppliedOperations(receipt, {
|
|
153
|
-
creates:
|
|
154
|
-
extensions:
|
|
214
|
+
creates: createOps.length,
|
|
215
|
+
extensions: extendOps.length,
|
|
155
216
|
}),
|
|
156
217
|
}
|
|
157
218
|
} catch (error) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Wraps whatever a write step threw into an {@link EntityMutationError} — engine terms when the
|
|
164
|
-
* revert data decodes, the node's own message otherwise. An {@link EntityMutationError} passes
|
|
165
|
-
* through unchanged, so wrapped steps compose without double-wrapping.
|
|
166
|
-
*/
|
|
167
|
-
function asEntityMutationError(error: unknown): EntityMutationError {
|
|
168
|
-
if (error instanceof EntityMutationError) return error
|
|
169
|
-
|
|
170
|
-
const described = describeEntityRevert(error)
|
|
171
|
-
if (described !== undefined) {
|
|
172
|
-
return new EntityMutationError(`Transaction failed: ${described}`, { cause: error })
|
|
173
|
-
}
|
|
219
|
+
const described = error instanceof EntityMutationError ? undefined : describeEntityRevert(error)
|
|
220
|
+
if (described !== undefined) {
|
|
221
|
+
throw new EntityMutationError(`Transaction failed: ${described}`, { cause: error })
|
|
222
|
+
}
|
|
174
223
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
224
|
+
let message = "Transaction failed"
|
|
225
|
+
if (error instanceof TransactionExecutionError) {
|
|
226
|
+
message += `: ${error.details}`
|
|
227
|
+
} else if (error instanceof ContractFunctionExecutionError) {
|
|
228
|
+
logger("Contract function execution error data:", error.shortMessage)
|
|
229
|
+
if (error.cause instanceof ContractFunctionRevertedError) {
|
|
230
|
+
message += `: ${error.cause.message}`
|
|
231
|
+
} else {
|
|
232
|
+
message += ": Execution error without revert data"
|
|
233
|
+
}
|
|
234
|
+
} else if (error instanceof EntityMutationError) {
|
|
235
|
+
throw error
|
|
236
|
+
} else if (error instanceof Error) {
|
|
237
|
+
message += `: ${error.message}`
|
|
184
238
|
}
|
|
185
|
-
} else if (error instanceof Error) {
|
|
186
|
-
message += `: ${error.message}`
|
|
187
|
-
}
|
|
188
239
|
|
|
189
|
-
|
|
240
|
+
throw new EntityMutationError(message, { cause: error })
|
|
241
|
+
}
|
|
190
242
|
}
|
|
191
243
|
|
|
192
244
|
/**
|
|
@@ -196,11 +248,26 @@ function readAppliedOperations(
|
|
|
196
248
|
receipt: TransactionReceipt,
|
|
197
249
|
expected: { creates: number; extensions: number },
|
|
198
250
|
): Omit<SendArkivTransactionResult, "receipt"> {
|
|
199
|
-
const
|
|
251
|
+
const createdEntityKeys: Hex[] = []
|
|
252
|
+
const createdExpiries: bigint[] = []
|
|
253
|
+
const extendedExpiries: bigint[] = []
|
|
200
254
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
255
|
+
// `?? []` so a receipt without logs is reported by the count check below, which explains what is
|
|
256
|
+
// missing and that the write succeeded, rather than as a TypeError from this loop.
|
|
257
|
+
for (const log of receipt.logs ?? []) {
|
|
258
|
+
// Only the operation address emits these, and a batch's transaction can carry logs from
|
|
259
|
+
// anything else it touched.
|
|
260
|
+
if (log.address.toLowerCase() !== ARKIV_ADDRESS.toLowerCase()) continue
|
|
261
|
+
const decoded = decodeEntityLog(log)
|
|
262
|
+
if (decoded === undefined) continue
|
|
263
|
+
|
|
264
|
+
if (decoded.eventName === "EntityCreated") {
|
|
265
|
+
createdEntityKeys.push(decoded.args.entityKey)
|
|
266
|
+
createdExpiries.push(decoded.args.expiresAt)
|
|
267
|
+
} else if (decoded.eventName === "ExpiryExtended") {
|
|
268
|
+
extendedExpiries.push(decoded.args.expiresAt)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
204
271
|
|
|
205
272
|
// Operations apply in batch order and emit one event each, and creates lead the batch — so these
|
|
206
273
|
// arrive already aligned with the parameters they came from. A count that disagrees means that
|
|
@@ -231,210 +298,3 @@ function assertCount(receipt: TransactionReceipt, what: string, got: number, wan
|
|
|
231
298
|
`say which entity each operation produced, so read them back to find them.`,
|
|
232
299
|
)
|
|
233
300
|
}
|
|
234
|
-
|
|
235
|
-
// ─────────────────────────────────────────────────────────────────────────────────────────────────
|
|
236
|
-
// The seams the advanced path is built on.
|
|
237
|
-
//
|
|
238
|
-
// `sendArkivTransaction` above is the everyday path: encode, send, wait, decode, diagnose. These
|
|
239
|
-
// are the same steps as separate pieces, so `actions/advanced` can spend exactly the RPC calls a
|
|
240
|
-
// caller asks for and no more — without a second copy of the encoding or the event decoding.
|
|
241
|
-
// ─────────────────────────────────────────────────────────────────────────────────────────────────
|
|
242
|
-
|
|
243
|
-
/** The five kinds of entity operation one Arkiv transaction can batch. */
|
|
244
|
-
export type EntityMutationOps = {
|
|
245
|
-
/** The entities to create. */
|
|
246
|
-
creates?: CreateEntityParameters[]
|
|
247
|
-
/** The patches to apply. */
|
|
248
|
-
patches?: PatchEntityParameters[]
|
|
249
|
-
/** The entities to delete. */
|
|
250
|
-
deletes?: DeleteEntityParameters[]
|
|
251
|
-
/** The expiries to set. */
|
|
252
|
-
extensions?: ExtendEntityParameters[]
|
|
253
|
-
/** The ownership transfers to perform. */
|
|
254
|
-
ownershipChanges?: ChangeOwnershipParameters[]
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Whether encoding the batch actually requires the chain head.
|
|
259
|
-
*
|
|
260
|
-
* Only a relative lifetime (`minLifetime > 0`) or a wall-clock deadline (a `Date`) is measured
|
|
261
|
-
* from the current block. A purely absolute `atBlock(n)` expiry encodes the same bytes whatever
|
|
262
|
-
* the head is, so a batch of those — or one with no expiries at all — needs no `eth_blockNumber`.
|
|
263
|
-
*/
|
|
264
|
-
export function mutationNeedsBlockNumber(ops: EntityMutationOps): boolean {
|
|
265
|
-
return [...(ops.creates ?? []), ...(ops.extensions ?? [])].some((item) => {
|
|
266
|
-
const expires = item.expires as Expiry | undefined
|
|
267
|
-
// Malformed expiries are rejected by buildEntityOperations without needing the head.
|
|
268
|
-
if (expires === undefined || typeof expires.minLifetime !== "bigint") return false
|
|
269
|
-
return expires.minLifetime !== 0n || expires.expiresAt instanceof Date
|
|
270
|
-
})
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Encodes a batch into the `execute` operations, in the order the engine applies them: creates,
|
|
275
|
-
* patches, deletes, extensions, ownership transfers.
|
|
276
|
-
*
|
|
277
|
-
* Pure — every check here is local (content types, salts, expiry bounds, non-empty patches and a
|
|
278
|
-
* non-empty batch), so the only chain input is `context.currentBlock`. Callers that carry no
|
|
279
|
-
* relative expiry (see {@link mutationNeedsBlockNumber}) can pass `0n`, which disables the
|
|
280
|
-
* dead-on-arrival pre-check and leaves that to the engine.
|
|
281
|
-
*/
|
|
282
|
-
export function buildEntityOperations(ops: EntityMutationOps, context: ExpiryContext): Operation[] {
|
|
283
|
-
const { creates, patches, deletes, extensions, ownershipChanges } = ops
|
|
284
|
-
|
|
285
|
-
const createOps = (creates ?? []).map((item) => {
|
|
286
|
-
validateContentType(item.contentType)
|
|
287
|
-
|
|
288
|
-
const salt = item.salt === undefined ? randomSalt() : validateSalt(item.salt)
|
|
289
|
-
const expiry = resolveExpiry(item.expires, context)
|
|
290
|
-
|
|
291
|
-
return createOperation({
|
|
292
|
-
salt,
|
|
293
|
-
expiry,
|
|
294
|
-
creationFlags: encodeCreationFlags(item.flags),
|
|
295
|
-
attributes: encodeAttributes(item.attributes, {
|
|
296
|
-
payload: item.payload,
|
|
297
|
-
contentType: item.contentType,
|
|
298
|
-
}),
|
|
299
|
-
})
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
const patchOps = (patches ?? []).map((item) => {
|
|
303
|
-
if (item.contentType !== undefined) validateContentType(item.contentType)
|
|
304
|
-
const mutations = encodeMutations(item)
|
|
305
|
-
if (mutations.length === 0) {
|
|
306
|
-
throw new EmptyPatchError(item.entityKey)
|
|
307
|
-
}
|
|
308
|
-
return patchOperation({ entityKey: item.entityKey, mutations })
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
// An extension resolves its expiry exactly as a create does — the engine applies the same
|
|
312
|
-
// `max(expiresAt, currentBlock + minLifetime)` to both — so the same bounds are checked here
|
|
313
|
-
// rather than left to a revert.
|
|
314
|
-
const extendOps = (extensions ?? []).map((item) => {
|
|
315
|
-
const expiry = resolveExpiry(item.expires, context)
|
|
316
|
-
return extendExpiryOperation({ entityKey: item.entityKey, expiry })
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
const operations: Operation[] = [
|
|
320
|
-
...createOps,
|
|
321
|
-
...patchOps,
|
|
322
|
-
...(deletes ?? []).map((item) => deleteOperation({ entityKey: item.entityKey })),
|
|
323
|
-
...extendOps,
|
|
324
|
-
...(ownershipChanges ?? []).map((item) =>
|
|
325
|
-
transferOwnershipOperation({
|
|
326
|
-
entityKey: item.entityKey,
|
|
327
|
-
newOwner: item.newOwner as Address,
|
|
328
|
-
}),
|
|
329
|
-
),
|
|
330
|
-
]
|
|
331
|
-
|
|
332
|
-
if (operations.length === 0) {
|
|
333
|
-
throw new Error("No operations to perform")
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return operations
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Signs and sends an encoded batch, wrapping any node rejection in {@link EntityMutationError}.
|
|
341
|
-
*
|
|
342
|
-
* The submission half both write paths share: one `writeContract` — an `eth_sendRawTransaction`,
|
|
343
|
-
* plus only the lookups viem needs for whatever `txParams` leaves out — with no waiting and no
|
|
344
|
-
* polling. A rejection is reported in engine terms when the node returned decodable revert data,
|
|
345
|
-
* and with the node's own message otherwise; either way the caller sees an
|
|
346
|
-
* {@link EntityMutationError}.
|
|
347
|
-
*
|
|
348
|
-
* Internal seam shared with `actions/advanced/sendMutation`; not part of the package's public
|
|
349
|
-
* surface.
|
|
350
|
-
*/
|
|
351
|
-
export async function submitMutation(
|
|
352
|
-
client: ArkivClient,
|
|
353
|
-
operations: Operation[],
|
|
354
|
-
txParams?: TxParams,
|
|
355
|
-
): Promise<Hash> {
|
|
356
|
-
if (!client.account) throw new Error("Account required")
|
|
357
|
-
if (!client.chain) throw new Error("Chain required")
|
|
358
|
-
const walletClient = client as WalletArkivClient
|
|
359
|
-
|
|
360
|
-
try {
|
|
361
|
-
return await walletClient.writeContract({
|
|
362
|
-
address: ARKIV_ADDRESS,
|
|
363
|
-
abi: EXECUTE_ABI,
|
|
364
|
-
functionName: "execute",
|
|
365
|
-
args: [operations],
|
|
366
|
-
account: client.account,
|
|
367
|
-
chain: client.chain,
|
|
368
|
-
...txParams,
|
|
369
|
-
})
|
|
370
|
-
} catch (error) {
|
|
371
|
-
throw asEntityMutationError(error)
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** Everything the batch's events say happened, one entry per applied operation, per kind. */
|
|
376
|
-
export type MutationEvents = {
|
|
377
|
-
created: { entityKey: Hex; owner: Address; expiresAt: bigint }[]
|
|
378
|
-
patched: { entityKey: Hex; owner: Address }[]
|
|
379
|
-
deleted: { entityKey: Hex; owner: Address }[]
|
|
380
|
-
extended: { entityKey: Hex; owner: Address; expiresAt: bigint }[]
|
|
381
|
-
ownershipTransferred: { entityKey: Hex; previousOwner: Address; newOwner: Address }[]
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Reads the entity events out of a receipt's logs, exactly as they were emitted.
|
|
386
|
-
*
|
|
387
|
-
* Lenient by design: it reports what the events say and nothing else — no counting against what
|
|
388
|
-
* was asked for (the caller may not have the request at hand) and no error for a receipt with no
|
|
389
|
-
* entity events at all. Logs from other contracts and events this SDK has no name for are skipped.
|
|
390
|
-
*/
|
|
391
|
-
export function collectMutationEvents(logs: TransactionReceipt["logs"]): MutationEvents {
|
|
392
|
-
const events: MutationEvents = {
|
|
393
|
-
created: [],
|
|
394
|
-
patched: [],
|
|
395
|
-
deleted: [],
|
|
396
|
-
extended: [],
|
|
397
|
-
ownershipTransferred: [],
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// `?? []` so a receipt without logs decodes to "nothing happened" rather than a TypeError.
|
|
401
|
-
for (const log of logs ?? []) {
|
|
402
|
-
// Only the operation address emits these, and a batch's transaction can carry logs from
|
|
403
|
-
// anything else it touched.
|
|
404
|
-
if (log.address.toLowerCase() !== ARKIV_ADDRESS.toLowerCase()) continue
|
|
405
|
-
const decoded = decodeEntityLog(log)
|
|
406
|
-
if (decoded === undefined) continue
|
|
407
|
-
|
|
408
|
-
switch (decoded.eventName) {
|
|
409
|
-
case "EntityCreated":
|
|
410
|
-
events.created.push({
|
|
411
|
-
entityKey: decoded.args.entityKey,
|
|
412
|
-
owner: decoded.args.owner,
|
|
413
|
-
expiresAt: decoded.args.expiresAt,
|
|
414
|
-
})
|
|
415
|
-
break
|
|
416
|
-
case "EntityPatched":
|
|
417
|
-
events.patched.push({ entityKey: decoded.args.entityKey, owner: decoded.args.owner })
|
|
418
|
-
break
|
|
419
|
-
case "EntityDeleted":
|
|
420
|
-
events.deleted.push({ entityKey: decoded.args.entityKey, owner: decoded.args.owner })
|
|
421
|
-
break
|
|
422
|
-
case "ExpiryExtended":
|
|
423
|
-
events.extended.push({
|
|
424
|
-
entityKey: decoded.args.entityKey,
|
|
425
|
-
owner: decoded.args.owner,
|
|
426
|
-
expiresAt: decoded.args.expiresAt,
|
|
427
|
-
})
|
|
428
|
-
break
|
|
429
|
-
case "OwnershipTransferred":
|
|
430
|
-
events.ownershipTransferred.push({
|
|
431
|
-
entityKey: decoded.args.entityKey,
|
|
432
|
-
previousOwner: decoded.args.previousOwner,
|
|
433
|
-
newOwner: decoded.args.newOwner,
|
|
434
|
-
})
|
|
435
|
-
break
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return events
|
|
440
|
-
}
|
|
@@ -51,11 +51,50 @@ export const ExpirationTime = {
|
|
|
51
51
|
* @throws {InvalidExpiryError} If the duration is not a positive multiple of the block time.
|
|
52
52
|
*/
|
|
53
53
|
fromSeconds: (seconds: number): Lifetime => lifetimeOf(seconds),
|
|
54
|
+
/**
|
|
55
|
+
* A number of minutes.
|
|
56
|
+
*
|
|
57
|
+
* @throws {InvalidExpiryError} If the duration is not a positive multiple of the block time.
|
|
58
|
+
*/
|
|
54
59
|
fromMinutes: (minutes: number): Lifetime => lifetimeOf(minutes * 60),
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A number of hours.
|
|
63
|
+
*
|
|
64
|
+
* @throws {InvalidExpiryError} If the duration is not a positive multiple of the block time.
|
|
65
|
+
*/
|
|
55
66
|
fromHours: (hours: number): Lifetime => lifetimeOf(hours * 60 * 60),
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A number of days. The everyday form — `fromDays(30)` is what most creates want.
|
|
70
|
+
*
|
|
71
|
+
* @throws {InvalidExpiryError} If the duration is not a positive multiple of the block time.
|
|
72
|
+
*/
|
|
56
73
|
fromDays: (days: number): Lifetime => lifetimeOf(days * 24 * 60 * 60),
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A number of weeks, each of 7 days.
|
|
77
|
+
*
|
|
78
|
+
* @throws {InvalidExpiryError} If the duration is not a positive multiple of the block time.
|
|
79
|
+
*/
|
|
57
80
|
fromWeeks: (weeks: number): Lifetime => lifetimeOf(weeks * 7 * 24 * 60 * 60),
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A number of months, each of exactly **30 days** — calendar months are not all the same length,
|
|
84
|
+
* and a lifetime is a duration rather than a date. Use {@link ExpirationTime.atDate} when the
|
|
85
|
+
* expiry has to land on a particular calendar day.
|
|
86
|
+
*
|
|
87
|
+
* @throws {InvalidExpiryError} If the duration is not a positive multiple of the block time.
|
|
88
|
+
*/
|
|
58
89
|
fromMonths: (months: number): Lifetime => lifetimeOf(months * 30 * 24 * 60 * 60),
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A number of years, each of exactly **365 days** — leap days are not counted, so `fromYears(4)`
|
|
93
|
+
* is a day short of four calendar years. Use {@link ExpirationTime.atDate} for a calendar
|
|
94
|
+
* deadline.
|
|
95
|
+
*
|
|
96
|
+
* @throws {InvalidExpiryError} If the duration is not a positive multiple of the block time.
|
|
97
|
+
*/
|
|
59
98
|
fromYears: (years: number): Lifetime => lifetimeOf(years * 365 * 24 * 60 * 60),
|
|
60
99
|
|
|
61
100
|
/**
|
package/src/utils/payload.ts
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
import { stringToBytes, toBytes } from "viem"
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A JSON-serialisable value as an entity payload — `JSON.stringify` then UTF-8.
|
|
5
|
+
*
|
|
6
|
+
* Pair it with `contentType: "application/json"`, which is what lets `entity.toJson()` read it back.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* await client.createEntity({
|
|
10
|
+
* payload: jsonToPayload({ hello: "world" }),
|
|
11
|
+
* contentType: "application/json",
|
|
12
|
+
* expires: ExpirationTime.fromDays(30),
|
|
13
|
+
* })
|
|
14
|
+
*/
|
|
3
15
|
export function jsonToPayload(json: object): Uint8Array {
|
|
4
16
|
return toBytes(JSON.stringify(json))
|
|
5
17
|
}
|
|
6
18
|
|
|
19
|
+
/**
|
|
20
|
+
* A string as an entity payload, encoded as UTF-8.
|
|
21
|
+
*
|
|
22
|
+
* Hex-looking text is encoded as text: `stringToPayload("0xab")` gives the four bytes of that
|
|
23
|
+
* string, not the single byte `0xab`. Read it back with `entity.toText()`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* await client.createEntity({
|
|
27
|
+
* payload: stringToPayload("hello"),
|
|
28
|
+
* contentType: "text/plain",
|
|
29
|
+
* expires: ExpirationTime.fromDays(30),
|
|
30
|
+
* })
|
|
31
|
+
*/
|
|
7
32
|
export function stringToPayload(data: string): Uint8Array {
|
|
8
33
|
// stringToBytes, not toBytes: toBytes hex-decodes hex-looking strings, which
|
|
9
34
|
// would corrupt payloads like "0xab" instead of UTF-8 encoding them
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
require('../../expiry-BELY2Kpw.cjs');
|
|
2
|
-
const require_advanced = require('../../advanced-fPImL6lW.cjs');
|
|
3
|
-
require('../../attributes-Df9IsCVs.cjs');
|
|
4
|
-
require('../../key-WKhXnXuU.cjs');
|
|
5
|
-
require('../../errors-BVbjKVMN.cjs');
|
|
6
|
-
require('../../flags-kr276q6i.cjs');
|
|
7
|
-
require('../../logger-Cr15lPAQ.cjs');
|
|
8
|
-
|
|
9
|
-
exports.buildMutation = require_advanced.buildMutation;
|
|
10
|
-
exports.decodeMutationResult = require_advanced.decodeMutationResult;
|
|
11
|
-
exports.getMutationResult = require_advanced.getMutationResult;
|
|
12
|
-
exports.pingTransaction = require_advanced.pingTransaction;
|
|
13
|
-
exports.sendMutation = require_advanced.sendMutation;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "../../selection-DPg0uyJf.cjs";
|
|
2
|
-
import "../../index-CV5zCdpa.cjs";
|
|
3
|
-
import "../../expiry-DRV29F1r.cjs";
|
|
4
|
-
import "../../index-CA4V7Pdh.cjs";
|
|
5
|
-
import "../../arkivTransactions-D74gERHG.cjs";
|
|
6
|
-
import "../../flags-CRjPpoh3.cjs";
|
|
7
|
-
import { a as pingTransaction, c as decodeMutationResult, d as BuildMutationReturnType, f as buildMutation, i as PingTransactionReturnType, l as getMutationResult, n as SendMutationReturnType, o as GetMutationResultReturnType, r as sendMutation, s as MutationResult, t as SendMutationOptions, u as BuildMutationOptions } from "../../index-EeUO0nnJ.cjs";
|
|
8
|
-
export { BuildMutationOptions, BuildMutationReturnType, GetMutationResultReturnType, MutationResult, PingTransactionReturnType, SendMutationOptions, SendMutationReturnType, buildMutation, decodeMutationResult, getMutationResult, pingTransaction, sendMutation };
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import "../../selection-C4UhsMOO.js";
|
|
2
|
-
import "../../codec-Car3KkFF.js";
|
|
3
|
-
import "../../expiry-nDbCgZX4.js";
|
|
4
|
-
import "../../index-fX_XyqLp.js";
|
|
5
|
-
import "../../arkivTransactions-NtGD6uk5.js";
|
|
6
|
-
import "../../index-DvYvrkdk.js";
|
|
7
|
-
import "../../flags-5dTNRnow.js";
|
|
8
|
-
import { a as pingTransaction, c as decodeMutationResult, d as BuildMutationReturnType, f as buildMutation, i as PingTransactionReturnType, l as getMutationResult, n as SendMutationReturnType, o as GetMutationResultReturnType, r as sendMutation, s as MutationResult, t as SendMutationOptions, u as BuildMutationOptions } from "../../index-B5j76711.js";
|
|
9
|
-
export { BuildMutationOptions, BuildMutationReturnType, GetMutationResultReturnType, MutationResult, PingTransactionReturnType, SendMutationOptions, SendMutationReturnType, buildMutation, decodeMutationResult, getMutationResult, pingTransaction, sendMutation };
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import "../../expiry-BjpxZZQU.js";
|
|
2
|
-
import { a as buildMutation, i as getMutationResult, n as pingTransaction, r as decodeMutationResult, t as sendMutation } from "../../advanced-8ZPTmmaN.js";
|
|
3
|
-
import "../../attributes-BhY5DwJp.js";
|
|
4
|
-
import "../../key-D1Bh7MF0.js";
|
|
5
|
-
import "../../errors-LTe7bOYS.js";
|
|
6
|
-
import "../../flags-DFBs2J94.js";
|
|
7
|
-
import "../../logger-CVpvU8ik.js";
|
|
8
|
-
|
|
9
|
-
export { buildMutation, decodeMutationResult, getMutationResult, pingTransaction, sendMutation };
|