kokio-sdk 1.1.0 → 2.0.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.
Files changed (106) hide show
  1. package/README.md +68 -19
  2. package/dist/esm/abis/BeaconProxy.js +30 -46
  3. package/dist/esm/abis/DeviceWallet.js +546 -535
  4. package/dist/esm/abis/DeviceWalletFactory.js +537 -544
  5. package/dist/esm/abis/ESIMWallet.js +440 -299
  6. package/dist/esm/abis/ESIMWalletFactory.js +372 -289
  7. package/dist/esm/abis/LazyWalletRegistry.js +941 -421
  8. package/dist/esm/abis/P256Verifier.js +29 -29
  9. package/dist/esm/abis/ProtocolAdmin.js +1228 -0
  10. package/dist/esm/abis/Registry.js +1207 -466
  11. package/dist/esm/abis/RegistryHelper.js +414 -20
  12. package/dist/esm/abis/index.js +2 -1
  13. package/dist/esm/admin/config-admin.js +7 -0
  14. package/dist/esm/admin/interface/deviceWalletClass.js +27 -6
  15. package/dist/esm/admin/interface/deviceWalletFactoryClass.js +41 -19
  16. package/dist/esm/admin/interface/eSIMWalletClass.js +10 -1
  17. package/dist/esm/admin/interface/eSIMWalletFactoryClass.js +24 -2
  18. package/dist/esm/admin/interface/lazyWalletRegistryClass.js +83 -4
  19. package/dist/esm/admin/interface/protocolAdminClass.js +195 -0
  20. package/dist/esm/admin/interface/registryClass.js +93 -2
  21. package/dist/esm/config.js +42 -7
  22. package/dist/esm/interface/deviceWalletClass.js +45 -5
  23. package/dist/esm/interface/deviceWalletFactoryClass.js +20 -1
  24. package/dist/esm/interface/eSIMWalletClass.js +12 -3
  25. package/dist/esm/interface/registryClass.js +47 -0
  26. package/dist/esm/interface/smartAccountClass.js +2 -4
  27. package/dist/esm/logic/account-kit/createSmartAccount.js +156 -102
  28. package/dist/esm/logic/admin/deviceWallet.eoa.js +23 -12
  29. package/dist/esm/logic/admin/deviceWalletFactory.eoa.js +63 -42
  30. package/dist/esm/logic/admin/eSIMWallet.eoa.js +3 -5
  31. package/dist/esm/logic/admin/eSIMWalletFactory.eoa.js +76 -8
  32. package/dist/esm/logic/admin/lazyWalletRegistry.eoa.js +330 -19
  33. package/dist/esm/logic/admin/protocolAdmin.eoa.js +524 -0
  34. package/dist/esm/logic/admin/reads/deviceWallet.reads.js +82 -7
  35. package/dist/esm/logic/admin/reads/deviceWalletFactory.reads.js +82 -23
  36. package/dist/esm/logic/admin/reads/eSIMWallet.reads.js +50 -6
  37. package/dist/esm/logic/admin/reads/eSIMWalletFactory.reads.js +63 -5
  38. package/dist/esm/logic/admin/reads/lazyWalletRegistry.reads.js +171 -10
  39. package/dist/esm/logic/admin/reads/protocolAdmin.reads.js +154 -0
  40. package/dist/esm/logic/admin/reads/registry.reads.js +289 -9
  41. package/dist/esm/logic/admin/registry.eoa.js +274 -4
  42. package/dist/esm/logic/constants.js +53 -26
  43. package/dist/esm/logic/deviceWallet.js +224 -31
  44. package/dist/esm/logic/deviceWalletFactory.js +89 -0
  45. package/dist/esm/logic/eSIMWallet.js +113 -43
  46. package/dist/esm/logic/eSIMWalletFactory.js +8 -8
  47. package/dist/esm/logic/errors.js +64 -0
  48. package/dist/esm/logic/registry.js +236 -0
  49. package/dist/esm/logic/utils.js +2 -1
  50. package/dist/types/abis/BeaconProxy.d.ts +21 -33
  51. package/dist/types/abis/DeviceWallet.d.ts +372 -365
  52. package/dist/types/abis/DeviceWalletFactory.d.ts +438 -448
  53. package/dist/types/abis/ESIMWallet.d.ts +352 -245
  54. package/dist/types/abis/ESIMWalletFactory.d.ts +278 -216
  55. package/dist/types/abis/LazyWalletRegistry.d.ts +710 -317
  56. package/dist/types/abis/P256Verifier.d.ts +16 -16
  57. package/dist/types/abis/ProtocolAdmin.d.ts +947 -0
  58. package/dist/types/abis/Registry.d.ts +960 -393
  59. package/dist/types/abis/RegistryHelper.d.ts +317 -16
  60. package/dist/types/abis/index.d.ts +2 -1
  61. package/dist/types/admin/config-admin.d.ts +3 -0
  62. package/dist/types/admin/interface/deviceWalletClass.d.ts +10 -3
  63. package/dist/types/admin/interface/deviceWalletFactoryClass.d.ts +14 -7
  64. package/dist/types/admin/interface/eSIMWalletClass.d.ts +3 -0
  65. package/dist/types/admin/interface/eSIMWalletFactoryClass.d.ts +8 -1
  66. package/dist/types/admin/interface/lazyWalletRegistryClass.d.ts +38 -2
  67. package/dist/types/admin/interface/protocolAdminClass.d.ts +95 -0
  68. package/dist/types/admin/interface/registryClass.d.ts +30 -0
  69. package/dist/types/config.d.ts +26 -5
  70. package/dist/types/interface/P256VerifierClass.d.ts +3 -4
  71. package/dist/types/interface/deviceWalletClass.d.ts +22 -2569
  72. package/dist/types/interface/deviceWalletFactoryClass.d.ts +10 -2564
  73. package/dist/types/interface/eSIMWalletClass.d.ts +11 -8
  74. package/dist/types/interface/eSIMWalletFactoryClass.d.ts +5 -2565
  75. package/dist/types/interface/registryClass.d.ts +19 -0
  76. package/dist/types/interface/smartAccountClass.d.ts +6 -2568
  77. package/dist/types/logic/P256Verifier.d.ts +2 -2
  78. package/dist/types/logic/account-kit/createSmartAccount.d.ts +43 -19
  79. package/dist/types/logic/admin/deviceWallet.eoa.d.ts +15 -9
  80. package/dist/types/logic/admin/deviceWalletFactory.eoa.d.ts +34 -23
  81. package/dist/types/logic/admin/eSIMWallet.eoa.d.ts +0 -5
  82. package/dist/types/logic/admin/eSIMWalletFactory.eoa.d.ts +27 -9
  83. package/dist/types/logic/admin/lazyWalletRegistry.eoa.d.ts +102 -10
  84. package/dist/types/logic/admin/protocolAdmin.eoa.d.ts +163 -0
  85. package/dist/types/logic/admin/reads/deviceWallet.reads.d.ts +28 -8
  86. package/dist/types/logic/admin/reads/deviceWalletFactory.reads.d.ts +45 -14
  87. package/dist/types/logic/admin/reads/eSIMWallet.reads.d.ts +25 -6
  88. package/dist/types/logic/admin/reads/eSIMWalletFactory.reads.d.ts +21 -6
  89. package/dist/types/logic/admin/reads/lazyWalletRegistry.reads.d.ts +44 -11
  90. package/dist/types/logic/admin/reads/protocolAdmin.reads.d.ts +53 -0
  91. package/dist/types/logic/admin/reads/registry.reads.d.ts +105 -9
  92. package/dist/types/logic/admin/registry.eoa.d.ts +102 -5
  93. package/dist/types/logic/constants.d.ts +2 -0
  94. package/dist/types/logic/deviceWallet.d.ts +78 -6
  95. package/dist/types/logic/deviceWalletFactory.d.ts +32 -3
  96. package/dist/types/logic/eSIMWallet.d.ts +48 -7
  97. package/dist/types/logic/eSIMWalletFactory.d.ts +3 -3
  98. package/dist/types/logic/errors.d.ts +45 -0
  99. package/dist/types/logic/registry.d.ts +78 -0
  100. package/dist/types/types-export.d.ts +1 -1
  101. package/dist/types/types.d.ts +111 -1
  102. package/package.json +11 -13
  103. package/dist/esm/interface/lazyWalletRegistryClass.js +0 -10
  104. package/dist/esm/logic/lazyWalletRegistry.js +0 -19
  105. package/dist/types/interface/lazyWalletRegistryClass.d.ts +0 -6
  106. package/dist/types/logic/lazyWalletRegistry.d.ts +0 -2
