@arkiv-network/sdk 0.7.0 → 0.8.0-dev.1
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 +29 -16
- package/dist/attr/index.cjs +34 -0
- package/dist/attr/index.d.cts +2 -0
- package/dist/attr/index.d.ts +2 -0
- package/dist/attr/index.js +3 -0
- package/dist/attr-BDZ-APg5.js +809 -0
- package/dist/attr-BDZ-APg5.js.map +1 -0
- package/dist/attr-BvxHN99W.cjs +1029 -0
- package/dist/chains/index.cjs +2 -3
- package/dist/chains/index.d.cts +39 -77
- package/dist/chains/index.d.cts.map +1 -1
- package/dist/chains/index.d.ts +22 -60
- package/dist/chains/index.d.ts.map +1 -1
- package/dist/chains/index.js +2 -2
- package/dist/chains-BJrZs6sG.js +57 -0
- package/dist/chains-BJrZs6sG.js.map +1 -0
- package/dist/chains-BYRTTipa.cjs +73 -0
- package/dist/changeOwnership-B7bEgla4.d.cts +509 -0
- package/dist/changeOwnership-B7bEgla4.d.cts.map +1 -0
- package/dist/changeOwnership-DwgtdSFc.d.ts +509 -0
- package/dist/changeOwnership-DwgtdSFc.d.ts.map +1 -0
- package/dist/entity/index.cjs +19 -0
- package/dist/entity/index.d.cts +4 -0
- package/dist/entity/index.d.ts +4 -0
- package/dist/entity/index.js +6 -0
- package/dist/entity-B0t6sN7F.cjs +71 -0
- package/dist/entity-BIp_df9u.js +67 -0
- package/dist/entity-BIp_df9u.js.map +1 -0
- package/dist/entity-CFhbAv0R.js +70 -0
- package/dist/entity-CFhbAv0R.js.map +1 -0
- package/dist/entity-wfBnZd91.cjs +98 -0
- package/dist/errors-D-eJkGVc.cjs +56 -0
- package/dist/errors-DGB8AgT2.js +33 -0
- package/dist/errors-DGB8AgT2.js.map +1 -0
- package/dist/expiry-BUYIXTGb.cjs +135 -0
- package/dist/expiry-BxOlvVWC.d.ts +79 -0
- package/dist/expiry-BxOlvVWC.d.ts.map +1 -0
- package/dist/expiry-DGRzrSan.js +95 -0
- package/dist/expiry-DGRzrSan.js.map +1 -0
- package/dist/expiry-DRV29F1r.d.cts +79 -0
- package/dist/expiry-DRV29F1r.d.cts.map +1 -0
- package/dist/flags-0Vl5rrHq.d.cts +41 -0
- package/dist/flags-0Vl5rrHq.d.cts.map +1 -0
- package/dist/flags-Ct8k0gpB.cjs +43 -0
- package/dist/flags-ZtlvOIIk.js +33 -0
- package/dist/flags-ZtlvOIIk.js.map +1 -0
- package/dist/flags-wmcMoAnN.d.ts +41 -0
- package/dist/flags-wmcMoAnN.d.ts.map +1 -0
- package/dist/index-Cgw1u5aY.d.cts +278 -0
- package/dist/index-Cgw1u5aY.d.cts.map +1 -0
- package/dist/index-ClgIuxJ8.d.ts +159 -0
- package/dist/index-ClgIuxJ8.d.ts.map +1 -0
- package/dist/index-D0CmtiWT.d.cts +159 -0
- package/dist/index-D0CmtiWT.d.cts.map +1 -0
- package/dist/index-D8LZt-3J.d.cts +417 -0
- package/dist/index-D8LZt-3J.d.cts.map +1 -0
- package/dist/index-DVYFaXNV.d.ts +417 -0
- package/dist/index-DVYFaXNV.d.ts.map +1 -0
- package/dist/index-VCqgn29D.d.ts +278 -0
- package/dist/index-VCqgn29D.d.ts.map +1 -0
- package/dist/index.cjs +675 -357
- package/dist/index.d.cts +251 -203
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +251 -203
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +626 -355
- package/dist/index.js.map +1 -1
- package/dist/payload-Bnw1hrvb.cjs +83 -0
- package/dist/payload-CIxSn_DW.js +67 -0
- package/dist/payload-CIxSn_DW.js.map +1 -0
- package/dist/payload-Dgx3PQeK.d.ts +94 -0
- package/dist/payload-Dgx3PQeK.d.ts.map +1 -0
- package/dist/payload-P0nWyjLg.d.cts +94 -0
- package/dist/payload-P0nWyjLg.d.cts.map +1 -0
- package/dist/query/index.cjs +24 -149
- package/dist/query/index.d.cts +5 -3
- package/dist/query/index.d.ts +5 -3
- package/dist/query/index.js +6 -137
- package/dist/queryBuilder-BXDvX1K_.cjs +1123 -0
- package/dist/queryBuilder-CbLL4pG5.js +963 -0
- package/dist/queryBuilder-CbLL4pG5.js.map +1 -0
- package/dist/selection-BvK6yX0g.d.cts +433 -0
- package/dist/selection-BvK6yX0g.d.cts.map +1 -0
- package/dist/selection-fpmI3ADQ.d.ts +433 -0
- package/dist/selection-fpmI3ADQ.d.ts.map +1 -0
- package/dist/types/index.cjs +4 -1
- package/dist/types/index.d.cts +7 -2
- package/dist/types/index.d.ts +7 -2
- package/dist/types/index.js +4 -1
- package/dist/utils/index.cjs +4 -17
- package/dist/utils/index.d.cts +3 -17
- package/dist/utils/index.d.ts +3 -17
- package/dist/utils/index.js +4 -18
- package/package.json +26 -5
- package/src/actions/public/getEntity.ts +27 -43
- package/src/actions/public/getEntityNonce.ts +30 -0
- package/src/actions/public/predictEntityKeys.test.ts +143 -0
- package/src/actions/public/predictEntityKeys.ts +138 -0
- package/src/actions/public/query.ts +33 -69
- package/src/actions/public/watchEntityEvents.test.ts +414 -0
- package/src/actions/public/watchEntityEvents.ts +225 -0
- package/src/actions/wallet/changeOwnership.ts +8 -8
- package/src/actions/wallet/createEntity.ts +104 -26
- package/src/actions/wallet/deleteEntity.ts +2 -3
- package/src/actions/wallet/extendEntity.ts +39 -16
- package/src/actions/wallet/mutateEntities.test.ts +76 -0
- package/src/actions/wallet/mutateEntities.ts +29 -74
- package/src/actions/wallet/patchEntity.ts +90 -0
- package/src/attr/attributes.test.ts +120 -0
- package/src/attr/attributes.ts +190 -0
- package/src/attr/codec.test.ts +162 -0
- package/src/attr/codec.ts +220 -0
- package/src/attr/decimal.ts +86 -0
- package/src/attr/errors.ts +145 -0
- package/src/attr/index.ts +50 -0
- package/src/attr/names.ts +106 -0
- package/src/attr/types.ts +150 -0
- package/src/attr/values.test.ts +271 -0
- package/src/attr/values.ts +502 -0
- package/src/chains/cheesecake.ts +22 -0
- package/src/chains/index.ts +1 -2
- package/src/clients/createPublicClient.test.ts +3 -5
- package/src/clients/createPublicClient.ts +5 -4
- package/src/clients/createWalletClient.test.ts +2 -2
- package/src/clients/createWalletClient.ts +13 -7
- package/src/clients/decorators/arkivPublic.ts +160 -139
- package/src/clients/decorators/arkivWallet.ts +91 -95
- package/src/consts.ts +2 -1
- package/src/entity/create.test.ts +184 -0
- package/src/entity/errors.ts +38 -0
- package/src/entity/events.ts +16 -0
- package/src/entity/expiry.test.ts +195 -0
- package/src/entity/expiry.ts +181 -0
- package/src/entity/flags.ts +65 -0
- package/src/entity/index.ts +14 -0
- package/src/entity/key.ts +68 -0
- package/src/entity/operations.ts +134 -0
- package/src/entity/params.ts +15 -0
- package/src/errors.ts +33 -28
- package/src/index.ts +6 -0
- package/src/query/engine.ts +59 -134
- package/src/query/errors.ts +151 -0
- package/src/query/expression.test.ts +286 -0
- package/src/query/expression.ts +493 -0
- package/src/query/index.ts +40 -4
- package/src/query/injection.test.ts +151 -0
- package/src/query/queryBuilder.test.ts +322 -637
- package/src/query/queryBuilder.ts +126 -359
- package/src/query/queryResult.ts +54 -51
- package/src/query/selection.test.ts +48 -98
- package/src/query/selection.ts +77 -65
- package/src/types/entity.test.ts +44 -155
- package/src/types/entity.ts +62 -44
- package/src/types/events.ts +64 -23
- package/src/types/index.ts +20 -16
- package/src/types/rpcSchema.ts +70 -33
- package/src/utils/arkivTransactions.test.ts +681 -38
- package/src/utils/arkivTransactions.ts +249 -104
- package/src/utils/chains.test.ts +21 -24
- package/src/utils/compression.ts +1 -1
- package/src/utils/entities.test.ts +62 -0
- package/src/utils/entities.ts +111 -29
- package/src/utils/expirationTime.ts +114 -10
- package/src/utils/logger.ts +2 -2
- package/src/utils/payload.ts +4 -2
- package/src/utils/revert.test.ts +99 -0
- package/src/utils/revert.ts +145 -0
- package/dist/chains-BifaY04V.cjs +0 -108
- package/dist/chains-CrtkVbBh.js +0 -86
- package/dist/chains-CrtkVbBh.js.map +0 -1
- package/dist/consts-AUoL-v42.cjs +0 -18
- package/dist/consts-BV4PR-de.js +0 -7
- package/dist/consts-BV4PR-de.js.map +0 -1
- package/dist/entity-B6g_EPm1.d.cts +0 -382
- package/dist/entity-B6g_EPm1.d.cts.map +0 -1
- package/dist/entity-qoLMAuuk.d.ts +0 -382
- package/dist/entity-qoLMAuuk.d.ts.map +0 -1
- package/dist/index-DdspxNAA.d.ts +0 -481
- package/dist/index-DdspxNAA.d.ts.map +0 -1
- package/dist/index-Ds9dwqb5.d.cts +0 -481
- package/dist/index-Ds9dwqb5.d.cts.map +0 -1
- package/dist/payload-B2kFJyah.d.ts +0 -6
- package/dist/payload-B2kFJyah.d.ts.map +0 -1
- package/dist/payload-CJN8fTvX.d.cts +0 -6
- package/dist/payload-CJN8fTvX.d.cts.map +0 -1
- package/dist/payload-Claf2jUO.js +0 -13
- package/dist/payload-Claf2jUO.js.map +0 -1
- package/dist/payload-DpA5PA-w.cjs +0 -23
- package/dist/query/index.js.map +0 -1
- package/dist/queryBuilder-D2uwP9jI.js +0 -704
- package/dist/queryBuilder-D2uwP9jI.js.map +0 -1
- package/dist/queryBuilder-UW2MCaX4.cjs +0 -792
- package/dist/utils/index.d.cts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/src/actions/public/buildQuery.ts +0 -10
- package/src/actions/public/subscribeEntityEvents.ts +0 -119
- package/src/actions/wallet/updateEntity.ts +0 -60
- package/src/chains/braga.ts +0 -26
- package/src/chains/kaolin.ts +0 -29
- package/src/query/engine.test.ts +0 -526
- package/src/query/predicate.test.ts +0 -461
- package/src/query/predicate.ts +0 -144
- package/src/query/queryResult.test.ts +0 -270
- package/src/query/selectQueryBuilder.test.ts +0 -210
- package/src/types/attributes.ts +0 -16
- package/src/utils/validation.test.ts +0 -60
- package/src/utils/validation.ts +0 -32
- /package/dist/{types-H-TSmRpm.js → types-Bx1EKe0v.js} +0 -0
- /package/dist/{types-o3zWarRp.cjs → types-CKPddlfS.cjs} +0 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test"
|
|
2
|
+
import { type Abi, encodeAbiParameters, encodeEventTopics, getAbiItem, type Hex } from "viem"
|
|
3
|
+
import type { ArkivClient } from "../../clients/baseClient"
|
|
4
|
+
import { ARKIV_ADDRESS } from "../../consts"
|
|
5
|
+
import { ENTITY_EVENTS_ABI, type EntityEventName } from "../../entity/events"
|
|
6
|
+
import type { EntityEvent } from "../../types/events"
|
|
7
|
+
import { watchEntityEvents } from "./watchEntityEvents"
|
|
8
|
+
|
|
9
|
+
const KEY_A = `0x${"aa".repeat(32)}` as Hex
|
|
10
|
+
const KEY_B = `0x${"bb".repeat(32)}` as Hex
|
|
11
|
+
const ALICE = "0x1111111111111111111111111111111111111111" as const
|
|
12
|
+
const BOB = "0x2222222222222222222222222222222222222222" as const
|
|
13
|
+
|
|
14
|
+
type Log = {
|
|
15
|
+
topics: Hex[]
|
|
16
|
+
data: Hex
|
|
17
|
+
blockNumber: bigint | null
|
|
18
|
+
transactionHash: Hex | null
|
|
19
|
+
logIndex: number | null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Builds the log the engine would emit for an event, from the same ABI the watcher decodes with.
|
|
24
|
+
*
|
|
25
|
+
* Encoding rather than hand-writing topics is what keeps the test honest about the indexed/
|
|
26
|
+
* non-indexed split: change the ABI and the log changes with it.
|
|
27
|
+
*/
|
|
28
|
+
function logFor(
|
|
29
|
+
eventName: EntityEventName,
|
|
30
|
+
args: Record<string, unknown>,
|
|
31
|
+
position: { blockNumber?: bigint; transactionHash?: Hex; logIndex?: number } = {},
|
|
32
|
+
): Log {
|
|
33
|
+
const item = getAbiItem({ abi: ENTITY_EVENTS_ABI as Abi, name: eventName })
|
|
34
|
+
if (item === undefined || item.type !== "event") throw new Error(`no event ${eventName}`)
|
|
35
|
+
|
|
36
|
+
const unindexed = item.inputs.filter((input) => !("indexed" in input && input.indexed))
|
|
37
|
+
return {
|
|
38
|
+
topics: encodeEventTopics({
|
|
39
|
+
abi: ENTITY_EVENTS_ABI,
|
|
40
|
+
eventName,
|
|
41
|
+
// biome-ignore lint/suspicious/noExplicitAny: the arg shape varies per event by design.
|
|
42
|
+
args: args as any,
|
|
43
|
+
}) as Hex[],
|
|
44
|
+
data:
|
|
45
|
+
unindexed.length === 0
|
|
46
|
+
? "0x"
|
|
47
|
+
: encodeAbiParameters(
|
|
48
|
+
unindexed,
|
|
49
|
+
unindexed.map((input) => args[input.name ?? ""]),
|
|
50
|
+
),
|
|
51
|
+
blockNumber: position.blockNumber ?? 100n,
|
|
52
|
+
transactionHash: position.transactionHash ?? (`0x${"cc".repeat(32)}` as Hex),
|
|
53
|
+
logIndex: position.logIndex ?? 0,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type Harness = {
|
|
58
|
+
client: ArkivClient
|
|
59
|
+
/** Feeds logs to the watcher, as the poller would. */
|
|
60
|
+
emitLogs: (logs: Log[]) => void
|
|
61
|
+
/** Raises a transport error on the log watcher. */
|
|
62
|
+
failLogs: (error: Error) => void
|
|
63
|
+
watchEventCalls: Record<string, unknown>[]
|
|
64
|
+
stops: string[]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A client that hands back its watcher callbacks so a test can drive them directly. */
|
|
68
|
+
function harness(): Harness {
|
|
69
|
+
const state: Partial<{
|
|
70
|
+
onLogs: (logs: Log[]) => void
|
|
71
|
+
onLogsError: (error: Error) => void
|
|
72
|
+
}> = {}
|
|
73
|
+
const watchEventCalls: Record<string, unknown>[] = []
|
|
74
|
+
const stops: string[] = []
|
|
75
|
+
|
|
76
|
+
const client = {
|
|
77
|
+
// biome-ignore lint/suspicious/noExplicitAny: a stand-in for viem's generic watcher.
|
|
78
|
+
watchEvent(parameters: any) {
|
|
79
|
+
watchEventCalls.push(parameters)
|
|
80
|
+
state.onLogs = parameters.onLogs
|
|
81
|
+
state.onLogsError = parameters.onError
|
|
82
|
+
return () => stops.push("logs")
|
|
83
|
+
},
|
|
84
|
+
} as unknown as ArkivClient
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
client,
|
|
88
|
+
emitLogs: (logs) => state.onLogs?.(logs),
|
|
89
|
+
failLogs: (error) => state.onLogsError?.(error),
|
|
90
|
+
watchEventCalls,
|
|
91
|
+
stops,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Runs `body` with `console.error` captured, so a test can assert on the default error path. */
|
|
96
|
+
function captureConsoleError(body: () => void): unknown[][] {
|
|
97
|
+
const calls: unknown[][] = []
|
|
98
|
+
const original = console.error
|
|
99
|
+
console.error = (...args: unknown[]) => {
|
|
100
|
+
calls.push(args)
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
body()
|
|
104
|
+
} finally {
|
|
105
|
+
console.error = original
|
|
106
|
+
}
|
|
107
|
+
return calls
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
describe("decoding the five on-chain events", () => {
|
|
111
|
+
it("decodes a create, with the position it was applied at", () => {
|
|
112
|
+
const h = harness()
|
|
113
|
+
const seen: EntityEvent[] = []
|
|
114
|
+
watchEntityEvents(h.client, { onEntityCreated: (event) => seen.push(event) })
|
|
115
|
+
|
|
116
|
+
h.emitLogs([
|
|
117
|
+
logFor(
|
|
118
|
+
"EntityCreated",
|
|
119
|
+
{ entityKey: KEY_A, owner: ALICE, expiresAt: 1_200_000n, creationFlags: 0b11 },
|
|
120
|
+
{ blockNumber: 42n, logIndex: 3 },
|
|
121
|
+
),
|
|
122
|
+
])
|
|
123
|
+
|
|
124
|
+
expect(seen).toEqual([
|
|
125
|
+
{
|
|
126
|
+
type: "EntityCreated",
|
|
127
|
+
entityKey: KEY_A,
|
|
128
|
+
owner: ALICE,
|
|
129
|
+
expiresAt: 1_200_000n,
|
|
130
|
+
// Decoded, with the byte kept alongside so a flag this SDK has no name for stays visible.
|
|
131
|
+
creationFlags: { readonly: true, permissionlessExtension: true, raw: 0b11 },
|
|
132
|
+
blockNumber: 42n,
|
|
133
|
+
transactionHash: `0x${"cc".repeat(32)}`,
|
|
134
|
+
logIndex: 3,
|
|
135
|
+
},
|
|
136
|
+
])
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it("decodes a patch", () => {
|
|
140
|
+
const h = harness()
|
|
141
|
+
const seen: EntityEvent[] = []
|
|
142
|
+
watchEntityEvents(h.client, { onEntityPatched: (event) => seen.push(event) })
|
|
143
|
+
|
|
144
|
+
h.emitLogs([logFor("EntityPatched", { entityKey: KEY_A, owner: ALICE })])
|
|
145
|
+
|
|
146
|
+
expect(seen).toMatchObject([{ type: "EntityPatched", entityKey: KEY_A, owner: ALICE }])
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it("decodes an extension", () => {
|
|
150
|
+
const h = harness()
|
|
151
|
+
const seen: EntityEvent[] = []
|
|
152
|
+
watchEntityEvents(h.client, { onExpiryExtended: (event) => seen.push(event) })
|
|
153
|
+
|
|
154
|
+
h.emitLogs([logFor("ExpiryExtended", { entityKey: KEY_A, owner: ALICE, expiresAt: 999n })])
|
|
155
|
+
|
|
156
|
+
// `owner` is the entity's owner, not the caller — a permissionless extension is by a non-owner.
|
|
157
|
+
expect(seen).toMatchObject([
|
|
158
|
+
{ type: "ExpiryExtended", entityKey: KEY_A, owner: ALICE, expiresAt: 999n },
|
|
159
|
+
])
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("decodes a transfer, naming both sides", () => {
|
|
163
|
+
const h = harness()
|
|
164
|
+
const seen: EntityEvent[] = []
|
|
165
|
+
watchEntityEvents(h.client, { onOwnershipTransferred: (event) => seen.push(event) })
|
|
166
|
+
|
|
167
|
+
h.emitLogs([
|
|
168
|
+
logFor("OwnershipTransferred", { entityKey: KEY_A, previousOwner: ALICE, newOwner: BOB }),
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
expect(seen).toMatchObject([
|
|
172
|
+
{ type: "OwnershipTransferred", entityKey: KEY_A, previousOwner: ALICE, newOwner: BOB },
|
|
173
|
+
])
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it("decodes a delete", () => {
|
|
177
|
+
const h = harness()
|
|
178
|
+
const seen: EntityEvent[] = []
|
|
179
|
+
watchEntityEvents(h.client, { onEntityDeleted: (event) => seen.push(event) })
|
|
180
|
+
|
|
181
|
+
h.emitLogs([logFor("EntityDeleted", { entityKey: KEY_A, owner: ALICE })])
|
|
182
|
+
|
|
183
|
+
expect(seen).toMatchObject([{ type: "EntityDeleted", entityKey: KEY_A, owner: ALICE }])
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
describe("onEvent", () => {
|
|
188
|
+
it("receives every event in the order it was applied", () => {
|
|
189
|
+
const h = harness()
|
|
190
|
+
const seen: string[] = []
|
|
191
|
+
watchEntityEvents(h.client, { onEvent: (event) => seen.push(event.type) })
|
|
192
|
+
|
|
193
|
+
h.emitLogs([
|
|
194
|
+
logFor(
|
|
195
|
+
"EntityCreated",
|
|
196
|
+
{ entityKey: KEY_A, owner: ALICE, expiresAt: 500n, creationFlags: 0 },
|
|
197
|
+
{ logIndex: 0 },
|
|
198
|
+
),
|
|
199
|
+
logFor("EntityPatched", { entityKey: KEY_A, owner: ALICE }, { logIndex: 1 }),
|
|
200
|
+
logFor(
|
|
201
|
+
"OwnershipTransferred",
|
|
202
|
+
{ entityKey: KEY_A, previousOwner: ALICE, newOwner: BOB },
|
|
203
|
+
{ logIndex: 2 },
|
|
204
|
+
),
|
|
205
|
+
logFor("EntityDeleted", { entityKey: KEY_A, owner: BOB }, { logIndex: 3 }),
|
|
206
|
+
])
|
|
207
|
+
|
|
208
|
+
expect(seen).toEqual([
|
|
209
|
+
"EntityCreated",
|
|
210
|
+
"EntityPatched",
|
|
211
|
+
"OwnershipTransferred",
|
|
212
|
+
"EntityDeleted",
|
|
213
|
+
])
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it("runs before the per-event handler for the same event", () => {
|
|
217
|
+
const h = harness()
|
|
218
|
+
const order: string[] = []
|
|
219
|
+
watchEntityEvents(h.client, {
|
|
220
|
+
onEvent: () => order.push("onEvent"),
|
|
221
|
+
onEntityCreated: () => order.push("onEntityCreated"),
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
h.emitLogs([
|
|
225
|
+
logFor("EntityCreated", { entityKey: KEY_A, owner: ALICE, expiresAt: 1n, creationFlags: 0 }),
|
|
226
|
+
])
|
|
227
|
+
|
|
228
|
+
expect(order).toEqual(["onEvent", "onEntityCreated"])
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
describe("the watcher itself", () => {
|
|
233
|
+
it("only listens to the Arkiv operation address", () => {
|
|
234
|
+
const h = harness()
|
|
235
|
+
watchEntityEvents(h.client, { onEntityCreated: () => {} })
|
|
236
|
+
|
|
237
|
+
expect(h.watchEventCalls[0]).toMatchObject({ address: ARKIV_ADDRESS })
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it("polls twice a block by default", () => {
|
|
241
|
+
const h = harness()
|
|
242
|
+
watchEntityEvents(h.client, { onEntityCreated: () => {} })
|
|
243
|
+
|
|
244
|
+
// Blocks are 2s, so a default that leaves an event a whole block stale is too slow.
|
|
245
|
+
expect(h.watchEventCalls[0]).toMatchObject({ pollingInterval: 1000 })
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it("forwards fromBlock and pollingInterval, and omits fromBlock when unset", () => {
|
|
249
|
+
const withOptions = harness()
|
|
250
|
+
watchEntityEvents(withOptions.client, {
|
|
251
|
+
onEntityCreated: () => {},
|
|
252
|
+
fromBlock: 90n,
|
|
253
|
+
pollingInterval: 250,
|
|
254
|
+
})
|
|
255
|
+
expect(withOptions.watchEventCalls[0]).toMatchObject({ fromBlock: 90n, pollingInterval: 250 })
|
|
256
|
+
|
|
257
|
+
const bare = harness()
|
|
258
|
+
watchEntityEvents(bare.client, { onEntityCreated: () => {} })
|
|
259
|
+
expect(bare.watchEventCalls[0]).not.toHaveProperty("fromBlock")
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it("stops the watcher when unwatched", () => {
|
|
263
|
+
const h = harness()
|
|
264
|
+
const unwatch = watchEntityEvents(h.client, { onEntityCreated: () => {} })
|
|
265
|
+
|
|
266
|
+
expect(h.stops).toEqual([])
|
|
267
|
+
unwatch()
|
|
268
|
+
expect(h.stops).toEqual(["logs"])
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it("skips a log with no position rather than inventing one", () => {
|
|
272
|
+
const h = harness()
|
|
273
|
+
const seen: EntityEvent[] = []
|
|
274
|
+
watchEntityEvents(h.client, { onEvent: (event) => seen.push(event) })
|
|
275
|
+
|
|
276
|
+
const log = logFor("EntityCreated", {
|
|
277
|
+
entityKey: KEY_A,
|
|
278
|
+
owner: ALICE,
|
|
279
|
+
expiresAt: 1n,
|
|
280
|
+
creationFlags: 0,
|
|
281
|
+
})
|
|
282
|
+
h.emitLogs([{ ...log, blockNumber: null }])
|
|
283
|
+
h.emitLogs([{ ...log, transactionHash: null }])
|
|
284
|
+
h.emitLogs([{ ...log, logIndex: null }])
|
|
285
|
+
|
|
286
|
+
expect(seen).toEqual([])
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
it("reports a log it cannot decode instead of throwing out of the poller", () => {
|
|
290
|
+
const h = harness()
|
|
291
|
+
const errors: Error[] = []
|
|
292
|
+
const seen: EntityEvent[] = []
|
|
293
|
+
watchEntityEvents(h.client, {
|
|
294
|
+
onEvent: (event) => seen.push(event),
|
|
295
|
+
onError: (error) => errors.push(error),
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
const good = logFor("EntityDeleted", { entityKey: KEY_A, owner: ALICE })
|
|
299
|
+
// A create's topic with no data — the expiresAt word is missing.
|
|
300
|
+
const broken = {
|
|
301
|
+
...logFor("EntityCreated", {
|
|
302
|
+
entityKey: KEY_A,
|
|
303
|
+
owner: ALICE,
|
|
304
|
+
expiresAt: 1n,
|
|
305
|
+
creationFlags: 0,
|
|
306
|
+
}),
|
|
307
|
+
data: "0x" as Hex,
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
expect(() => h.emitLogs([broken, good])).not.toThrow()
|
|
311
|
+
expect(errors).toHaveLength(1)
|
|
312
|
+
// The rest of the batch still gets through.
|
|
313
|
+
expect(seen).toMatchObject([{ type: "EntityDeleted" }])
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it("forwards transport errors", () => {
|
|
317
|
+
const h = harness()
|
|
318
|
+
const errors: Error[] = []
|
|
319
|
+
watchEntityEvents(h.client, {
|
|
320
|
+
onEntityCreated: () => {},
|
|
321
|
+
onError: (error) => errors.push(error),
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
const boom = new Error("transport died")
|
|
325
|
+
h.failLogs(boom)
|
|
326
|
+
|
|
327
|
+
expect(errors).toEqual([boom])
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it("logs to the console when no onError was given, so a dead watcher is not silent", () => {
|
|
331
|
+
const h = harness()
|
|
332
|
+
watchEntityEvents(h.client, { onEntityCreated: () => {} })
|
|
333
|
+
|
|
334
|
+
const boom = new Error("filter not found")
|
|
335
|
+
const logged = captureConsoleError(() => h.failLogs(boom))
|
|
336
|
+
|
|
337
|
+
expect(logged).toEqual([["watchEntityEvents error", boom]])
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
describe("a handler that throws", () => {
|
|
342
|
+
it("is reported, not allowed to escape into the poller", () => {
|
|
343
|
+
const h = harness()
|
|
344
|
+
const errors: Error[] = []
|
|
345
|
+
watchEntityEvents(h.client, {
|
|
346
|
+
onEntityCreated: () => {
|
|
347
|
+
throw new Error("consumer blew up")
|
|
348
|
+
},
|
|
349
|
+
onError: (error) => errors.push(error),
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
expect(() =>
|
|
353
|
+
h.emitLogs([
|
|
354
|
+
logFor("EntityCreated", {
|
|
355
|
+
entityKey: KEY_A,
|
|
356
|
+
owner: ALICE,
|
|
357
|
+
expiresAt: 200n,
|
|
358
|
+
creationFlags: 0,
|
|
359
|
+
}),
|
|
360
|
+
]),
|
|
361
|
+
).not.toThrow()
|
|
362
|
+
expect(errors).toMatchObject([{ message: "consumer blew up" }])
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
it("does not stop the other handlers for the same event", () => {
|
|
366
|
+
const h = harness()
|
|
367
|
+
const seen: string[] = []
|
|
368
|
+
watchEntityEvents(h.client, {
|
|
369
|
+
onEvent: () => {
|
|
370
|
+
throw new Error("onEvent blew up")
|
|
371
|
+
},
|
|
372
|
+
onEntityCreated: () => seen.push("onEntityCreated"),
|
|
373
|
+
onError: () => {},
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
h.emitLogs([
|
|
377
|
+
logFor("EntityCreated", {
|
|
378
|
+
entityKey: KEY_A,
|
|
379
|
+
owner: ALICE,
|
|
380
|
+
expiresAt: 200n,
|
|
381
|
+
creationFlags: 0,
|
|
382
|
+
}),
|
|
383
|
+
])
|
|
384
|
+
|
|
385
|
+
expect(seen).toEqual(["onEntityCreated"])
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
it("does not stop the rest of the batch", () => {
|
|
389
|
+
const h = harness()
|
|
390
|
+
const seen: Hex[] = []
|
|
391
|
+
watchEntityEvents(h.client, {
|
|
392
|
+
onEntityCreated: ({ entityKey }) => {
|
|
393
|
+
seen.push(entityKey)
|
|
394
|
+
if (entityKey === KEY_A) throw new Error("first one blew up")
|
|
395
|
+
},
|
|
396
|
+
onError: () => {},
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
h.emitLogs([
|
|
400
|
+
logFor(
|
|
401
|
+
"EntityCreated",
|
|
402
|
+
{ entityKey: KEY_A, owner: ALICE, expiresAt: 200n, creationFlags: 0 },
|
|
403
|
+
{ logIndex: 0 },
|
|
404
|
+
),
|
|
405
|
+
logFor(
|
|
406
|
+
"EntityCreated",
|
|
407
|
+
{ entityKey: KEY_B, owner: BOB, expiresAt: 200n, creationFlags: 0 },
|
|
408
|
+
{ logIndex: 1 },
|
|
409
|
+
),
|
|
410
|
+
])
|
|
411
|
+
|
|
412
|
+
expect(seen).toEqual([KEY_A, KEY_B])
|
|
413
|
+
})
|
|
414
|
+
})
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { decodeEventLog, type Hex } from "viem"
|
|
2
|
+
import type { ArkivClient } from "../../clients/baseClient"
|
|
3
|
+
import type { PublicArkivClient } from "../../clients/createPublicClient"
|
|
4
|
+
import { ARKIV_ADDRESS, BLOCK_TIME } from "../../consts"
|
|
5
|
+
import { ENTITY_EVENTS_ABI } from "../../entity/events"
|
|
6
|
+
import { decodeCreationFlags } from "../../entity/flags"
|
|
7
|
+
import type {
|
|
8
|
+
EntityCreatedEvent,
|
|
9
|
+
EntityDeletedEvent,
|
|
10
|
+
EntityEvent,
|
|
11
|
+
EntityEventContext,
|
|
12
|
+
EntityPatchedEvent,
|
|
13
|
+
ExpiryExtendedEvent,
|
|
14
|
+
OwnershipTransferredEvent,
|
|
15
|
+
} from "../../types/events"
|
|
16
|
+
import { getLogger } from "../../utils/logger"
|
|
17
|
+
|
|
18
|
+
const logger = getLogger("actions:public:watch-entity-events")
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* How often to poll, when the caller does not say.
|
|
22
|
+
*
|
|
23
|
+
* Half a block: fast enough that an event is never a whole block stale, slow enough not to hammer
|
|
24
|
+
* the node. viem's own default is derived from a 12-second Ethereum block and is far too slow here.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_POLLING_INTERVAL = (BLOCK_TIME * 1000) / 2
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Handlers and options for `client.watchEntityEvents`. Every handler is optional; the watcher does
|
|
30
|
+
* the work for the ones you pass.
|
|
31
|
+
*/
|
|
32
|
+
export type WatchEntityEventsParameters = {
|
|
33
|
+
onEntityCreated?: ((event: EntityCreatedEvent) => void) | undefined
|
|
34
|
+
onEntityPatched?: ((event: EntityPatchedEvent) => void) | undefined
|
|
35
|
+
onExpiryExtended?: ((event: ExpiryExtendedEvent) => void) | undefined
|
|
36
|
+
onOwnershipTransferred?: ((event: OwnershipTransferredEvent) => void) | undefined
|
|
37
|
+
onEntityDeleted?: ((event: EntityDeletedEvent) => void) | undefined
|
|
38
|
+
/**
|
|
39
|
+
* Every event, in application order, whatever its type. Runs *before* the per-event handler for
|
|
40
|
+
* the same event.
|
|
41
|
+
*
|
|
42
|
+
* This is the replay seam: operations apply in batch order and emit one event each, so an
|
|
43
|
+
* off-chain replica that consumes this in order sees exactly what the engine did.
|
|
44
|
+
*/
|
|
45
|
+
onEvent?: ((event: EntityEvent) => void) | undefined
|
|
46
|
+
/**
|
|
47
|
+
* Transport failures, logs that will not decode, and anything one of your own handlers throws.
|
|
48
|
+
*
|
|
49
|
+
* Defaults to `console.error`. A watcher whose filter the node dropped otherwise just goes quiet,
|
|
50
|
+
* and a quiet watcher is indistinguishable from a quiet chain.
|
|
51
|
+
*/
|
|
52
|
+
onError?: ((error: Error) => void) | undefined
|
|
53
|
+
/** Replay from this block before following the head. Defaults to the head. */
|
|
54
|
+
fromBlock?: bigint | undefined
|
|
55
|
+
/** How often to poll, in milliseconds. Defaults to half a block. */
|
|
56
|
+
pollingInterval?: number | undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Watches entity events, calling the handlers you pass as they arrive.
|
|
61
|
+
*
|
|
62
|
+
* Every event here is decoded from a log the Arkiv operation address emitted, and carries the
|
|
63
|
+
* block, transaction and log index it came from — so consuming them in order replays exactly what
|
|
64
|
+
* the engine did.
|
|
65
|
+
*
|
|
66
|
+
* @param parameters - Handlers and options. {@link WatchEntityEventsParameters}
|
|
67
|
+
* @returns A function that stops the watcher.
|
|
68
|
+
*
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* import { createPublicClient } from "@arkiv-network/sdk"
|
|
72
|
+
* import { cheesecake } from "@arkiv-network/sdk/chains"
|
|
73
|
+
* import { http } from "viem"
|
|
74
|
+
*
|
|
75
|
+
* const client = createPublicClient({ chain: cheesecake, transport: http() })
|
|
76
|
+
*
|
|
77
|
+
* const unwatch = client.watchEntityEvents({
|
|
78
|
+
* onEntityCreated: ({ entityKey, owner, expiresAt }) =>
|
|
79
|
+
* console.log("created", entityKey, "by", owner, "until block", expiresAt),
|
|
80
|
+
* onEntityDeleted: ({ entityKey }) => console.log("deleted", entityKey),
|
|
81
|
+
* onError: (error) => console.error(error),
|
|
82
|
+
* })
|
|
83
|
+
*
|
|
84
|
+
* unwatch()
|
|
85
|
+
*/
|
|
86
|
+
export function watchEntityEvents(
|
|
87
|
+
client: ArkivClient,
|
|
88
|
+
parameters: WatchEntityEventsParameters,
|
|
89
|
+
): () => void {
|
|
90
|
+
const {
|
|
91
|
+
onEntityCreated,
|
|
92
|
+
onEntityPatched,
|
|
93
|
+
onExpiryExtended,
|
|
94
|
+
onOwnershipTransferred,
|
|
95
|
+
onEntityDeleted,
|
|
96
|
+
onEvent,
|
|
97
|
+
onError,
|
|
98
|
+
fromBlock,
|
|
99
|
+
pollingInterval = DEFAULT_POLLING_INTERVAL,
|
|
100
|
+
} = parameters
|
|
101
|
+
|
|
102
|
+
const publicClient = client as PublicArkivClient
|
|
103
|
+
const reportError = onError ?? logToConsole
|
|
104
|
+
|
|
105
|
+
function emit(event: EntityEvent): void {
|
|
106
|
+
deliver(onEvent, event)
|
|
107
|
+
switch (event.type) {
|
|
108
|
+
case "EntityCreated":
|
|
109
|
+
deliver(onEntityCreated, event)
|
|
110
|
+
break
|
|
111
|
+
case "EntityPatched":
|
|
112
|
+
deliver(onEntityPatched, event)
|
|
113
|
+
break
|
|
114
|
+
case "ExpiryExtended":
|
|
115
|
+
deliver(onExpiryExtended, event)
|
|
116
|
+
break
|
|
117
|
+
case "OwnershipTransferred":
|
|
118
|
+
deliver(onOwnershipTransferred, event)
|
|
119
|
+
break
|
|
120
|
+
case "EntityDeleted":
|
|
121
|
+
deliver(onEntityDeleted, event)
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Runs one handler, keeping its failure to itself.
|
|
128
|
+
*
|
|
129
|
+
* A handler that throws is the consumer's problem, not the watcher's: letting it escape would
|
|
130
|
+
* reject the poll, drop the rest of the batch, and take down every other handler with it.
|
|
131
|
+
*/
|
|
132
|
+
function deliver<TEvent>(handler: ((event: TEvent) => void) | undefined, event: TEvent): void {
|
|
133
|
+
if (handler === undefined) return
|
|
134
|
+
try {
|
|
135
|
+
handler(event)
|
|
136
|
+
} catch (error) {
|
|
137
|
+
reportError(asError(error))
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const unwatchLogs = publicClient.watchEvent({
|
|
142
|
+
// Only the Arkiv operation address emits these. Without the filter any contract whose event
|
|
143
|
+
// happens to hash to the same topic lands in `onLogs`, and viem's observer key — which covers
|
|
144
|
+
// the address but not `events` — would make this subscription collide with any other
|
|
145
|
+
// unaddressed `watchEvent` on the same client.
|
|
146
|
+
address: ARKIV_ADDRESS,
|
|
147
|
+
events: ENTITY_EVENTS_ABI,
|
|
148
|
+
pollingInterval,
|
|
149
|
+
...(fromBlock !== undefined && { fromBlock }),
|
|
150
|
+
onLogs: (logs) => {
|
|
151
|
+
logger("logs %o", logs)
|
|
152
|
+
for (const log of logs) {
|
|
153
|
+
// A log with no position cannot be placed in application order, which is the one thing an
|
|
154
|
+
// event consumer needs from it. Dropping it beats inventing a position it never had.
|
|
155
|
+
if (log.blockNumber === null || log.transactionHash === null || log.logIndex === null) {
|
|
156
|
+
continue
|
|
157
|
+
}
|
|
158
|
+
const context: EntityEventContext = {
|
|
159
|
+
blockNumber: log.blockNumber,
|
|
160
|
+
transactionHash: log.transactionHash,
|
|
161
|
+
logIndex: log.logIndex,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Only the decode is guarded here. Dispatch guards each handler separately, so a consumer's
|
|
165
|
+
// exception is never reported as if the log were the thing that was malformed.
|
|
166
|
+
let event: EntityEvent
|
|
167
|
+
try {
|
|
168
|
+
event = toEntityEvent(log.topics, log.data, context)
|
|
169
|
+
} catch (error) {
|
|
170
|
+
reportError(asError(error))
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
emit(event)
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
onError: (error) => reportError(error),
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return unwatchLogs
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The fallback when no `onError` was given — see {@link WatchEntityEventsParameters.onError}. */
|
|
183
|
+
function logToConsole(error: Error): void {
|
|
184
|
+
console.error("watchEntityEvents error", error)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Handlers take an `Error`, and a `throw` can be anything. */
|
|
188
|
+
function asError(error: unknown): Error {
|
|
189
|
+
return error instanceof Error ? error : new Error(String(error))
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Decodes one log into the {@link EntityEvent} it carries.
|
|
194
|
+
*
|
|
195
|
+
* @throws If the log does not decode against {@link ENTITY_EVENTS_ABI}.
|
|
196
|
+
*/
|
|
197
|
+
function toEntityEvent(
|
|
198
|
+
topics: readonly Hex[],
|
|
199
|
+
data: Hex,
|
|
200
|
+
context: EntityEventContext,
|
|
201
|
+
): EntityEvent {
|
|
202
|
+
const decoded = decodeEventLog({
|
|
203
|
+
abi: ENTITY_EVENTS_ABI,
|
|
204
|
+
topics: topics as [Hex, ...Hex[]] | [],
|
|
205
|
+
data,
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
switch (decoded.eventName) {
|
|
209
|
+
case "EntityCreated":
|
|
210
|
+
return {
|
|
211
|
+
type: "EntityCreated",
|
|
212
|
+
...context,
|
|
213
|
+
...decoded.args,
|
|
214
|
+
creationFlags: decodeCreationFlags(decoded.args.creationFlags),
|
|
215
|
+
}
|
|
216
|
+
case "EntityPatched":
|
|
217
|
+
return { type: "EntityPatched", ...context, ...decoded.args }
|
|
218
|
+
case "ExpiryExtended":
|
|
219
|
+
return { type: "ExpiryExtended", ...context, ...decoded.args }
|
|
220
|
+
case "OwnershipTransferred":
|
|
221
|
+
return { type: "OwnershipTransferred", ...context, ...decoded.args }
|
|
222
|
+
case "EntityDeleted":
|
|
223
|
+
return { type: "EntityDeleted", ...context, ...decoded.args }
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import type { Hex } from "viem"
|
|
1
|
+
import type { Hash, Hex } from "viem"
|
|
2
2
|
import type { ArkivClient } from "../../clients/baseClient"
|
|
3
3
|
import type { TxParams } from "../../types"
|
|
4
|
-
import {
|
|
4
|
+
import { sendArkivTransaction } from "../../utils/arkivTransactions"
|
|
5
5
|
import { getLogger } from "../../utils/logger"
|
|
6
6
|
|
|
7
7
|
const logger = getLogger("actions:wallet:change-ownership")
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Parameters for the
|
|
11
|
-
* - entityKey: The key of the entity
|
|
10
|
+
* Parameters for the changeOwnership function.
|
|
11
|
+
* - entityKey: The key of the entity.
|
|
12
|
+
* - newOwner: The address of the new owner.
|
|
12
13
|
*/
|
|
13
14
|
export type ChangeOwnershipParameters = {
|
|
14
15
|
entityKey: Hex
|
|
@@ -16,13 +17,13 @@ export type ChangeOwnershipParameters = {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* Return type for the
|
|
20
|
+
* Return type for the changeOwnership function.
|
|
20
21
|
* - entityKey: The key of the entity.
|
|
21
22
|
* - txHash: The transaction hash.
|
|
22
23
|
*/
|
|
23
24
|
export type ChangeOwnershipReturnType = {
|
|
24
25
|
entityKey: Hex
|
|
25
|
-
txHash:
|
|
26
|
+
txHash: Hash
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export async function changeOwnership(
|
|
@@ -31,8 +32,7 @@ export async function changeOwnership(
|
|
|
31
32
|
txParams?: TxParams,
|
|
32
33
|
): Promise<ChangeOwnershipReturnType> {
|
|
33
34
|
logger("changeOwnership %o", data)
|
|
34
|
-
const
|
|
35
|
-
const receipt = await sendArkivTransaction(client, txData, txParams)
|
|
35
|
+
const { receipt } = await sendArkivTransaction(client, { ownershipChanges: [data] }, txParams)
|
|
36
36
|
|
|
37
37
|
logger("Receipt from changeOwnership %o", receipt)
|
|
38
38
|
|