accounts 0.18.4 → 0.19.0
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/CHANGELOG.md +20 -0
- package/dist/core/AccessKey.d.ts +5 -1
- package/dist/core/AccessKey.d.ts.map +1 -1
- package/dist/core/AccessKey.js +2 -0
- package/dist/core/AccessKey.js.map +1 -1
- package/dist/core/Adapter.d.ts +4 -0
- package/dist/core/Adapter.d.ts.map +1 -1
- package/dist/core/Client.d.ts.map +1 -1
- package/dist/core/Client.js +62 -2
- package/dist/core/Client.js.map +1 -1
- package/dist/core/ExecutionError.d.ts.map +1 -1
- package/dist/core/ExecutionError.js +38 -3
- package/dist/core/ExecutionError.js.map +1 -1
- package/dist/core/Provider.d.ts.map +1 -1
- package/dist/core/Provider.js +16 -4
- package/dist/core/Provider.js.map +1 -1
- package/dist/core/Schema.d.ts +43 -0
- package/dist/core/Schema.d.ts.map +1 -1
- package/dist/core/adapters/privy.d.ts.map +1 -1
- package/dist/core/adapters/privy.js +7 -2
- package/dist/core/adapters/privy.js.map +1 -1
- package/dist/core/zod/rpc.d.ts +18 -0
- package/dist/core/zod/rpc.d.ts.map +1 -1
- package/dist/core/zod/rpc.js +13 -2
- package/dist/core/zod/rpc.js.map +1 -1
- package/dist/react/Remote.d.ts.map +1 -1
- package/dist/react/Remote.js +5 -1
- package/dist/react/Remote.js.map +1 -1
- package/dist/server/CliAuth.d.ts.map +1 -1
- package/dist/server/CliAuth.js +6 -4
- package/dist/server/CliAuth.js.map +1 -1
- package/dist/server/internal/handlers/relay.d.ts +0 -7
- package/dist/server/internal/handlers/relay.d.ts.map +1 -1
- package/dist/server/internal/handlers/relay.js +0 -46
- package/dist/server/internal/handlers/relay.js.map +1 -1
- package/dist/server/internal/handlers/sponsorship.d.ts +0 -2
- package/dist/server/internal/handlers/sponsorship.d.ts.map +1 -1
- package/dist/server/internal/handlers/sponsorship.js +1 -2
- package/dist/server/internal/handlers/sponsorship.js.map +1 -1
- package/dist/trusted-hosts.json +1 -0
- package/package.json +5 -5
- package/src/cli/Provider.localnet.test.ts +3 -3
- package/src/core/AccessKey.permissions.test.ts +73 -0
- package/src/core/AccessKey.test.ts +29 -27
- package/src/core/AccessKey.ts +7 -1
- package/src/core/Adapter.ts +4 -0
- package/src/core/Client.test.ts +153 -1
- package/src/core/Client.ts +73 -2
- package/src/core/ExecutionError.ts +39 -3
- package/src/core/Provider.localnet.test.ts +116 -1
- package/src/core/Provider.ts +25 -8
- package/src/core/Schema.test-d.ts +14 -0
- package/src/core/adapters/deviceCode/deviceCode.localnet.test.ts +4 -1
- package/src/core/adapters/privy.test.ts +150 -8
- package/src/core/adapters/privy.ts +13 -5
- package/src/core/zod/request.test.ts +26 -0
- package/src/core/zod/rpc.ts +15 -4
- package/src/react/Remote.test.ts +153 -0
- package/src/react/Remote.ts +7 -1
- package/src/server/CliAuth.test.ts +5 -3
- package/src/server/CliAuth.ts +6 -4
- package/src/server/internal/handlers/relay.ts +0 -56
- package/src/server/internal/handlers/sponsorship.ts +1 -4
- package/src/trusted-hosts.json +1 -0
- package/dist/server/internal/handlers/multisig.d.ts +0 -214
- package/dist/server/internal/handlers/multisig.d.ts.map +0 -1
- package/dist/server/internal/handlers/multisig.js +0 -505
- package/dist/server/internal/handlers/multisig.js.map +0 -1
- package/src/server/internal/handlers/multisig.localnet.test.ts +0 -94
- package/src/server/internal/handlers/multisig.test.ts +0 -743
- package/src/server/internal/handlers/multisig.ts +0 -774
|
@@ -21,7 +21,6 @@ export type Rpc = UnionOmit<ExecutionError, 'args'>
|
|
|
21
21
|
/** Human-readable messages keyed by ABI error name. */
|
|
22
22
|
export const messages: Record<AbiErrorName, string> = {
|
|
23
23
|
AbdicationAlreadyScheduled: 'Capability {0} abdication is already scheduled.',
|
|
24
|
-
AccountAlreadyInitialized: 'Account already initialized.',
|
|
25
24
|
AccountNotAllowed: 'Account {0} is not allowed.',
|
|
26
25
|
ActiveLeaderRemoved: 'The active leader cannot be removed.',
|
|
27
26
|
ActiveSupplyWithoutBacking: 'Active supply has no backing.',
|
|
@@ -32,6 +31,7 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
32
31
|
AlreadyInitialized: 'Already initialized.',
|
|
33
32
|
AmountExceedsDeposit: 'Amount exceeds deposit.',
|
|
34
33
|
AmountExceedsLimit: 'Amount exceeds limit.',
|
|
34
|
+
AmountIsZero: 'Amount cannot be zero.',
|
|
35
35
|
AmountNotIncreasing: 'Amount is not increasing.',
|
|
36
36
|
AmountOverflow: 'Amount overflow.',
|
|
37
37
|
AssetsNotArrived: 'Assets for request {0} have not arrived.',
|
|
@@ -56,6 +56,8 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
56
56
|
ContractPaused: 'Contract is paused.',
|
|
57
57
|
CreditExceedsBalance:
|
|
58
58
|
'Credit for asset {0} exceeds the balance. Available: {1}, requested total: {2}.',
|
|
59
|
+
CustomerIdIsZero: 'Customer ID cannot be zero.',
|
|
60
|
+
DeadlineExpired: 'Deadline {0} has expired at timestamp {1}.',
|
|
59
61
|
DepositBlockCapacityExceeded: 'Deposit block capacity of {0} exceeded.',
|
|
60
62
|
DepositOverflow: 'Deposit overflow.',
|
|
61
63
|
DepositTooSmall: 'Deposit too small.',
|
|
@@ -76,6 +78,7 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
76
78
|
EarnVaultNotSet: 'Earn vault is not set.',
|
|
77
79
|
EmptyDeploymentId: 'Deployment ID cannot be empty.',
|
|
78
80
|
EmptyEarnShareMetadata: 'Empty earn share metadata.',
|
|
81
|
+
EmptyInstallation: 'Installation cannot be empty.',
|
|
79
82
|
EmptyMetadata: 'Empty metadata.',
|
|
80
83
|
EmptyV1ValidatorSet: 'Validator set is empty.',
|
|
81
84
|
EncryptionKeyExpired: 'Encryption key {0}, activated at block {1}, expired at block {2}.',
|
|
@@ -86,6 +89,7 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
86
89
|
EngineEarnVaultMismatch: 'Engine earn vault mismatch.',
|
|
87
90
|
EngineExitUnsupported: 'This engine does not support exits.',
|
|
88
91
|
EngineSharesTooLarge: 'Engine share amount {0} is too large.',
|
|
92
|
+
ExactBalanceDeltaFailed: 'Balance change for {0} was {2}, expected {1}.',
|
|
89
93
|
ExceedsMaxEarnShares: 'Maximum earn shares exceeded.',
|
|
90
94
|
ExcessiveConversionLoss:
|
|
91
95
|
'Conversion loss is excessive. Input shares: {0}, represented shares: {1}.',
|
|
@@ -99,6 +103,7 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
99
103
|
FailedDeployment: 'Deployment failed.',
|
|
100
104
|
FeeValuationStale: 'Fee valuation is stale.',
|
|
101
105
|
FinalizedAssetMismatch: 'Finalized asset {0} does not match.',
|
|
106
|
+
FutureOracleTimestamp: 'Oracle timestamp {0} is after the current time ({1}).',
|
|
102
107
|
GasFeeRateTooHigh: 'Gas fee rate too high.',
|
|
103
108
|
GasLimitTooHigh: 'Gas limit too high.',
|
|
104
109
|
GlobalFeeDisableUnavailable: 'Global fee disabling is unavailable.',
|
|
@@ -117,11 +122,13 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
117
122
|
'Insufficient engine share surplus. Available: {0}, requested: {1}.',
|
|
118
123
|
InsufficientFeeTokenBalance: 'Insufficient fee token balance.',
|
|
119
124
|
InsufficientFunding: 'Insufficient funding. Required: {0}, actual: {1}.',
|
|
125
|
+
InsufficientInventory: 'Insufficient inventory for {0}: available {1}, required {2}.',
|
|
120
126
|
InsufficientLiquidity: 'Not enough liquidity in the order book to fill this trade.',
|
|
121
127
|
InsufficientOutput:
|
|
122
128
|
'The output amount is below the slippage minimum — try increasing slippage tolerance.',
|
|
123
129
|
InsufficientReserves: 'Insufficient reserves.',
|
|
124
130
|
InvalidAccount: 'Invalid account.',
|
|
131
|
+
InvalidAddress: 'Invalid address.',
|
|
125
132
|
InvalidAdmin: 'Invalid admin.',
|
|
126
133
|
InvalidAmount: 'Invalid amount.',
|
|
127
134
|
InvalidArrayLengths: 'Invalid array lengths.',
|
|
@@ -152,6 +159,7 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
152
159
|
InvalidEngine: 'Invalid engine.',
|
|
153
160
|
InvalidEngineShareScale: 'Invalid engine share scale.',
|
|
154
161
|
InvalidEphemeralPubkey: 'Invalid ephemeral pubkey.',
|
|
162
|
+
InvalidExistingDeployment: 'Existing deployment {0} is invalid.',
|
|
155
163
|
InvalidExpiringNonceExpiry: 'Invalid expiring nonce expiry.',
|
|
156
164
|
InvalidFallbackRecipient: 'Invalid fallback recipient.',
|
|
157
165
|
InvalidFeeClaimReceiver: 'Invalid fee claim receiver.',
|
|
@@ -163,16 +171,21 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
163
171
|
InvalidLeader: 'Invalid leader.',
|
|
164
172
|
InvalidLogoURI: 'Invalid logo URI.',
|
|
165
173
|
InvalidMasterAddress: 'Invalid master address.',
|
|
174
|
+
InvalidMaxOracleAge: 'Invalid maximum oracle age.',
|
|
175
|
+
InvalidMaxPriceDeviationBps: 'Maximum price deviation {0} exceeds {1} basis points.',
|
|
166
176
|
InvalidMaxRateAge: 'Invalid maximum rate age.',
|
|
167
177
|
InvalidMigrationIndex: 'Invalid migration index.',
|
|
168
178
|
InvalidMode: 'Invalid mode.',
|
|
169
179
|
InvalidMultiproof: 'Invalid multiproof.',
|
|
180
|
+
InvalidMultisigOwner: 'Invalid multisig owner.',
|
|
170
181
|
InvalidNonceKey: 'Invalid nonce key.',
|
|
182
|
+
InvalidOraclePriceToleranceBps: 'Oracle price tolerance {0} exceeds {1} basis points.',
|
|
171
183
|
InvalidOwner: 'Invalid owner.',
|
|
172
184
|
InvalidOwnerOrder: 'Invalid owner order.',
|
|
173
185
|
InvalidPayee: 'Invalid payee.',
|
|
174
186
|
InvalidPayload: 'Invalid payload.',
|
|
175
187
|
InvalidPolicyType: 'Invalid policy type.',
|
|
188
|
+
InvalidPrice: 'Invalid price.',
|
|
176
189
|
InvalidPrivateAsset: 'Invalid private asset.',
|
|
177
190
|
InvalidProof: 'Invalid proof.',
|
|
178
191
|
InvalidProofOfPossession: 'Invalid proof of possession.',
|
|
@@ -186,6 +199,7 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
186
199
|
InvalidRecoveryAuthority: 'Invalid recovery authority.',
|
|
187
200
|
InvalidReturnToken: 'Invalid return token.',
|
|
188
201
|
InvalidRevealTo: 'Invalid reveal to.',
|
|
202
|
+
InvalidRewardConfiguration: 'Invalid reward configuration.',
|
|
189
203
|
InvalidRoutePair: 'Invalid route pair.',
|
|
190
204
|
InvalidSequencerSet: 'Invalid sequencer set.',
|
|
191
205
|
InvalidSignature: 'Invalid signature.',
|
|
@@ -245,18 +259,21 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
245
259
|
NotEmergencyGuardianOrOperator: 'Caller is not the emergency guardian or operator.',
|
|
246
260
|
NotEngine: 'Not engine.',
|
|
247
261
|
NotFactory: 'Not factory.',
|
|
262
|
+
NotGuardianOrOwner: 'Not the guardian or owner.',
|
|
248
263
|
NotHostPort: '"{1}" is not a valid host:port for {0}.',
|
|
249
264
|
NotInitialized: 'Not initialized.',
|
|
250
265
|
NotIp: '"{0}" is not a valid IP address.',
|
|
251
266
|
NotIpPort: '"{1}" is not a valid IP:port for {0}.',
|
|
252
|
-
NotMultisigAccount: 'Not multisig account.',
|
|
253
267
|
NotOperator: 'Not operator.',
|
|
254
268
|
NotOwner: 'Not owner.',
|
|
255
269
|
NotPauseAuthority: 'Not pause authority.',
|
|
270
|
+
NotPaused: 'Not paused.',
|
|
256
271
|
NotPayeeOrOperator: 'Not payee or operator.',
|
|
257
272
|
NotPayer: 'Not payer.',
|
|
258
273
|
NotPendingAdmin: 'Not pending admin.',
|
|
259
274
|
NotPendingDistributor: 'Not pending distributor.',
|
|
275
|
+
NotPendingOperator: 'Not the pending operator.',
|
|
276
|
+
NotPendingOwner: 'Not the pending owner.',
|
|
260
277
|
NotRequesterOrJanitor: 'Caller is not the requester or janitor.',
|
|
261
278
|
NotSelf: 'Not self.',
|
|
262
279
|
NotSequencer: 'Not sequencer.',
|
|
@@ -266,6 +283,10 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
266
283
|
OnlySequencer: 'Only the sequencer may call this function.',
|
|
267
284
|
OnlyZoneInbox: 'Only the zone inbox may call this function.',
|
|
268
285
|
OperatorMigrationDisabled: 'Operator migration disabled.',
|
|
286
|
+
OperatorTransferNotPending: 'No operator transfer is pending.',
|
|
287
|
+
OracleDecimalsChanged: 'Oracle decimals changed from {0} to {1}.',
|
|
288
|
+
OraclePriceToleranceExceeded: 'Oracle price {1} differs from {0} by more than {2} basis points.',
|
|
289
|
+
OracleTimestampTooOld: 'Oracle timestamp {1} is before the minimum ({0}).',
|
|
269
290
|
OrderDoesNotExist: 'No order exists with the given ID.',
|
|
270
291
|
OrderNotStale: 'This order is not yet eligible for stale-cleanup.',
|
|
271
292
|
OwnableInvalidOwner: 'Owner {0} is invalid.',
|
|
@@ -281,13 +302,18 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
281
302
|
PolicyNotFound: 'Policy not found.',
|
|
282
303
|
PolicyNotSimple: 'Policy is not a simple policy.',
|
|
283
304
|
PortalIsPaused: 'Portal is paused.',
|
|
305
|
+
PriceAnchorNotInitialized: 'Price anchor is not initialized for direction {0}.',
|
|
306
|
+
PriceDeviationExceeded:
|
|
307
|
+
'Price {1} deviates from anchor {2} by more than {3} basis points for direction {0}.',
|
|
284
308
|
ProofOfWorkFailed: 'Proof of work failed.',
|
|
285
309
|
ProtectedAddress: 'Address is protected.',
|
|
286
310
|
ProtocolNonceNotSupported: 'Protocol nonce is not supported.',
|
|
287
311
|
PublicKeyAlreadyExists: 'Public key already exists.',
|
|
288
312
|
QueueTrackingDisabled: 'Tracking is disabled for queue {0}.',
|
|
289
313
|
QueueUnavailable: 'Queue unavailable.',
|
|
314
|
+
QueuedAssetsBelowMinimum: 'Queued assets ({1}) are below the minimum ({0}).',
|
|
290
315
|
RateChangedWithinTransaction: 'Rate changed within the transaction. Expected {0}, got {1}.',
|
|
316
|
+
RecipientNotAllowed: 'Recipient {0} is not allowed.',
|
|
291
317
|
ReentrancyGuardReentrantCall: 'Reentrant call blocked.',
|
|
292
318
|
ReentrantCall: 'Reentrant call blocked.',
|
|
293
319
|
ReentrantWithdrawal: 'Reentrant withdrawal blocked.',
|
|
@@ -299,18 +325,22 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
299
325
|
RootTotalDecreased: 'Root total decreased from {0} to {1}.',
|
|
300
326
|
RootTotalExceeded: 'Requested amount {0} exceeds the remaining entitlement {1}.',
|
|
301
327
|
SameEngine: 'Same engine.',
|
|
302
|
-
|
|
328
|
+
SameToken: 'Tokens must be different.',
|
|
303
329
|
SequencerConfigurationUnchanged: 'Sequencer configuration unchanged.',
|
|
330
|
+
SettlementAlreadyConsumed: 'Settlement {0} has already been consumed.',
|
|
304
331
|
SignatureTypeMismatch: 'Signature type mismatch. Expected {0}, got {1}.',
|
|
332
|
+
SlippageExceeded: 'Amount {1} exceeds slippage limit {0}.',
|
|
305
333
|
SolveUnderfunded: 'Solve for asset {0} is underfunded. Delivered: {1}, required: {2}.',
|
|
306
334
|
SpendingLimitExceeded: 'Spending limit exceeded.',
|
|
307
335
|
StaleAccountantRate: 'Accountant rate updated at {0} is stale at time {1}; maximum age is {2}.',
|
|
308
336
|
StaleLeadershipEpoch: 'Stale leadership epoch. Expected {0}, got {1}.',
|
|
337
|
+
StaleOraclePrice: 'Oracle price timestamp {0} exceeds the maximum age ({1}).',
|
|
309
338
|
StaleRootVersion: 'Stale root version. Expected {0}, got {1}.',
|
|
310
339
|
StaticCallNotAllowed: 'Static call not allowed.',
|
|
311
340
|
SupplyCapExceeded: 'Supply cap exceeded.',
|
|
312
341
|
SweepExceedsAvailableBalance:
|
|
313
342
|
'Sweep for token {0} exceeds the balance. Available: {1}, requested: {2}.',
|
|
343
|
+
TakerNotAllowed: 'Taker {0} is not allowed.',
|
|
314
344
|
TickOutOfBounds: 'Price tick {0} is outside the allowed range.',
|
|
315
345
|
TokenAlreadyEnabled: 'Token already enabled.',
|
|
316
346
|
TokenAlreadyExists: 'Token {0} already exists.',
|
|
@@ -319,6 +349,7 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
319
349
|
TokenEnablementBlockCapacityExceeded: 'Token enablement block capacity of {0} exceeded.',
|
|
320
350
|
TokenMetadataTooLong: 'Token metadata too long.',
|
|
321
351
|
TokenNotEnabled: 'Token not enabled.',
|
|
352
|
+
TokenTransferFailed: 'Transfer of token {0} failed.',
|
|
322
353
|
TokenTransferPolicyNotSet: 'Token transfer policy not set.',
|
|
323
354
|
TooManyOwners: 'Too many owners.',
|
|
324
355
|
TooManyWithdrawalsThisBlock: 'Too many withdrawals this block.',
|
|
@@ -327,6 +358,7 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
327
358
|
Unauthorized: 'Unauthorized.',
|
|
328
359
|
UnauthorizedCaller: 'Unauthorized caller.',
|
|
329
360
|
UnauthorizedClaimer: 'Unauthorized claimer.',
|
|
361
|
+
UnauthorizedMultisigCaller: 'Unauthorized multisig caller.',
|
|
330
362
|
UnexpectedAssetsReceived: 'Unexpected assets received. Expected {0}, got {1}.',
|
|
331
363
|
UnexpectedEarnShares: 'Unexpected earn shares. Reported: {0}, measured: {1}.',
|
|
332
364
|
UnexpectedRetainedAssets: 'Unexpected assets retained. Balance before: {0}, after: {1}.',
|
|
@@ -334,6 +366,8 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
334
366
|
UnexpectedVenueSharesQueued: 'Unexpected venue shares queued. Expected {0}, got {1}.',
|
|
335
367
|
Uninitialized: 'Uninitialized.',
|
|
336
368
|
UnsupportedAccountantFreshness: 'Accountant {0} does not support freshness checks.',
|
|
369
|
+
UnsupportedDecimals: 'Unsupported decimals: base {0}, quote {1}, oracle {2}.',
|
|
370
|
+
UnsupportedToken: 'Token {0} is not supported.',
|
|
337
371
|
UnsupportedTransferPolicy: 'Transfer policy {0} has unsupported type {1}.',
|
|
338
372
|
ValidatorAlreadyDeactivated: 'Validator is already deactivated.',
|
|
339
373
|
ValidatorAlreadyExists: 'Validator already exists.',
|
|
@@ -352,8 +386,10 @@ export const messages: Record<AbiErrorName, string> = {
|
|
|
352
386
|
ZeroMinimumAssets: 'Minimum assets cannot be zero.',
|
|
353
387
|
ZeroMinimumEarnShares: 'Minimum earn shares cannot be zero.',
|
|
354
388
|
ZeroMinimumEngineShares: 'Minimum engine shares cannot be zero.',
|
|
389
|
+
ZeroMinimumQueuedAssets: 'Minimum queued assets cannot be zero.',
|
|
355
390
|
ZeroPublicKey: 'Public key cannot be zero.',
|
|
356
391
|
ZeroRoot: 'Root cannot be zero.',
|
|
392
|
+
ZeroSettlementId: 'Settlement ID cannot be zero.',
|
|
357
393
|
ZeroStatementHash: 'Statement hash cannot be zero.',
|
|
358
394
|
}
|
|
359
395
|
|
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
} from 'viem'
|
|
12
12
|
import {
|
|
13
13
|
getBalance,
|
|
14
|
+
getBlockNumber,
|
|
15
|
+
getLogs,
|
|
16
|
+
getTransactionReceipt,
|
|
14
17
|
sendCalls,
|
|
15
18
|
sendTransactionSync,
|
|
16
19
|
signMessage,
|
|
@@ -1109,6 +1112,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
|
|
|
1109
1112
|
{
|
|
1110
1113
|
"contractAddress": null,
|
|
1111
1114
|
"feeToken": "0x20c0000000000000000000000000000000000000",
|
|
1115
|
+
"multisig": undefined,
|
|
1112
1116
|
"status": "success",
|
|
1113
1117
|
"to": "0x20c0000000000000000000000000000000000000",
|
|
1114
1118
|
"type": "0x76",
|
|
@@ -1386,7 +1390,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
|
|
|
1386
1390
|
from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
|
|
1387
1391
|
|
|
1388
1392
|
Details: plain send failure
|
|
1389
|
-
Version: viem@2.56.
|
|
1393
|
+
Version: viem@2.56.9]
|
|
1390
1394
|
`)
|
|
1391
1395
|
})
|
|
1392
1396
|
})
|
|
@@ -2192,6 +2196,66 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
|
|
|
2192
2196
|
})
|
|
2193
2197
|
|
|
2194
2198
|
describe('wallet_revokeAccessKey', () => {
|
|
2199
|
+
test('error: rejects an authorization for a different access key', async () => {
|
|
2200
|
+
const provider = Provider.create({ adapter: adapter(), chains: [chain] })
|
|
2201
|
+
await connect(provider)
|
|
2202
|
+
|
|
2203
|
+
const connected = (await provider.request({ method: 'eth_accounts' }))[0]!
|
|
2204
|
+
const first = await provider.request({
|
|
2205
|
+
method: 'wallet_authorizeAccessKey',
|
|
2206
|
+
params: [{ expiry: Expiry.days(1) }],
|
|
2207
|
+
})
|
|
2208
|
+
const second = await provider.request({
|
|
2209
|
+
method: 'wallet_authorizeAccessKey',
|
|
2210
|
+
params: [{ expiry: Expiry.days(1) }],
|
|
2211
|
+
})
|
|
2212
|
+
|
|
2213
|
+
await expect(
|
|
2214
|
+
provider.request({
|
|
2215
|
+
method: 'wallet_revokeAccessKey',
|
|
2216
|
+
params: [
|
|
2217
|
+
{
|
|
2218
|
+
address: connected,
|
|
2219
|
+
accessKeyAddress: first.keyAuthorization.address!,
|
|
2220
|
+
keyAuthorization: second.keyAuthorization,
|
|
2221
|
+
},
|
|
2222
|
+
],
|
|
2223
|
+
}),
|
|
2224
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
2225
|
+
`[RpcResponse.InvalidParamsError: \`keyAuthorization\` must authorize \`accessKeyAddress\`.]`,
|
|
2226
|
+
)
|
|
2227
|
+
})
|
|
2228
|
+
|
|
2229
|
+
test('behavior: atomically authorizes and revokes an unpublished access key', async () => {
|
|
2230
|
+
const provider = Provider.create({ adapter: adapter(), chains: [chain] })
|
|
2231
|
+
await connect(provider)
|
|
2232
|
+
|
|
2233
|
+
const connected = (await provider.request({ method: 'eth_accounts' }))[0]!
|
|
2234
|
+
await fund(connected)
|
|
2235
|
+
|
|
2236
|
+
const { keyAuthorization } = await provider.request({
|
|
2237
|
+
method: 'wallet_authorizeAccessKey',
|
|
2238
|
+
params: [{ expiry: Expiry.days(1) }],
|
|
2239
|
+
})
|
|
2240
|
+
|
|
2241
|
+
await provider.request({
|
|
2242
|
+
method: 'wallet_revokeAccessKey',
|
|
2243
|
+
params: [
|
|
2244
|
+
{
|
|
2245
|
+
address: connected,
|
|
2246
|
+
accessKeyAddress: keyAuthorization.address!,
|
|
2247
|
+
keyAuthorization,
|
|
2248
|
+
},
|
|
2249
|
+
],
|
|
2250
|
+
})
|
|
2251
|
+
|
|
2252
|
+
const metadata = await Actions.accessKey.getMetadata(getClient(), {
|
|
2253
|
+
account: connected,
|
|
2254
|
+
accessKey: keyAuthorization.address!,
|
|
2255
|
+
})
|
|
2256
|
+
expect(metadata.isRevoked).toMatchInlineSnapshot(`true`)
|
|
2257
|
+
})
|
|
2258
|
+
|
|
2195
2259
|
test('default: revokes a granted access key on-chain', async () => {
|
|
2196
2260
|
const provider = Provider.create({ adapter: adapter(), chains: [chain] })
|
|
2197
2261
|
await connect(provider)
|
|
@@ -3232,6 +3296,57 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
|
|
|
3232
3296
|
expect((result.tx as { feePayerSignature?: unknown }).feePayerSignature).toBeDefined()
|
|
3233
3297
|
})
|
|
3234
3298
|
|
|
3299
|
+
test('behavior: feePayer URL sponsors wallet_revokeAccessKey', async () => {
|
|
3300
|
+
const provider = Provider.create({ adapter: adapter(), chains: [chain] })
|
|
3301
|
+
const connected = await connect(provider)
|
|
3302
|
+
await fund(connected)
|
|
3303
|
+
const { keyAuthorization } = await provider.request({
|
|
3304
|
+
method: 'wallet_authorizeAccessKey',
|
|
3305
|
+
params: [{ expiry: Expiry.days(1) }],
|
|
3306
|
+
})
|
|
3307
|
+
await provider.request({
|
|
3308
|
+
method: 'eth_sendTransactionSync',
|
|
3309
|
+
params: [{ calls: [transferCall] }],
|
|
3310
|
+
})
|
|
3311
|
+
|
|
3312
|
+
const rpc = getClient()
|
|
3313
|
+
const blockNumber = await getBlockNumber(rpc)
|
|
3314
|
+
|
|
3315
|
+
await provider.request({
|
|
3316
|
+
method: 'wallet_revokeAccessKey',
|
|
3317
|
+
params: [
|
|
3318
|
+
{
|
|
3319
|
+
accessKeyAddress: keyAuthorization.address!,
|
|
3320
|
+
address: connected,
|
|
3321
|
+
feePayer: server.url,
|
|
3322
|
+
},
|
|
3323
|
+
],
|
|
3324
|
+
})
|
|
3325
|
+
|
|
3326
|
+
const logs = await getLogs(rpc, {
|
|
3327
|
+
address: Addresses.accountKeychain,
|
|
3328
|
+
fromBlock: blockNumber,
|
|
3329
|
+
toBlock: 'latest',
|
|
3330
|
+
})
|
|
3331
|
+
const event = Actions.accessKey.revoke.extractEvent(logs)
|
|
3332
|
+
const receipt = await getTransactionReceipt(rpc, { hash: event.transactionHash })
|
|
3333
|
+
const metadata = await Actions.accessKey.getMetadata(rpc, {
|
|
3334
|
+
account: connected,
|
|
3335
|
+
accessKey: keyAuthorization.address!,
|
|
3336
|
+
})
|
|
3337
|
+
expect({
|
|
3338
|
+
feePayer: receipt.feePayer,
|
|
3339
|
+
isRevoked: metadata.isRevoked,
|
|
3340
|
+
status: receipt.status,
|
|
3341
|
+
}).toMatchInlineSnapshot(`
|
|
3342
|
+
{
|
|
3343
|
+
"feePayer": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
|
3344
|
+
"isRevoked": true,
|
|
3345
|
+
"status": "success",
|
|
3346
|
+
}
|
|
3347
|
+
`)
|
|
3348
|
+
})
|
|
3349
|
+
|
|
3235
3350
|
test('behavior: feePayer: true uses default from Provider.create', async () => {
|
|
3236
3351
|
const provider = Provider.create({
|
|
3237
3352
|
adapter: adapter({ feePayer: server.url }),
|
package/src/core/Provider.ts
CHANGED
|
@@ -558,28 +558,29 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
558
558
|
})
|
|
559
559
|
}
|
|
560
560
|
|
|
561
|
-
async function revokeAccessKey(parameters: {
|
|
562
|
-
address: Address.Address
|
|
563
|
-
accessKeyAddress: Address.Address
|
|
564
|
-
}) {
|
|
561
|
+
async function revokeAccessKey(parameters: Adapter.revokeAccessKey.Parameters) {
|
|
565
562
|
const selected = await getAdapterAccount({ address: parameters.address })
|
|
563
|
+
const feePayer = parameters.feePayer
|
|
566
564
|
const client = getWalletClient({
|
|
567
565
|
account: selected.account,
|
|
566
|
+
feePayer: feePayer === true ? undefined : feePayer,
|
|
568
567
|
transport: selected.transport,
|
|
569
568
|
})
|
|
570
569
|
if (selected.account.type === 'json-rpc') {
|
|
571
570
|
await client.request({
|
|
572
571
|
method: 'wallet_revokeAccessKey' as never,
|
|
573
|
-
params: [parameters] as never,
|
|
572
|
+
params: [z.encode(Rpc.wallet_revokeAccessKey.parameters, parameters)] as never,
|
|
574
573
|
})
|
|
575
574
|
} else {
|
|
576
575
|
try {
|
|
577
|
-
await Actions.accessKey.revokeSync(
|
|
576
|
+
await Actions.accessKey.revokeSync(client, {
|
|
578
577
|
account: selected.account as TempoAccount.Account,
|
|
579
578
|
accessKey: parameters.accessKeyAddress,
|
|
579
|
+
...(feePayer ? { feePayer: true as never } : {}),
|
|
580
|
+
...(parameters.keyAuthorization ? { keyAuthorization: parameters.keyAuthorization } : {}),
|
|
580
581
|
})
|
|
581
582
|
} catch (error) {
|
|
582
|
-
if (!AccessKey.isUnavailableError(error)) throw error
|
|
583
|
+
if (typeof feePayer === 'string' || !AccessKey.isUnavailableError(error)) throw error
|
|
583
584
|
}
|
|
584
585
|
}
|
|
585
586
|
store.accessKeys.remove({
|
|
@@ -677,6 +678,13 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
677
678
|
parameters: Adapter.revokeAccessKey.Parameters,
|
|
678
679
|
request: Pick<Rpc.wallet_revokeAccessKey.Encoded, 'method' | 'params'>,
|
|
679
680
|
) {
|
|
681
|
+
if (
|
|
682
|
+
parameters.keyAuthorization &&
|
|
683
|
+
!AddressUtil.isEqual(parameters.keyAuthorization.address, parameters.accessKeyAddress)
|
|
684
|
+
)
|
|
685
|
+
throw new RpcResponse.InvalidParamsError({
|
|
686
|
+
message: '`keyAuthorization` must authorize `accessKeyAddress`.',
|
|
687
|
+
})
|
|
680
688
|
if (actions.revokeAccessKey) return await actions.revokeAccessKey(parameters, request)
|
|
681
689
|
if (instance.getAccount) return await revokeAccessKey(parameters)
|
|
682
690
|
unsupported('revokeAccessKey')
|
|
@@ -713,6 +721,12 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
713
721
|
return url
|
|
714
722
|
}
|
|
715
723
|
|
|
724
|
+
function resolveRevocationFeePayer(
|
|
725
|
+
feePayer: string | boolean | undefined,
|
|
726
|
+
): Adapter.revokeAccessKey.Parameters['feePayer'] {
|
|
727
|
+
return resolveFeePayer(feePayer) ?? (feePayer === true ? true : undefined)
|
|
728
|
+
}
|
|
729
|
+
|
|
716
730
|
function stripAuthorizeAccessKey(
|
|
717
731
|
parameters: create.AuthorizeAccessKeyParameters,
|
|
718
732
|
): Adapter.authorizeAccessKey.Parameters {
|
|
@@ -1608,7 +1622,10 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
1608
1622
|
case 'wallet_revokeAccessKey': {
|
|
1609
1623
|
assertConnected()
|
|
1610
1624
|
const [decoded] = request._decoded.params
|
|
1611
|
-
await revokeAccessKeyAction(
|
|
1625
|
+
await revokeAccessKeyAction(
|
|
1626
|
+
{ ...decoded, feePayer: resolveRevocationFeePayer(decoded.feePayer) },
|
|
1627
|
+
request,
|
|
1628
|
+
)
|
|
1612
1629
|
return
|
|
1613
1630
|
}
|
|
1614
1631
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RpcSchema } from 'ox'
|
|
2
|
+
import type { KeyAuthorization } from 'ox/tempo'
|
|
2
3
|
import type { Hex } from 'viem'
|
|
3
4
|
import { describe, expectTypeOf, test } from 'vp/test'
|
|
4
5
|
|
|
@@ -309,6 +310,19 @@ describe('Request', () => {
|
|
|
309
310
|
expectTypeOf<SwitchChain['params']>().toEqualTypeOf<readonly [{ chainId: number }]>()
|
|
310
311
|
})
|
|
311
312
|
|
|
313
|
+
test('wallet_revokeAccessKey supports app-provided fee sponsorship', () => {
|
|
314
|
+
expectTypeOf<Rpc.wallet_revokeAccessKey.Decoded['params']>().toEqualTypeOf<
|
|
315
|
+
readonly [
|
|
316
|
+
{
|
|
317
|
+
accessKeyAddress: Hex
|
|
318
|
+
address: Hex
|
|
319
|
+
feePayer?: boolean | string | undefined
|
|
320
|
+
keyAuthorization?: KeyAuthorization.Signed | undefined
|
|
321
|
+
},
|
|
322
|
+
]
|
|
323
|
+
>()
|
|
324
|
+
})
|
|
325
|
+
|
|
312
326
|
test('eth_accounts has no params', () => {
|
|
313
327
|
type EthAccounts = Extract<Schema.Request, { method: 'eth_accounts' }>
|
|
314
328
|
expectTypeOf<EthAccounts>().not.toHaveProperty('params')
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Hex } from 'ox'
|
|
2
|
+
import { SignatureEnvelope } from 'ox/tempo'
|
|
2
3
|
import { describe, expect, test } from 'vp/test'
|
|
3
4
|
import type { DeviceCode } from 'wata'
|
|
4
5
|
|
|
@@ -61,7 +62,9 @@ describe('deviceCode', () => {
|
|
|
61
62
|
const keyAuthorization = account.capabilities.keyAuthorization
|
|
62
63
|
? {
|
|
63
64
|
...account.capabilities.keyAuthorization,
|
|
64
|
-
signature: {
|
|
65
|
+
signature: {
|
|
66
|
+
type: SignatureEnvelope.fromRpc(account.capabilities.keyAuthorization.signature).type,
|
|
67
|
+
},
|
|
65
68
|
}
|
|
66
69
|
: undefined
|
|
67
70
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Address as core_Address, Hex, Secp256k1, Signature } from 'ox'
|
|
2
|
-
import { decodeFunctionData } from 'viem'
|
|
2
|
+
import { BaseError, decodeFunctionData } from 'viem'
|
|
3
3
|
import type { Address } from 'viem/accounts'
|
|
4
4
|
import { Abis } from 'viem/tempo'
|
|
5
5
|
import { describe, expect, test } from 'vp/test'
|
|
@@ -323,6 +323,140 @@ describe('privy', () => {
|
|
|
323
323
|
expect(store.getState().accessKeys).toMatchInlineSnapshot(`[]`)
|
|
324
324
|
})
|
|
325
325
|
|
|
326
|
+
test('behavior: revokeAccessKey forwards a fee payer URL', async () => {
|
|
327
|
+
const { adapter, client, store } = setup()
|
|
328
|
+
store.setState({ accounts: [{ address }], activeAccount: 0 })
|
|
329
|
+
|
|
330
|
+
await adapter.actions.revokeAccessKey!(
|
|
331
|
+
{ accessKeyAddress: other, address, feePayer: 'https://example.com/fee-payer' },
|
|
332
|
+
{
|
|
333
|
+
method: 'wallet_revokeAccessKey',
|
|
334
|
+
params: [{ accessKeyAddress: other, address, feePayer: 'https://example.com/fee-payer' }],
|
|
335
|
+
},
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
expect({
|
|
339
|
+
clientOptions: client.clientOptions,
|
|
340
|
+
transactions: client.transactions,
|
|
341
|
+
}).toMatchInlineSnapshot(`
|
|
342
|
+
{
|
|
343
|
+
"clientOptions": [
|
|
344
|
+
{
|
|
345
|
+
"feePayer": "https://example.com/fee-payer",
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
"transactions": [
|
|
349
|
+
{
|
|
350
|
+
"account": {
|
|
351
|
+
"address": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
352
|
+
"sign": [Function],
|
|
353
|
+
"signTransaction": [Function],
|
|
354
|
+
"source": "privy",
|
|
355
|
+
"type": "local",
|
|
356
|
+
},
|
|
357
|
+
"data": "0x5ae7ab320000000000000000000000002b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
358
|
+
"feePayer": true,
|
|
359
|
+
"to": "0xaAAAaaAA00000000000000000000000000000000",
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
}
|
|
363
|
+
`)
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
test('error: revokeAccessKey preserves local state on external fee payer errors', async () => {
|
|
367
|
+
const transactionError = new BaseError('revoke failed', {
|
|
368
|
+
cause: Object.assign(new Error('execution reverted'), {
|
|
369
|
+
data: { errorName: 'KeyAlreadyRevoked' },
|
|
370
|
+
}),
|
|
371
|
+
})
|
|
372
|
+
const { adapter, store } = setup({ transactionError })
|
|
373
|
+
store.setState({
|
|
374
|
+
accounts: [{ address }],
|
|
375
|
+
activeAccount: 0,
|
|
376
|
+
accessKeys: [
|
|
377
|
+
{
|
|
378
|
+
access: address,
|
|
379
|
+
address: other,
|
|
380
|
+
chainId: 1,
|
|
381
|
+
keyType: 'secp256k1',
|
|
382
|
+
} as never,
|
|
383
|
+
],
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
await expect(
|
|
387
|
+
adapter.actions.revokeAccessKey!(
|
|
388
|
+
{ accessKeyAddress: other, address, feePayer: 'https://example.com/fee-payer' },
|
|
389
|
+
{
|
|
390
|
+
method: 'wallet_revokeAccessKey',
|
|
391
|
+
params: [{ accessKeyAddress: other, address, feePayer: 'https://example.com/fee-payer' }],
|
|
392
|
+
},
|
|
393
|
+
),
|
|
394
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
395
|
+
[ContractFunctionExecutionError: revoke failed
|
|
396
|
+
|
|
397
|
+
Contract Call:
|
|
398
|
+
address: 0xaAAAaaAA00000000000000000000000000000000
|
|
399
|
+
function: revokeKey(address keyId)
|
|
400
|
+
args: (0x2b5ad5c4795c026514f8317c7a215e218dccd6cf)
|
|
401
|
+
sender: 0x7e5f4552091a69125d5dfcb7b8c2659029395bdf
|
|
402
|
+
|
|
403
|
+
Docs: https://viem.sh/docs/contract/writeContract
|
|
404
|
+
Details: execution reverted
|
|
405
|
+
Version: viem@2.56.9]
|
|
406
|
+
`)
|
|
407
|
+
|
|
408
|
+
expect(store.getState().accessKeys).toMatchInlineSnapshot(`
|
|
409
|
+
[
|
|
410
|
+
{
|
|
411
|
+
"access": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
412
|
+
"address": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
413
|
+
"chainId": 1,
|
|
414
|
+
"keyType": "secp256k1",
|
|
415
|
+
},
|
|
416
|
+
]
|
|
417
|
+
`)
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
test('behavior: revokeAccessKey forwards default fee sponsorship', async () => {
|
|
421
|
+
const { adapter, client, store } = setup()
|
|
422
|
+
store.setState({ accounts: [{ address }], activeAccount: 0 })
|
|
423
|
+
|
|
424
|
+
await adapter.actions.revokeAccessKey!(
|
|
425
|
+
{ accessKeyAddress: other, address, feePayer: true },
|
|
426
|
+
{
|
|
427
|
+
method: 'wallet_revokeAccessKey',
|
|
428
|
+
params: [{ accessKeyAddress: other, address, feePayer: true }],
|
|
429
|
+
},
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
expect({
|
|
433
|
+
clientOptions: client.clientOptions,
|
|
434
|
+
transactions: client.transactions,
|
|
435
|
+
}).toMatchInlineSnapshot(`
|
|
436
|
+
{
|
|
437
|
+
"clientOptions": [
|
|
438
|
+
{
|
|
439
|
+
"feePayer": undefined,
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
"transactions": [
|
|
443
|
+
{
|
|
444
|
+
"account": {
|
|
445
|
+
"address": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
446
|
+
"sign": [Function],
|
|
447
|
+
"signTransaction": [Function],
|
|
448
|
+
"source": "privy",
|
|
449
|
+
"type": "local",
|
|
450
|
+
},
|
|
451
|
+
"data": "0x5ae7ab320000000000000000000000002b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
452
|
+
"feePayer": true,
|
|
453
|
+
"to": "0xaAAAaaAA00000000000000000000000000000000",
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
}
|
|
457
|
+
`)
|
|
458
|
+
})
|
|
459
|
+
|
|
326
460
|
test('behavior: signing silently restores wallet accounts via the Privy SDK', async () => {
|
|
327
461
|
const { adapter, client, store } = setup()
|
|
328
462
|
store.setState({ accounts: [{ address }], activeAccount: 0 })
|
|
@@ -642,13 +776,17 @@ function setup(options: setup.Options = {}) {
|
|
|
642
776
|
getAccount: (() => {
|
|
643
777
|
throw new Error('not implemented')
|
|
644
778
|
}) as never,
|
|
645
|
-
getClient: (() =>
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
779
|
+
getClient: ((clientOptions: unknown) => {
|
|
780
|
+
client.clientOptions.push(clientOptions)
|
|
781
|
+
return {
|
|
782
|
+
chain: { id: 1 },
|
|
783
|
+
sendTransaction: async (parameters: unknown) => {
|
|
784
|
+
client.transactions.push(parameters)
|
|
785
|
+
if (options.transactionError) throw options.transactionError
|
|
786
|
+
return Hex.padLeft('0x1', 32)
|
|
787
|
+
},
|
|
788
|
+
}
|
|
789
|
+
}) as never,
|
|
652
790
|
storage,
|
|
653
791
|
store,
|
|
654
792
|
})
|
|
@@ -675,6 +813,8 @@ declare namespace setup {
|
|
|
675
813
|
restoreError?: unknown
|
|
676
814
|
token?: string | null | undefined
|
|
677
815
|
signError?: unknown
|
|
816
|
+
/** Make the mock wallet client transaction fail. */
|
|
817
|
+
transactionError?: unknown
|
|
678
818
|
/** Override the value returned by the embedded provider's `secp256k1_sign`. */
|
|
679
819
|
signResult?: unknown
|
|
680
820
|
/** Force the test wallet to sign with this private key (for wrong-signer tests). */
|
|
@@ -683,6 +823,7 @@ declare namespace setup {
|
|
|
683
823
|
}
|
|
684
824
|
|
|
685
825
|
type MockClient = privy.Client & {
|
|
826
|
+
clientOptions: unknown[]
|
|
686
827
|
createCalls: number
|
|
687
828
|
initCalls: number
|
|
688
829
|
loadCalls: number
|
|
@@ -699,6 +840,7 @@ type MockClient = privy.Client & {
|
|
|
699
840
|
|
|
700
841
|
function createClient(options: setup.Options = {}) {
|
|
701
842
|
const client: MockClient = {
|
|
843
|
+
clientOptions: [] as unknown[],
|
|
702
844
|
createCalls: 0,
|
|
703
845
|
initCalls: 0,
|
|
704
846
|
loadCalls: 0,
|