@@ -1,13 +1,15 @@
1
1
  import { publicActions } from "viem";
2
2
  import { _getChainSpecificConstants } from "../../constants.js";
3
3
  import { Registry } from "../../../abis/index.js";
4
+ // Read-only admin logic for `Registry` (which inherits `RegistryHelper`, so its
5
+ // ABI carries the helper mappings too). Surfaces the public storage getters for
6
+ // the backend. Each read extends the `WalletClient` with `publicActions`; no EOA
7
+ // account is required.
4
8
  /**
5
- * Read-only admin logic for `Registry` (which inherits `RegistryHelper`, so its
6
- * ABI carries the helper mappings too). Surfaces the public storage getters for
7
- * the backend. Each read extends the `WalletClient` with `publicActions`; no EOA
8
- * account is required.
9
+ * The admin EOA (`eSIMWalletAdmin`) recorded in the registry. Reads zero while a
10
+ * nomination is pending or the admin is suspended, which means the role is
11
+ * dormant rather than unset.
9
12
  */
10
- /** The admin EOA (`eSIMWalletAdmin`) recorded in the registry. */
11
13
  export const _eSIMWalletAdmin = async (client) => {
12
14
  const chainID = await client.getChainId();
13
15
  const rpcURL = client.transport.url;
@@ -19,6 +21,68 @@ export const _eSIMWalletAdmin = async (client) => {
19
21
  args: []
20
22
  });
