@arkiv-network/sdk 0.7.0 → 0.8.0-advanced.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -16
- package/dist/actions/advanced/index.cjs +13 -0
- package/dist/actions/advanced/index.d.cts +8 -0
- package/dist/actions/advanced/index.d.ts +9 -0
- package/dist/actions/advanced/index.js +9 -0
- package/dist/advanced-8ZPTmmaN.js +598 -0
- package/dist/advanced-8ZPTmmaN.js.map +1 -0
- package/dist/advanced-fPImL6lW.cjs +656 -0
- package/dist/arkivTransactions-D74gERHG.d.cts +571 -0
- package/dist/arkivTransactions-D74gERHG.d.cts.map +1 -0
- package/dist/arkivTransactions-NtGD6uk5.d.ts +571 -0
- package/dist/arkivTransactions-NtGD6uk5.d.ts.map +1 -0
- package/dist/attr/index.cjs +35 -0
- package/dist/attr/index.d.cts +2 -0
- package/dist/attr/index.d.ts +3 -0
- package/dist/attr/index.js +4 -0
- package/dist/attributes-BhY5DwJp.js +809 -0
- package/dist/attributes-BhY5DwJp.js.map +1 -0
- package/dist/attributes-Df9IsCVs.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/codec-Car3KkFF.d.ts +102 -0
- package/dist/codec-Car3KkFF.d.ts.map +1 -0
- package/dist/entity/index.cjs +20 -0
- package/dist/entity/index.d.cts +4 -0
- package/dist/entity/index.d.ts +5 -0
- package/dist/entity/index.js +7 -0
- package/dist/entity-B00Li04j.cjs +71 -0
- package/dist/entity-B7G8q_Eo.cjs +0 -0
- package/dist/entity-BYQR1uoe.js +67 -0
- package/dist/entity-BYQR1uoe.js.map +1 -0
- package/dist/entity-CD_qFF2M.js +0 -0
- package/dist/errors-BVbjKVMN.cjs +56 -0
- package/dist/errors-LTe7bOYS.js +33 -0
- package/dist/errors-LTe7bOYS.js.map +1 -0
- package/dist/expiry-BELY2Kpw.cjs +135 -0
- package/dist/expiry-BjpxZZQU.js +95 -0
- package/dist/expiry-BjpxZZQU.js.map +1 -0
- package/dist/expiry-DRV29F1r.d.cts +79 -0
- package/dist/expiry-DRV29F1r.d.cts.map +1 -0
- package/dist/expiry-nDbCgZX4.d.ts +79 -0
- package/dist/expiry-nDbCgZX4.d.ts.map +1 -0
- package/dist/flags-5dTNRnow.d.ts +41 -0
- package/dist/flags-5dTNRnow.d.ts.map +1 -0
- package/dist/flags-CRjPpoh3.d.cts +41 -0
- package/dist/flags-CRjPpoh3.d.cts.map +1 -0
- package/dist/flags-DFBs2J94.js +33 -0
- package/dist/flags-DFBs2J94.js.map +1 -0
- package/dist/flags-kr276q6i.cjs +43 -0
- package/dist/index-B5j76711.d.ts +184 -0
- package/dist/index-B5j76711.d.ts.map +1 -0
- package/dist/index-BF7Mq9Is.d.cts +280 -0
- package/dist/index-BF7Mq9Is.d.cts.map +1 -0
- package/dist/index-CA4V7Pdh.d.cts +167 -0
- package/dist/index-CA4V7Pdh.d.cts.map +1 -0
- package/dist/index-CV5zCdpa.d.cts +417 -0
- package/dist/index-CV5zCdpa.d.cts.map +1 -0
- package/dist/index-DHDud2hC.d.ts +280 -0
- package/dist/index-DHDud2hC.d.ts.map +1 -0
- package/dist/index-DvYvrkdk.d.ts +320 -0
- package/dist/index-DvYvrkdk.d.ts.map +1 -0
- package/dist/index-EeUO0nnJ.d.cts +184 -0
- package/dist/index-EeUO0nnJ.d.cts.map +1 -0
- package/dist/index-fX_XyqLp.d.ts +167 -0
- package/dist/index-fX_XyqLp.d.ts.map +1 -0
- package/dist/index.cjs +422 -399
- package/dist/index.d.cts +364 -204
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +365 -204
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +357 -389
- package/dist/index.js.map +1 -1
- package/dist/key-D1Bh7MF0.js +70 -0
- package/dist/key-D1Bh7MF0.js.map +1 -0
- package/dist/key-WKhXnXuU.cjs +98 -0
- package/dist/logger-CVpvU8ik.js +153 -0
- package/dist/logger-CVpvU8ik.js.map +1 -0
- package/dist/logger-Cr15lPAQ.cjs +188 -0
- package/dist/payload-BNoud2KZ.d.cts +94 -0
- package/dist/payload-BNoud2KZ.d.cts.map +1 -0
- package/dist/payload-Bz6651ax.js +67 -0
- package/dist/payload-Bz6651ax.js.map +1 -0
- package/dist/payload-CcUj85ys.d.ts +94 -0
- package/dist/payload-CcUj85ys.d.ts.map +1 -0
- package/dist/payload-KC14SDlN.cjs +83 -0
- package/dist/query/index.cjs +26 -149
- package/dist/query/index.d.cts +5 -3
- package/dist/query/index.d.ts +6 -3
- package/dist/query/index.js +8 -137
- package/dist/queryBuilder-DM6AWCBa.js +820 -0
- package/dist/queryBuilder-DM6AWCBa.js.map +1 -0
- package/dist/queryBuilder-DbUMEZln.cjs +944 -0
- package/dist/selection-C4UhsMOO.d.ts +434 -0
- package/dist/selection-C4UhsMOO.d.ts.map +1 -0
- package/dist/selection-DPg0uyJf.d.cts +433 -0
- package/dist/selection-DPg0uyJf.d.cts.map +1 -0
- package/dist/types/index.cjs +4 -1
- package/dist/types/index.d.cts +7 -2
- package/dist/types/index.d.ts +8 -2
- package/dist/types/index.js +4 -1
- package/dist/types-Bu_ZwMbZ.js +0 -0
- package/dist/types-NRqymi6S.cjs +0 -0
- 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/advanced/advanced.test.ts +258 -0
- package/src/actions/advanced/buildMutation.ts +104 -0
- package/src/actions/advanced/getMutationResult.ts +112 -0
- package/src/actions/advanced/index.ts +21 -0
- package/src/actions/advanced/pingTransaction.ts +43 -0
- package/src/actions/advanced/sendMutation.ts +59 -0
- 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 +35 -67
- 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 +23 -83
- 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/arkivAdvanced.ts +144 -0
- package/src/clients/decorators/arkivPublic.ts +172 -139
- package/src/clients/decorators/arkivWallet.ts +106 -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 +15 -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 +19 -0
- package/src/query/engine.ts +68 -132
- 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 +342 -637
- package/src/query/queryBuilder.ts +133 -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 +400 -115
- 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 → attr-DlmErkvR.cjs} +0 -0
- /package/dist/{types-o3zWarRp.cjs → attr-GcQKatub.js} +0 -0
|
@@ -1,155 +1,440 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type Address,
|
|
3
|
+
ContractFunctionExecutionError,
|
|
4
|
+
ContractFunctionRevertedError,
|
|
5
|
+
decodeEventLog,
|
|
6
|
+
type Hash,
|
|
7
|
+
type Hex,
|
|
8
|
+
parseAbi,
|
|
9
|
+
TransactionExecutionError,
|
|
10
|
+
type TransactionReceipt,
|
|
11
|
+
} from "viem"
|
|
2
12
|
import type { ChangeOwnershipParameters } from "../actions/wallet/changeOwnership"
|
|
3
13
|
import type { CreateEntityParameters } from "../actions/wallet/createEntity"
|
|
4
14
|
import type { DeleteEntityParameters } from "../actions/wallet/deleteEntity"
|
|
5
15
|
import type { ExtendEntityParameters } from "../actions/wallet/extendEntity"
|
|
6
|
-
import type {
|
|
16
|
+
import type { PatchEntityParameters } from "../actions/wallet/patchEntity"
|
|
17
|
+
// Internal seam: the ABI encoders are not part of the package's public surface.
|
|
18
|
+
import { encodeAttributes, encodeMutations } from "../attr/attributes"
|
|
7
19
|
import type { ArkivClient } from "../clients/baseClient"
|
|
8
20
|
import type { WalletArkivClient } from "../clients/createWalletClient"
|
|
9
|
-
import { ARKIV_ADDRESS
|
|
10
|
-
import {
|
|
21
|
+
import { ARKIV_ADDRESS } from "../consts"
|
|
22
|
+
import { ENTITY_EVENTS_ABI } from "../entity/events"
|
|
23
|
+
import { type Expiry, type ExpiryContext, resolveExpiry } from "../entity/expiry"
|
|
24
|
+
import { encodeCreationFlags } from "../entity/flags"
|
|
25
|
+
import { randomSalt, validateSalt } from "../entity/key"
|
|
26
|
+
import {
|
|
27
|
+
createOperation,
|
|
28
|
+
deleteOperation,
|
|
29
|
+
EXECUTE_ABI as EXECUTE_FUNCTION_ABI,
|
|
30
|
+
extendExpiryOperation,
|
|
31
|
+
type Operation,
|
|
32
|
+
patchOperation,
|
|
33
|
+
transferOwnershipOperation,
|
|
34
|
+
} from "../entity/operations"
|
|
35
|
+
import { EmptyPatchError, EntityMutationError, InvalidContentTypeError } from "../errors"
|
|
11
36
|
import type { TxParams } from "../types"
|
|
12
|
-
|
|
37
|
+
|
|
13
38
|
import { getLogger } from "./logger"
|
|
14
|
-
import {
|
|
39
|
+
import { describeEntityRevert } from "./revert"
|
|
15
40
|
|
|
16
41
|
const logger = getLogger("utils:arkiv-transactions")
|
|
17
42
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}: {
|
|
25
|
-
creates?: CreateEntityParameters[]
|
|
26
|
-
updates?: UpdateEntityParameters[]
|
|
27
|
-
deletes?: DeleteEntityParameters[]
|
|
28
|
-
extensions?: ExtendEntityParameters[]
|
|
29
|
-
ownershipChanges?: ChangeOwnershipParameters[]
|
|
30
|
-
}) {
|
|
31
|
-
function formatAttributes<T extends string | number | bigint | boolean>(attribute: {
|
|
32
|
-
key: string
|
|
33
|
-
value: T
|
|
34
|
-
}): [Hex, Hex] {
|
|
35
|
-
return [
|
|
36
|
-
toHex(attribute.key),
|
|
37
|
-
toHex(typeof attribute.value === "number" && attribute.value === 0 ? "" : attribute.value),
|
|
38
|
-
]
|
|
43
|
+
// RFC 2045 token chars, lowercase only. Rejects uppercase to prevent text/plain vs Text/Plain ambiguity.
|
|
44
|
+
const MIME_REGEX = /^[a-z][a-z0-9!#$&\-^_]*\/[a-z0-9][a-z0-9!#$&\-^_.+]*$/
|
|
45
|
+
|
|
46
|
+
function validateContentType(contentType: string): void {
|
|
47
|
+
if (!MIME_REGEX.test(contentType)) {
|
|
48
|
+
throw new InvalidContentTypeError(contentType)
|
|
39
49
|
}
|
|
50
|
+
}
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.filter((attribute) => typeof attribute.value === "number")
|
|
72
|
-
.map(formatAttributes),
|
|
73
|
-
]
|
|
74
|
-
}),
|
|
75
|
-
//deletes
|
|
76
|
-
(deletes ?? []).map((item) => item.entityKey),
|
|
77
|
-
//extends
|
|
78
|
-
(extensions ?? []).map((item) => {
|
|
79
|
-
validateExpiresIn(item.expiresIn)
|
|
80
|
-
return [item.entityKey, toHex(item.expiresIn / BLOCK_TIME)]
|
|
81
|
-
}),
|
|
82
|
-
//ownershipChanges TODO
|
|
83
|
-
(ownershipChanges ?? []).map((item) => [item.entityKey, item.newOwner]),
|
|
84
|
-
]
|
|
52
|
+
export const ENTITY_ERRORS_ABI = parseAbi([
|
|
53
|
+
// Batch and operation framing.
|
|
54
|
+
"error EmptyBatch()",
|
|
55
|
+
"error InvalidOpType(uint8 operation)",
|
|
56
|
+
"error NonCanonicalOperationData(uint8 operation)",
|
|
57
|
+
// The attribute triple list, structural.
|
|
58
|
+
"error AttributesNotSorted()",
|
|
59
|
+
"error TooManyAttributes(uint256 count, uint256 maxCount)",
|
|
60
|
+
"error EmptyMutations(bytes32 entityKey)",
|
|
61
|
+
"error TombstoneInCreate(bytes32 name)",
|
|
62
|
+
"error TombstoneValueNotEmpty(bytes32 name)",
|
|
63
|
+
"error SystemAttributeNotWritable(bytes32 name)",
|
|
64
|
+
"error InvalidValueType(bytes32 name, uint8 typeId)",
|
|
65
|
+
// Attribute-name validation. `Ident32` is a bytes32 on the wire.
|
|
66
|
+
"error Ident32Empty()",
|
|
67
|
+
"error Ident32InvalidByte(uint256 position, bytes1 value)",
|
|
68
|
+
// Entity state and authorisation.
|
|
69
|
+
"error EntityNotFound(bytes32 entityKey)",
|
|
70
|
+
"error EntityExpired(bytes32 entityKey, uint64 expiresAt)",
|
|
71
|
+
"error NotOwner(bytes32 entityKey, address caller, address owner)",
|
|
72
|
+
"error ReadOnlyEntity(bytes32 entityKey)",
|
|
73
|
+
"error ReservedCreationFlags(uint8 creationFlags)",
|
|
74
|
+
// Expiry. The engine bounds an expiry only by the width of the field and by liveness — there is
|
|
75
|
+
// no MAX_EXPIRES_AT or MAX_LIFETIME, so there is no error for exceeding one.
|
|
76
|
+
"error ExpiryDeadOnArrival(uint64 target, uint64 currentBlock)",
|
|
77
|
+
"error ExpiryNotExtended(bytes32 entityKey, uint64 newExpiresAt, uint64 currentExpiresAt)",
|
|
78
|
+
// Ownership transfer.
|
|
79
|
+
"error TransferToZeroAddress(bytes32 entityKey)",
|
|
80
|
+
"error TransferToSelf(bytes32 entityKey)",
|
|
81
|
+
])
|
|
85
82
|
|
|
86
|
-
|
|
83
|
+
const EXECUTE_ABI = [...EXECUTE_FUNCTION_ABI, ...ENTITY_ERRORS_ABI]
|
|
87
84
|
|
|
88
|
-
|
|
85
|
+
export type SendArkivTransactionResult = {
|
|
86
|
+
receipt: TransactionReceipt
|
|
87
|
+
/** The keys the engine minted for the entities this transaction created, in batch order. */
|
|
88
|
+
createdEntityKeys: Hex[]
|
|
89
|
+
/** The block each created entity expires at, as the engine recorded it, in the same order. */
|
|
90
|
+
createdExpiries: bigint[]
|
|
91
|
+
/** The block each extended entity now expires at, as the engine recorded it, in batch order. */
|
|
92
|
+
extendedExpiries: bigint[]
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
export async function sendArkivTransaction(
|
|
95
|
+
export async function sendArkivTransaction(
|
|
96
|
+
client: ArkivClient,
|
|
97
|
+
ops: EntityMutationOps,
|
|
98
|
+
txParams?: TxParams,
|
|
99
|
+
): Promise<SendArkivTransactionResult> {
|
|
92
100
|
if (!client.account) throw new Error("Account required")
|
|
101
|
+
if (!client.chain) throw new Error("Chain required")
|
|
93
102
|
const walletClient = client as WalletArkivClient
|
|
94
103
|
|
|
95
|
-
|
|
96
|
-
account: client.account,
|
|
97
|
-
chain: client.chain,
|
|
98
|
-
to: ARKIV_ADDRESS,
|
|
99
|
-
value: 0n,
|
|
100
|
-
data,
|
|
101
|
-
...txParams,
|
|
102
|
-
})
|
|
104
|
+
const { creates, extensions } = ops
|
|
103
105
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
})
|
|
106
|
+
// The block height, to resolve any relative expiry — which both creates and extensions carry.
|
|
107
|
+
// Skipped entirely for a batch with neither. Deliberately fetched even when every expiry is
|
|
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.
|
|
111
|
+
const currentBlock =
|
|
112
|
+
creates?.length || extensions?.length ? await walletClient.getBlockNumber() : 0n
|
|
113
|
+
|
|
114
|
+
const operations = buildEntityOperations(ops, { currentBlock })
|
|
114
115
|
|
|
116
|
+
logger("Sending execute with %d operations %o", operations.length, operations)
|
|
117
|
+
|
|
118
|
+
const txHash = await submitMutation(client, operations, txParams)
|
|
119
|
+
|
|
120
|
+
try {
|
|
115
121
|
const receipt = await walletClient.waitForTransactionReceipt({ hash: txHash })
|
|
116
122
|
logger("Tx receipt %o", receipt)
|
|
123
|
+
|
|
117
124
|
if (receipt.status === "reverted") {
|
|
118
125
|
try {
|
|
119
|
-
|
|
126
|
+
await walletClient.simulateContract({
|
|
127
|
+
address: ARKIV_ADDRESS,
|
|
128
|
+
abi: EXECUTE_ABI,
|
|
129
|
+
functionName: "execute",
|
|
130
|
+
args: [operations],
|
|
120
131
|
account: client.account,
|
|
121
|
-
|
|
122
|
-
value: 0n,
|
|
123
|
-
data: toHex(compressed),
|
|
124
|
-
...txParams,
|
|
132
|
+
chain: client.chain,
|
|
125
133
|
})
|
|
126
|
-
throw new EntityMutationError(
|
|
127
|
-
`Transaction ${receipt.transactionHash} reverted. Please make sure the data for entities doesn't contain invalid characters or invalid data types.
|
|
128
|
-
Reason: ${callResult.data ? hexToString(callResult.data) : "No reason provided by backend."}`,
|
|
129
|
-
)
|
|
130
134
|
} catch (err) {
|
|
131
|
-
const error = err as { cause?: { details?: string } }
|
|
132
|
-
const reason =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
const error = err as { shortMessage?: string; cause?: { details?: string } }
|
|
136
|
+
const reason =
|
|
137
|
+
describeEntityRevert(err) ??
|
|
138
|
+
error.shortMessage ??
|
|
139
|
+
error.cause?.details ??
|
|
140
|
+
"No reason provided by backend."
|
|
137
141
|
throw new EntityMutationError(
|
|
138
142
|
`Transaction ${receipt.transactionHash} reverted. Reason: ${reason}`,
|
|
139
143
|
)
|
|
140
144
|
}
|
|
145
|
+
throw new EntityMutationError(
|
|
146
|
+
`Transaction ${receipt.transactionHash} reverted. No reason provided by backend.`,
|
|
147
|
+
)
|
|
141
148
|
}
|
|
142
149
|
|
|
143
|
-
return
|
|
150
|
+
return {
|
|
151
|
+
receipt,
|
|
152
|
+
...readAppliedOperations(receipt, {
|
|
153
|
+
creates: creates?.length ?? 0,
|
|
154
|
+
extensions: extensions?.length ?? 0,
|
|
155
|
+
}),
|
|
156
|
+
}
|
|
144
157
|
} catch (error) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
158
|
+
throw asEntityMutationError(error)
|
|
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
|
+
}
|
|
174
|
+
|
|
175
|
+
let message = "Transaction failed"
|
|
176
|
+
if (error instanceof TransactionExecutionError) {
|
|
177
|
+
message += `: ${error.details}`
|
|
178
|
+
} else if (error instanceof ContractFunctionExecutionError) {
|
|
179
|
+
logger("Contract function execution error data:", error.shortMessage)
|
|
180
|
+
if (error.cause instanceof ContractFunctionRevertedError) {
|
|
181
|
+
message += `: ${error.cause.message}`
|
|
182
|
+
} else {
|
|
183
|
+
message += ": Execution error without revert data"
|
|
184
|
+
}
|
|
185
|
+
} else if (error instanceof Error) {
|
|
186
|
+
message += `: ${error.message}`
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return new EntityMutationError(message, { cause: error })
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The keys and expiries the engine recorded, read back from the events the batch emitted.
|
|
194
|
+
*/
|
|
195
|
+
function readAppliedOperations(
|
|
196
|
+
receipt: TransactionReceipt,
|
|
197
|
+
expected: { creates: number; extensions: number },
|
|
198
|
+
): Omit<SendArkivTransactionResult, "receipt"> {
|
|
199
|
+
const events = collectMutationEvents(receipt.logs)
|
|
200
|
+
|
|
201
|
+
const createdEntityKeys = events.created.map((e) => e.entityKey)
|
|
202
|
+
const createdExpiries = events.created.map((e) => e.expiresAt)
|
|
203
|
+
const extendedExpiries = events.extended.map((e) => e.expiresAt)
|
|
204
|
+
|
|
205
|
+
// Operations apply in batch order and emit one event each, and creates lead the batch — so these
|
|
206
|
+
// arrive already aligned with the parameters they came from. A count that disagrees means that
|
|
207
|
+
// assumption broke, and a misaligned array would hand back a key belonging to another entity.
|
|
208
|
+
assertCount(receipt, "create", createdEntityKeys.length, expected.creates)
|
|
209
|
+
assertCount(receipt, "extension", extendedExpiries.length, expected.extensions)
|
|
210
|
+
|
|
211
|
+
return { createdEntityKeys, createdExpiries, extendedExpiries }
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** One log as an entity event, or `undefined` for anything this SDK has no name for. */
|
|
215
|
+
function decodeEntityLog(log: TransactionReceipt["logs"][number]) {
|
|
216
|
+
try {
|
|
217
|
+
return decodeEventLog({ abi: ENTITY_EVENTS_ABI, topics: log.topics, data: log.data })
|
|
218
|
+
} catch {
|
|
219
|
+
// An event a newer engine emits and this ABI does not describe. Not this function's business.
|
|
220
|
+
return undefined
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function assertCount(receipt: TransactionReceipt, what: string, got: number, want: number): void {
|
|
225
|
+
if (got === want) return
|
|
226
|
+
// Deliberately explicit that the write succeeded: this throws *after* a successful transaction,
|
|
227
|
+
// and a caller who reads it as a failure and retries would create the entities a second time.
|
|
228
|
+
throw new EntityMutationError(
|
|
229
|
+
`Transaction ${receipt.transactionHash} succeeded — do not retry it — but emitted ${got} ` +
|
|
230
|
+
`${what} event(s) for ${want} ${what} operation(s). The batch was applied; the SDK cannot ` +
|
|
231
|
+
`say which entity each operation produced, so read them back to find them.`,
|
|
232
|
+
)
|
|
233
|
+
}
|
|
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)
|
|
150
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
|
|
151
359
|
|
|
152
|
-
|
|
153
|
-
|
|
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)
|
|
154
372
|
}
|
|
155
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
|
+
}
|
package/src/utils/chains.test.ts
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test"
|
|
2
|
-
import {
|
|
2
|
+
import { cheesecake, localhost } from "../chains"
|
|
3
3
|
import { chainFromName } from "./chains"
|
|
4
4
|
|
|
5
5
|
describe("chainFromName", () => {
|
|
6
6
|
describe("valid chain names", () => {
|
|
7
|
-
test("returns
|
|
8
|
-
const chain = chainFromName("
|
|
9
|
-
expect(chain).toBe(
|
|
10
|
-
expect(chain.id).toBe(
|
|
11
|
-
expect(chain.name).toBe("
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
test("returns kaolin chain for 'kaolin'", () => {
|
|
15
|
-
const chain = chainFromName("kaolin")
|
|
16
|
-
expect(chain).toBe(kaolin)
|
|
17
|
-
expect(chain.id).toBe(60138453025)
|
|
18
|
-
expect(chain.name).toBe("Kaolin")
|
|
7
|
+
test("returns cheesecake chain for 'cheesecake'", () => {
|
|
8
|
+
const chain = chainFromName("cheesecake")
|
|
9
|
+
expect(chain).toBe(cheesecake)
|
|
10
|
+
expect(chain.id).toBe(7733102)
|
|
11
|
+
expect(chain.name).toBe("Cheesecake")
|
|
19
12
|
})
|
|
20
13
|
|
|
21
14
|
test("returns localhost chain for 'localhost'", () => {
|
|
@@ -28,17 +21,14 @@ describe("chainFromName", () => {
|
|
|
28
21
|
|
|
29
22
|
describe("case insensitivity", () => {
|
|
30
23
|
test("handles uppercase chain names", () => {
|
|
31
|
-
expect(chainFromName("
|
|
32
|
-
expect(chainFromName("KAOLIN")).toBe(kaolin)
|
|
24
|
+
expect(chainFromName("CHEESECAKE")).toBe(cheesecake)
|
|
33
25
|
expect(chainFromName("LOCALHOST")).toBe(localhost)
|
|
34
26
|
})
|
|
35
27
|
|
|
36
28
|
test("handles mixed case chain names", () => {
|
|
37
|
-
expect(chainFromName("
|
|
38
|
-
expect(chainFromName("Kaolin")).toBe(kaolin)
|
|
29
|
+
expect(chainFromName("Cheesecake")).toBe(cheesecake)
|
|
39
30
|
expect(chainFromName("Localhost")).toBe(localhost)
|
|
40
|
-
expect(chainFromName("
|
|
41
|
-
expect(chainFromName("kAoLiN")).toBe(kaolin)
|
|
31
|
+
expect(chainFromName("cHeEsEcAkE")).toBe(cheesecake)
|
|
42
32
|
})
|
|
43
33
|
})
|
|
44
34
|
|
|
@@ -52,22 +42,28 @@ describe("chainFromName", () => {
|
|
|
52
42
|
})
|
|
53
43
|
|
|
54
44
|
test("throws error for chain name with whitespace", () => {
|
|
55
|
-
expect(() => chainFromName("
|
|
56
|
-
expect(() => chainFromName("
|
|
45
|
+
expect(() => chainFromName("cheesecake ")).toThrow("Unknown chain: cheesecake ")
|
|
46
|
+
expect(() => chainFromName(" cheesecake")).toThrow("Unknown chain: cheesecake")
|
|
57
47
|
})
|
|
58
48
|
|
|
59
49
|
test("throws error for similar but incorrect chain names", () => {
|
|
60
|
-
expect(() => chainFromName("
|
|
50
|
+
expect(() => chainFromName("cheesecakee")).toThrow("Unknown chain: cheesecakee")
|
|
51
|
+
expect(() => chainFromName("cheesecak")).toThrow("Unknown chain: cheesecak")
|
|
61
52
|
expect(() => chainFromName("mendoza")).toThrow("Unknown chain: mendoza")
|
|
62
53
|
expect(() => chainFromName("marketplace")).toThrow("Unknown chain: marketplace")
|
|
63
54
|
expect(() => chainFromName("rosario")).toThrow("Unknown chain: rosario")
|
|
64
55
|
expect(() => chainFromName("local")).toThrow("Unknown chain: local")
|
|
65
56
|
})
|
|
57
|
+
|
|
58
|
+
test("throws error for the retired chains", () => {
|
|
59
|
+
expect(() => chainFromName("braga")).toThrow("Unknown chain: braga")
|
|
60
|
+
expect(() => chainFromName("kaolin")).toThrow("Unknown chain: kaolin")
|
|
61
|
+
})
|
|
66
62
|
})
|
|
67
63
|
|
|
68
64
|
describe("chain properties", () => {
|
|
69
65
|
test("returned chain has required viem Chain properties", () => {
|
|
70
|
-
const chain = chainFromName("
|
|
66
|
+
const chain = chainFromName("cheesecake")
|
|
71
67
|
|
|
72
68
|
expect(chain).toHaveProperty("id")
|
|
73
69
|
expect(chain).toHaveProperty("name")
|
|
@@ -79,12 +75,13 @@ describe("chainFromName", () => {
|
|
|
79
75
|
})
|
|
80
76
|
|
|
81
77
|
test("returned chain has rpcUrls configured", () => {
|
|
82
|
-
const chain = chainFromName("
|
|
78
|
+
const chain = chainFromName("cheesecake")
|
|
83
79
|
|
|
84
80
|
expect(chain.rpcUrls).toBeDefined()
|
|
85
81
|
expect(chain.rpcUrls.default).toBeDefined()
|
|
86
82
|
expect(chain.rpcUrls.default.http).toBeInstanceOf(Array)
|
|
87
83
|
expect(chain.rpcUrls.default.http.length).toBeGreaterThan(0)
|
|
84
|
+
expect(chain.rpcUrls.default.webSocket?.length).toBeGreaterThan(0)
|
|
88
85
|
})
|
|
89
86
|
})
|
|
90
87
|
})
|
package/src/utils/compression.ts
CHANGED