21
23
  };
24
+ /**
25
+ * The admin address on the books, which keeps naming a suspended admin so the
26
+ * suspension can be lifted without supplying it again. Ask `_eSIMWalletAdmin`
27
+ * who may actually act.
28
+ */
29
+ export const _adminOfRecord = async (client) => {
30
+ const chainID = await client.getChainId();
31
+ const rpcURL = client.transport.url;
32
+ const values = _getChainSpecificConstants(chainID, rpcURL);
33
+ return client.extend(publicActions).readContract({
34
+ address: values.factoryAddresses.REGISTRY,
35
+ abi: Registry,
36
+ functionName: "adminOfRecord",
37
+ args: []
38
+ });
39
+ };
40
+ /**
41
+ * Whether the admin's powers are suspended protocol-wide. True and a pending
42
+ * nomination are separate reasons for `_eSIMWalletAdmin` to read zero, so read
43
+ * this alongside `_newRequestedAdmin` to tell them apart.
44
+ */
45
+ export const _adminDisabled = async (client) => {
46
+ const chainID = await client.getChainId();
47
+ const rpcURL = client.transport.url;
48
+ const values = _getChainSpecificConstants(chainID, rpcURL);
49
+ return client.extend(publicActions).readContract({
50
+ address: values.factoryAddresses.REGISTRY,
51
+ abi: Registry,
52
+ functionName: "adminDisabled",
53
+ args: []
54
+ });
55
+ };
56
+ /**
57
+ * Whether the protocol is paused. While true, every ETH-moving path on the
58
+ * device wallets and eSIM wallets reverts `ProtocolPaused`.
59
+ */
60
+ export const _paused = async (client) => {
61
+ const chainID = await client.getChainId();
62
+ const rpcURL = client.transport.url;
63
+ const values = _getChainSpecificConstants(chainID, rpcURL);
64
+ return client.extend(publicActions).readContract({
65
+ address: values.factoryAddresses.REGISTRY,
66
+ abi: Registry,
67
+ functionName: "paused",
68
+ args: []
69
+ });
70
+ };
71
+ /**
72
+ * The fallback price ceiling in wei, applied to any eSIM wallet holding no cap of
73
+ * its own. Never zero.
74
+ */
75
+ export const _defaultDataBundlePriceCap = async (client) => {
76
+ const chainID = await client.getChainId();
77
+ const rpcURL = client.transport.url;
78
+ const values = _getChainSpecificConstants(chainID, rpcURL);
79
+ return client.extend(publicActions).readContract({
80
+ address: values.factoryAddresses.REGISTRY,
81
+ abi: Registry,
82
+ functionName: "defaultDataBundlePriceCap",
83
+ args: []
84
+ });
85
+ };
22
86
  /** The vault EOA recorded in the registry. */
23
87
  export const _vault = async (client) => {
24
88
  const chainID = await client.getChainId();
@@ -31,6 +95,34 @@ export const _vault = async (client) => {
31
95
  args: []
32
96
  });
33
97
  };
98
+ /** The pending admin nominated via `requestAdminUpdate` (zero address if none). */
99
+ export const _newRequestedAdmin = async (client) => {
100
+ const chainID = await client.getChainId();
101
+ const rpcURL = client.transport.url;
102
+ const values = _getChainSpecificConstants(chainID, rpcURL);
103
+ return client.extend(publicActions).readContract({
104
+ address: values.factoryAddresses.REGISTRY,
105
+ abi: Registry,
106
+ functionName: "newRequestedAdmin",
107
+ args: []
108
+ });
109
+ };
110
+ /**
111
+ * Who holds `onlyOwner` on the registry. On the live deployment this is the
112
+ * `ProtocolAdmin` timelock, so an owner call sent from an EOA reverts and has to
113
+ * be scheduled instead.
114
+ */
115
+ export const _owner = async (client) => {
116
+ const chainID = await client.getChainId();
117
+ const rpcURL = client.transport.url;
118
+ const values = _getChainSpecificConstants(chainID, rpcURL);
119
+ return client.extend(publicActions).readContract({
120
+ address: values.factoryAddresses.REGISTRY,
121
+ abi: Registry,
122
+ functionName: "owner",
123
+ args: []
124
+ });
125
+ };
34
126
  /** The upgrade-manager (owner) EOA recorded in the registry. */
35
127
  export const _upgradeManager = async (client) => {
36
128
  const chainID = await client.getChainId();
@@ -43,6 +135,74 @@ export const _upgradeManager = async (client) => {
43
135
  args: []
44
136
  });
45
137
  };
138
+ /**
139
+ * The address a `transferOwnership` is waiting on. Worth reading before
140
+ * `protocolAdmin.acceptOwnershipBatch`, which reverts on any target that has not
141
+ * been offered to the timelock.
142
+ */
143
+ export const _pendingOwner = async (client) => {
144
+ const chainID = await client.getChainId();
145
+ const rpcURL = client.transport.url;
146
+ const values = _getChainSpecificConstants(chainID, rpcURL);
147
+ return client.extend(publicActions).readContract({
148
+ address: values.factoryAddresses.REGISTRY,
149
+ abi: Registry,
150
+ functionName: "pendingOwner",
151
+ args: []
152
+ });
153
+ };
154
+ /** The `DeviceWalletFactory` address wired into the registry. */
155
+ export const _deviceWalletFactory = async (client) => {
156
+ const chainID = await client.getChainId();
157
+ const rpcURL = client.transport.url;
158
+ const values = _getChainSpecificConstants(chainID, rpcURL);
159
+ return client.extend(publicActions).readContract({
160
+ address: values.factoryAddresses.REGISTRY,
161
+ abi: Registry,
162
+ functionName: "deviceWalletFactory",
163
+ args: []
164
+ });
165
+ };
166
+ /** The `ESIMWalletFactory` address wired into the registry. */
167
+ export const _eSIMWalletFactory = async (client) => {
168
+ const chainID = await client.getChainId();
169
+ const rpcURL = client.transport.url;
170
+ const values = _getChainSpecificConstants(chainID, rpcURL);
171
+ return client.extend(publicActions).readContract({
172
+ address: values.factoryAddresses.REGISTRY,
173
+ abi: Registry,
174
+ functionName: "eSIMWalletFactory",
175
+ args: []
176
+ });
177
+ };
178
+ /** The EntryPoint the registry recognises. One per chain. */
179
+ export const _entryPoint = async (client) => {
180
+ const chainID = await client.getChainId();
181
+ const rpcURL = client.transport.url;
182
+ const values = _getChainSpecificConstants(chainID, rpcURL);
183
+ return client.extend(publicActions).readContract({
184
+ address: values.factoryAddresses.REGISTRY,
185
+ abi: Registry,
186
+ functionName: "entryPoint",
187
+ args: []
188
+ });
189
+ };
190
+ /**
191
+ * The same pause check the wallets themselves run, which throws rather than
192
+ * returning false. Use `_paused` to branch on it; use this when you want the
193
+ * failure to carry the protocol's own revert reason.
194
+ */
195
+ export const _requireNotPaused = async (client) => {
196
+ const chainID = await client.getChainId();
197
+ const rpcURL = client.transport.url;
198
+ const values = _getChainSpecificConstants(chainID, rpcURL);
199
+ await client.extend(publicActions).readContract({
200
+ address: values.factoryAddresses.REGISTRY,
201
+ abi: Registry,
202
+ functionName: "requireNotPaused",
203
+ args: []
204
+ });
205
+ };
46
206
  /** The `LazyWalletRegistry` address wired into the registry. */
47
207
  export const _lazyWalletRegistry = async (client) => {
48
208
  const chainID = await client.getChainId();
@@ -107,9 +267,13 @@ export const _isDeviceWalletValid = async (client, deviceWallet) => {
107
267
  });
108
268
  };
109
269
  /**
110
- * The device wallet that owns an eSIM wallet (zero address if the eSIM is not
111
- * valid). On-chain this getter is named `isESIMWalletValid` but returns the
112
- * associated device-wallet address, not a boolean.
270
+ * The device wallet an eSIM wallet is registered against, zero if the protocol
271
+ * never deployed it. Despite the name this returns an address, not a boolean.
272
+ *
273
+ * This is a registration record, not a current holder. Once it goes non-zero it
274
+ * stays non-zero for the rest of the wallet's life, and mid-transfer it still
275
+ * names the device wallet that last held it. To ask who holds it now, read
276
+ * `DeviceWallet.isValidESIMWallet` on the device wallet.
113
277
  */
114
278
  export const _isESIMWalletValid = async (client, eSIMWallet) => {
115
279
  const chainID = await client.getChainId();
@@ -122,7 +286,15 @@ export const _isESIMWalletValid = async (client, eSIMWallet) => {
122
286
  args: [eSIMWallet]
123
287
  });
124
288
  };
125
- /** Whether an eSIM wallet is currently on standby. */
289
+ /**
290
+ * Whether a transfer is outstanding on an eSIM wallet. `bindESIMWallet` clears
291
+ * it once the new device wallet takes the eSIM wallet on.
292
+ *
293
+ * Independent of `isESIMWalletValid`, and neither implies the other. A `true`
294
+ * here is not a claim the wallet left the protocol, and it is normal for the
295
+ * association to still name the device wallet that raised the flag. Do not use
296
+ * this to decide whether an eSIM wallet belongs to the protocol.
297
+ */
126
298
  export const _isESIMWalletOnStandby = async (client, eSIMWallet) => {
127
299
  const chainID = await client.getChainId();
128
300
  const rpcURL = client.transport.url;
@@ -134,3 +306,111 @@ export const _isESIMWalletOnStandby = async (client, eSIMWallet) => {
134
306
  args: [eSIMWallet]
135
307
  });
136
308
  };
309
+ /**
310
+ * Whether a device identifier already has a wallet on chain. Not the same
311
+ * question as `lazyWalletRegistry.isDeviceIdentifierReserved`, which reads true
312
+ * as soon as history is recorded and well before anything is deployed.
313
+ */
314
+ export const _isDeviceIdentifierAlreadyUsed = async (client, deviceUniqueIdentifier) => {
315
+ const chainID = await client.getChainId();
316
+ const rpcURL = client.transport.url;
317
+ const values = _getChainSpecificConstants(chainID, rpcURL);
318
+ return client.extend(publicActions).readContract({
319
+ address: values.factoryAddresses.REGISTRY,
320
+ abi: Registry,
321
+ functionName: "isDeviceIdentifierAlreadyUsed",
322
+ args: [deviceUniqueIdentifier]
323
+ });
324
+ };
325
+ /** Whether an eSIM identifier is already held by a wallet. */
326
+ export const _isESIMIdentifierClaimed = async (client, eSIMUniqueIdentifier) => {
327
+ const chainID = await client.getChainId();
328
+ const rpcURL = client.transport.url;
329
+ const values = _getChainSpecificConstants(chainID, rpcURL);
330
+ return client.extend(publicActions).readContract({
331
+ address: values.factoryAddresses.REGISTRY,
332
+ abi: Registry,
333
+ functionName: "isESIMIdentifierClaimed",
334
+ args: [eSIMUniqueIdentifier]
335
+ });
336
+ };
337
+ /**
338
+ * The one eSIM wallet holding an eSIM identifier, zero if nobody holds it. Set
339
+ * once and never cleared, an ownership transfer included, because the eSIM
340
+ * belongs to the wallet rather than to whichever device is holding it.
341
+ */
342
+ export const _eSIMWalletForIdentifier = async (client, eSIMUniqueIdentifier) => {
343
+ const chainID = await client.getChainId();
344
+ const rpcURL = client.transport.url;
345
+ const values = _getChainSpecificConstants(chainID, rpcURL);
346
+ return client.extend(publicActions).readContract({
347
+ address: values.factoryAddresses.REGISTRY,
348
+ abi: Registry,
349
+ functionName: "eSIMWalletForIdentifier",
350
+ args: [eSIMUniqueIdentifier]
351
+ });
352
+ };
353
+ /**
354
+ * The same answer as `_eSIMWalletForIdentifier`, keyed by the keccak256 of the
355
+ * identifier. Use it when the hash is what you already have; otherwise take the
356
+ * string version and skip the hashing.
357
+ */
358
+ export const _claimedESIMIdentifiers = async (client, hashOfESIMIdentifier) => {
359
+ const chainID = await client.getChainId();
360
+ const rpcURL = client.transport.url;
361
+ const values = _getChainSpecificConstants(chainID, rpcURL);
362
+ return client.extend(publicActions).readContract({
363
+ address: values.factoryAddresses.REGISTRY,
364
+ abi: Registry,
365
+ functionName: "claimedESIMIdentifiers",
366
+ args: [hashOfESIMIdentifier]
367
+ });
368
+ };
369
+ /**
370
+ * The check `DeviceWalletFactory` runs before taking a device identifier.
371
+ * Resolves if the identifier is free, reverts `DeviceIdentifierReservedForLazyWallet`
372
+ * if a fiat user's eSIMs are already waiting on it.
373
+ *
374
+ * Worth calling ahead of a deployment: taking a reserved identifier strands the
375
+ * lazy user, since the history copy, the wallet deployment and the device switch
376
+ * all then refuse it.
377
+ */
378
+ export const _requireDeviceIdentifierNotReserved = async (client, deviceUniqueIdentifier) => {
379
+ const chainID = await client.getChainId();
380
+ const rpcURL = client.transport.url;
381
+ const values = _getChainSpecificConstants(chainID, rpcURL);
382
+ await client.extend(publicActions).readContract({
383
+ address: values.factoryAddresses.REGISTRY,
384
+ abi: Registry,
385
+ functionName: "requireDeviceIdentifierNotReserved",
386
+ args: [deviceUniqueIdentifier]
387
+ });
388
+ };
389
+ /**
390
+ * The ERC-1822 storage slot this proxy keeps its implementation in. An upgrade
391
+ * reverts unless the incoming implementation answers with the same value, which
392
+ * is what stops a non-UUPS address being installed.
393
+ */
394
+ export const _proxiableUUID = async (client) => {
395
+ const chainID = await client.getChainId();
396
+ const rpcURL = client.transport.url;
397
+ const values = _getChainSpecificConstants(chainID, rpcURL);
398
+ return client.extend(publicActions).readContract({
399
+ address: values.factoryAddresses.REGISTRY,
400
+ abi: Registry,
401
+ functionName: "proxiableUUID",
402
+ args: []
403
+ });
404
+ };
405
+ /** The OpenZeppelin upgrade interface this proxy speaks, currently `"5.0.0"`. */
406
+ export const _upgradeInterfaceVersion = async (client) => {
407
+ const chainID = await client.getChainId();
408
+ const rpcURL = client.transport.url;
409
+ const values = _getChainSpecificConstants(chainID, rpcURL);
410
+ return client.extend(publicActions).readContract({
411
+ address: values.factoryAddresses.REGISTRY,
412
+ abi: Registry,
413
+ functionName: "UPGRADE_INTERFACE_VERSION",
414
+ args: []
415
+ });
416
+ };
@@ -1,10 +1,14 @@
1
1
  import { _getChainSpecificConstants } from "../constants.js";
2
2
  import { MissingEOAWalletError } from "../errors.js";
3
3
  import { Registry } from "../../abis/index.js";
4
- /**
5
- * Admin-EOA logic for `Registry`. `addOrUpdateLazyWalletRegistryAddress` is
6
- * `onlyOwner`, so the `client` must carry the `upgradeManager` EOA.
7
- */
4
+ // Admin-EOA logic for `Registry`. Most of this is `onlyOwner`, so the `client`
5
+ // must carry the owner EOA. `_acceptAdminUpdate` is the nominee's own call and
6
+ // `_assignESIMIdentifier` is `onlyESIMWalletAdmin`.
7
+ //
8
+ // On the live deployment the owner is the `ProtocolAdmin` timelock, not an EOA,
9
+ // so an `onlyOwner` call sent directly reverts. Route those through
10
+ // `protocolAdmin.proposer.schedule` instead. The direct path stays for
11
+ // deployments whose owner is a plain EOA or multisig.
8
12
  /** Wire (or rewire) the LazyWalletRegistry into the Registry. `onlyOwner`. */
9
13
  export const _addOrUpdateLazyWalletRegistryAddress = async (client, lazyWalletRegistry) => {
10
14
  const chainID = await client.getChainId();
@@ -21,3 +25,269 @@ export const _addOrUpdateLazyWalletRegistryAddress = async (client, lazyWalletRe
21
25
  args: [lazyWalletRegistry]
22
26
  });
23
27
  };
28
+ /** Update the vault that receives eSIM payments. `onlyOwner`. */
29
+ export const _updateVaultAddress = async (client, newVaultAddress) => {
30
+ const chainID = await client.getChainId();
31
+ const rpcURL = client.transport.url;
32
+ const values = _getChainSpecificConstants(chainID, rpcURL);
33
+ if (!client.account)
34
+ throw new MissingEOAWalletError();
35
+ return client.writeContract({
36
+ address: values.factoryAddresses.REGISTRY,
37
+ chain: values.chain,
38
+ account: client.account.address,
39
+ abi: Registry,
40
+ functionName: 'updateVaultAddress',
41
+ args: [newVaultAddress]
42
+ });
43
+ };
44
+ /**
45
+ * Step 1 of the 2-step admin handover: nominate the next admin. `onlyOwner`, so
46
+ * the `client` is the owner EOA, not the outgoing admin.
47
+ *
48
+ * The nomination takes the role off the incumbent straight away: `eSIMWalletAdmin`
49
+ * reads zero until the nominee accepts, and every admin gated call across the
50
+ * protocol reverts in that window. Send the two steps close together. Naming the
51
+ * incumbent instead withdraws a pending nomination and hands the role back.
52
+ */
53
+ export const _requestAdminUpdate = async (client, newAdmin) => {
54
+ const chainID = await client.getChainId();
55
+ const rpcURL = client.transport.url;
56
+ const values = _getChainSpecificConstants(chainID, rpcURL);
57
+ if (!client.account)
58
+ throw new MissingEOAWalletError();
59
+ return client.writeContract({
60
+ address: values.factoryAddresses.REGISTRY,
61
+ chain: values.chain,
62
+ account: client.account.address,
63
+ abi: Registry,
64
+ functionName: 'requestAdminUpdate',
65
+ args: [newAdmin]
66
+ });
67
+ };
68
+ /**
69
+ * Suspend the admin's powers protocol-wide. `onlyOwner`.
70
+ *
71
+ * The address stays on the books as `adminOfRecord`, so lifting the suspension
72
+ * does not need it supplied again. Reverts if the admin is already suspended,
73
+ * rather than passing quietly and leaving the caller believing it acted.
74
+ */
75
+ export const _disableAdmin = async (client) => {
76
+ const chainID = await client.getChainId();
77
+ const rpcURL = client.transport.url;
78
+ const values = _getChainSpecificConstants(chainID, rpcURL);
79
+ if (!client.account)
80
+ throw new MissingEOAWalletError();
81
+ return client.writeContract({
82
+ address: values.factoryAddresses.REGISTRY,
83
+ chain: values.chain,
84
+ account: client.account.address,
85
+ abi: Registry,
86
+ functionName: 'disableAdmin',
87
+ args: []
88
+ });
89
+ };
90
+ /**
91
+ * Give the suspended admin its powers back. `onlyOwner`.
92
+ *
93
+ * Does nothing about an outstanding nomination, which keeps the incumbent
94
+ * powerless on its own. Withdraw that with `_requestAdminUpdate` naming the
95
+ * incumbent. Reverts if the admin was never suspended.
96
+ */
97
+ export const _enableAdmin = async (client) => {
98
+ const chainID = await client.getChainId();
99
+ const rpcURL = client.transport.url;
100
+ const values = _getChainSpecificConstants(chainID, rpcURL);
101
+ if (!client.account)
102
+ throw new MissingEOAWalletError();
103
+ return client.writeContract({
104
+ address: values.factoryAddresses.REGISTRY,
105
+ chain: values.chain,
106
+ account: client.account.address,
107
+ abi: Registry,
108
+ functionName: 'enableAdmin',
109
+ args: []
110
+ });
111
+ };
112
+ /**
113
+ * Stop the ETH-moving paths on every device wallet and eSIM wallet.
114
+ * `onlyESIMWalletAdmin`, so this is the one emergency lever the backend key can
115
+ * pull on its own.
116
+ *
117
+ * It cannot release it again: `_unpause` is `onlyOwner`. That split is what stops
118
+ * a compromised backend key holding user funds. Owners can still spend their own
119
+ * ETH through their device wallet's `execute`, which a pause never reaches.
120
+ */
121
+ export const _pause = async (client) => {
122
+ const chainID = await client.getChainId();
123
+ const rpcURL = client.transport.url;
124
+ const values = _getChainSpecificConstants(chainID, rpcURL);
125
+ if (!client.account)
126
+ throw new MissingEOAWalletError();
127
+ return client.writeContract({
128
+ address: values.factoryAddresses.REGISTRY,
129
+ chain: values.chain,
130
+ account: client.account.address,
131
+ abi: Registry,
132
+ functionName: 'pause',
133
+ args: []
134
+ });
135
+ };
136
+ /**
137
+ * Release the pause. `onlyOwner`, not the admin, see `_pause`.
138
+ *
139
+ * On the live deployment the owner is the timelock, so this reverts from an EOA.
140
+ * Schedule `protocolAdmin.unpauseCall` instead, or have a guardian call
141
+ * `unpauseInstantly` if the wait is not acceptable.
142
+ */
143
+ export const _unpause = async (client) => {
144
+ const chainID = await client.getChainId();
145
+ const rpcURL = client.transport.url;
146
+ const values = _getChainSpecificConstants(chainID, rpcURL);
147
+ if (!client.account)
148
+ throw new MissingEOAWalletError();
149
+ return client.writeContract({
150
+ address: values.factoryAddresses.REGISTRY,
151
+ chain: values.chain,
152
+ account: client.account.address,
153
+ abi: Registry,
154
+ functionName: 'unpause',
155
+ args: []
156
+ });
157
+ };
158
+ /**
159
+ * Set the price ceiling every eSIM wallet falls back to when it holds none of its
160
+ * own. `onlyOwner`, deliberately not the admin: the admin names the price on
161
+ * `buyDataBundle`, so it must not also be able to raise its own limit.
162
+ *
163
+ * Zero reverts `ZeroDataBundlePriceCap`, since a zero would read as "no ceiling"
164
+ * for every wallet without one of its own.
165
+ */
166
+ export const _setDefaultDataBundlePriceCap = async (client, cap) => {
167
+ const chainID = await client.getChainId();
168
+ const rpcURL = client.transport.url;
169
+ const values = _getChainSpecificConstants(chainID, rpcURL);
170
+ if (!client.account)
171
+ throw new MissingEOAWalletError();
172
+ return client.writeContract({
173
+ address: values.factoryAddresses.REGISTRY,
174
+ chain: values.chain,
175
+ account: client.account.address,
176
+ abi: Registry,
177
+ functionName: 'setDefaultDataBundlePriceCap',
178
+ args: [cap]
179
+ });
180
+ };
181
+ /**
182
+ * Bind an eSIM's unique identifier to its wallet. `onlyESIMWalletAdmin`.
183
+ *
184
+ * The identifier is claimed protocol-wide, so a string already bound to another
185
+ * wallet reverts rather than moving.
186
+ */
187
+ export const _assignESIMIdentifier = async (client, eSIMWalletAddress, eSIMUniqueIdentifier) => {
188
+ const chainID = await client.getChainId();
189
+ const rpcURL = client.transport.url;
190
+ const values = _getChainSpecificConstants(chainID, rpcURL);
191
+ if (!client.account)
192
+ throw new MissingEOAWalletError();
193
+ return client.writeContract({
194
+ address: values.factoryAddresses.REGISTRY,
195
+ chain: values.chain,
196
+ account: client.account.address,
197
+ abi: Registry,
198
+ functionName: 'assignESIMIdentifier',
199
+ args: [eSIMWalletAddress, eSIMUniqueIdentifier]
200
+ });
201
+ };
202
+ /**
203
+ * Step 2 of the 2-step admin handover: the nominee accepts. The chain requires
204
+ * `msg.sender` to equal the pending admin, so the `client` here must be the
205
+ * newly nominated admin EOA.
206
+ */
207
+ export const _acceptAdminUpdate = async (client) => {
208
+ const chainID = await client.getChainId();
209
+ const rpcURL = client.transport.url;
210
+ const values = _getChainSpecificConstants(chainID, rpcURL);
211
+ if (!client.account)
212
+ throw new MissingEOAWalletError();
213
+ return client.writeContract({
214
+ address: values.factoryAddresses.REGISTRY,
215
+ chain: values.chain,
216
+ account: client.account.address,
217
+ abi: Registry,
218
+ functionName: 'acceptAdminUpdate',
219
+ args: []
220
+ });
221
+ };
222
+ /**
223
+ * Take ownership after a `transferOwnership` named this client. The chain
224
+ * requires `msg.sender` to equal `pendingOwner`, so the `client` is the incoming
225
+ * owner, not the outgoing one.
226
+ *
227
+ * Permissionless in the sense that it needs no role, so where the incoming owner
228
+ * is the timelock this is not the call to use: `protocolAdmin.acceptOwnershipBatch`
229
+ * accepts for every contract at once and needs no delay.
230
+ */
231
+ export const _acceptOwnership = async (client) => {
232
+ const chainID = await client.getChainId();
233
+ const rpcURL = client.transport.url;
234
+ const values = _getChainSpecificConstants(chainID, rpcURL);
235
+ if (!client.account)
236
+ throw new MissingEOAWalletError();
237
+ return client.writeContract({
238
+ address: values.factoryAddresses.REGISTRY,
239
+ chain: values.chain,
240
+ account: client.account.address,
241
+ abi: Registry,
242
+ functionName: 'acceptOwnership',
243
+ args: []
244
+ });
245
+ };
246
+ // ---------------------------------------------------------------------------
247
+ // Owner payloads - only reachable through schedule
248
+ // ---------------------------------------------------------------------------
249
+ // Both are `onlyOwner`, and on the live deployment the owner is the timelock, so
250
+ // they exist as something to schedule rather than to send. Each returns the
251
+ // `OwnerCall` to hand to `protocolAdmin.schedule`.
252
+ /**
253
+ * Offer ownership to a new address. Pass the result to `schedule`.
254
+ *
255
+ * Ownable2Step, so the offer alone changes nothing: the named address has to
256
+ * call `acceptOwnership` before it holds anything. Until then the current owner
257
+ * keeps every power. Naming an address that cannot call back leaves the
258
+ * ownership where it is rather than stranding it.
259
+ */
260
+ export const _transferOwnershipCall = async (client, newOwner) => {
261
+ const chainID = await client.getChainId();
262
+ const rpcURL = client.transport.url;
263
+ const values = _getChainSpecificConstants(chainID, rpcURL);
264
+ return {
265
+ address: values.factoryAddresses.REGISTRY,
266
+ abi: Registry,
267
+ functionName: 'transferOwnership',
268
+ args: [newOwner],
269
+ };
270
+ };
271
+ /**
272
+ * Point the proxy at a new implementation. Builds `upgradeToAndCall`. Pass the
273
+ * result to `schedule`.
274
+ *
275
+ * `data` runs on the proxy straight after the swap, in the same transaction, and
276
+ * is where a `reinitializer` goes. Leave it empty when the new implementation
277
+ * needs no setup.
278
+ *
279
+ * There is no undo. The implementation is checked for a matching `proxiableUUID`
280
+ * and nothing else, so an address that answers correctly but cannot upgrade
281
+ * again ends the proxy's life. Diff the storage layout before scheduling.
282
+ */
283
+ export const _upgradeCall = async (client, newImplementation, data = '0x') => {
284
+ const chainID = await client.getChainId();
285
+ const rpcURL = client.transport.url;
286
+ const values = _getChainSpecificConstants(chainID, rpcURL);
287
+ return {
288
+ address: values.factoryAddresses.REGISTRY,
289
+ abi: Registry,
290
+ functionName: 'upgradeToAndCall',
291
+ args: [newImplementation, data],
292
+ };
293
+ };