kokio-sdk 2.1.0 → 3.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 (75) hide show
  1. package/README.md +21 -7
  2. package/dist/esm/abis/DeviceWallet.js +60 -59
  3. package/dist/esm/abis/ESIMWallet.js +263 -58
  4. package/dist/esm/abis/ESIMWalletFactory.js +2 -2
  5. package/dist/esm/abis/LazyWalletRegistry.js +68 -24
  6. package/dist/esm/abis/PaymentAdapter.js +861 -0
  7. package/dist/esm/abis/Registry.js +294 -18
  8. package/dist/esm/abis/RegistryHelper.js +101 -9
  9. package/dist/esm/abis/index.js +2 -1
  10. package/dist/esm/admin/config-admin.js +4 -0
  11. package/dist/esm/admin/interface/deviceWalletClass.js +3 -3
  12. package/dist/esm/admin/interface/eSIMWalletClass.js +6 -6
  13. package/dist/esm/admin/interface/lazyWalletRegistryClass.js +4 -1
  14. package/dist/esm/admin/interface/paymentAdapterClass.js +54 -0
  15. package/dist/esm/admin/interface/protocolAdminClass.js +6 -3
  16. package/dist/esm/admin/interface/registryClass.js +15 -6
  17. package/dist/esm/config.js +3 -0
  18. package/dist/esm/interface/deviceWalletClass.js +5 -5
  19. package/dist/esm/interface/eSIMWalletClass.js +10 -7
  20. package/dist/esm/interface/paymentAdapterClass.js +28 -0
  21. package/dist/esm/interface/registryClass.js +12 -3
  22. package/dist/esm/logic/admin/eSIMWallet.eoa.js +12 -10
  23. package/dist/esm/logic/admin/paymentAdapter.eoa.js +123 -0
  24. package/dist/esm/logic/admin/protocolAdmin.eoa.js +21 -3
  25. package/dist/esm/logic/admin/reads/deviceWallet.reads.js +2 -2
  26. package/dist/esm/logic/admin/reads/eSIMWallet.reads.js +8 -7
  27. package/dist/esm/logic/admin/reads/lazyWalletRegistry.reads.js +19 -2
  28. package/dist/esm/logic/admin/reads/paymentAdapter.reads.js +106 -0
  29. package/dist/esm/logic/admin/reads/registry.reads.js +34 -6
  30. package/dist/esm/logic/admin/registry.eoa.js +32 -8
  31. package/dist/esm/logic/constants.js +17 -87
  32. package/dist/esm/logic/deviceWallet.js +11 -11
  33. package/dist/esm/logic/eSIMWallet.js +51 -22
  34. package/dist/esm/logic/paymentAdapter.js +116 -0
  35. package/dist/esm/logic/registry.js +50 -5
  36. package/dist/esm/types-export.js +1 -1
  37. package/dist/esm/types.js +11 -1
  38. package/dist/types/abis/DeviceWallet.d.ts +50 -49
  39. package/dist/types/abis/ESIMWallet.d.ts +215 -55
  40. package/dist/types/abis/ESIMWalletFactory.d.ts +2 -2
  41. package/dist/types/abis/LazyWalletRegistry.d.ts +58 -24
  42. package/dist/types/abis/PaymentAdapter.d.ts +661 -0
  43. package/dist/types/abis/Registry.d.ts +233 -18
  44. package/dist/types/abis/RegistryHelper.d.ts +83 -9
  45. package/dist/types/abis/index.d.ts +2 -1
  46. package/dist/types/admin/config-admin.d.ts +2 -0
  47. package/dist/types/admin/interface/deviceWalletClass.d.ts +1 -1
  48. package/dist/types/admin/interface/eSIMWalletClass.d.ts +3 -3
  49. package/dist/types/admin/interface/lazyWalletRegistryClass.d.ts +1 -0
  50. package/dist/types/admin/interface/paymentAdapterClass.d.ts +20 -0
  51. package/dist/types/admin/interface/protocolAdminClass.d.ts +2 -1
  52. package/dist/types/admin/interface/registryClass.d.ts +6 -2
  53. package/dist/types/config.d.ts +2 -0
  54. package/dist/types/interface/constantsClass.d.ts +2 -2
  55. package/dist/types/interface/deviceWalletClass.d.ts +2 -2
  56. package/dist/types/interface/eSIMWalletClass.d.ts +5 -4
  57. package/dist/types/interface/paymentAdapterClass.d.ts +13 -0
  58. package/dist/types/interface/registryClass.d.ts +5 -2
  59. package/dist/types/logic/admin/eSIMWallet.eoa.d.ts +9 -6
  60. package/dist/types/logic/admin/paymentAdapter.eoa.d.ts +28 -0
  61. package/dist/types/logic/admin/protocolAdmin.eoa.d.ts +12 -2
  62. package/dist/types/logic/admin/reads/deviceWallet.reads.d.ts +1 -1
  63. package/dist/types/logic/admin/reads/eSIMWallet.reads.d.ts +5 -4
  64. package/dist/types/logic/admin/reads/lazyWalletRegistry.reads.d.ts +7 -0
  65. package/dist/types/logic/admin/reads/paymentAdapter.reads.d.ts +30 -0
  66. package/dist/types/logic/admin/reads/registry.reads.d.ts +13 -5
  67. package/dist/types/logic/admin/registry.eoa.d.ts +18 -8
  68. package/dist/types/logic/constants.d.ts +5 -17
  69. package/dist/types/logic/deviceWallet.d.ts +7 -7
  70. package/dist/types/logic/eSIMWallet.d.ts +29 -15
  71. package/dist/types/logic/paymentAdapter.d.ts +40 -0
  72. package/dist/types/logic/registry.d.ts +20 -5
  73. package/dist/types/types-export.d.ts +2 -1
  74. package/dist/types/types.d.ts +20 -2
  75. package/package.json +1 -1
@@ -0,0 +1,106 @@
1
+ import { publicActions } from "viem";
2
+ import { _getChainSpecificConstants } from "../../constants.js";
3
+ import { PaymentAdapter } from "../../../abis/index.js";
4
+ // Read-only admin logic for `PaymentAdapter`. Every write on it
5
+ // (registerAsset/updateAsset) is `onlyOwner`, i.e. only reachable through the
6
+ // timelock, so those live as payload builders on `KokioAdmin.protocolAdmin`
7
+ // rather than here.
8
+ /** The registry this adapter reads `vault()` and `isESIMWalletValid()` from. */
9
+ export const _registry = async (client) => {
10
+ const chainID = await client.getChainId();
11
+ const rpcURL = client.transport.url;
12
+ const values = _getChainSpecificConstants(chainID, rpcURL);
13
+ return client.extend(publicActions).readContract({
14
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
15
+ abi: PaymentAdapter,
16
+ functionName: "registry",
17
+ args: []
18
+ });
19
+ };
20
+ /** The ERC-20 registered under the `USDC` symbol at configure time. */
21
+ export const _settlementToken = async (client) => {
22
+ const chainID = await client.getChainId();
23
+ const rpcURL = client.transport.url;
24
+ const values = _getChainSpecificConstants(chainID, rpcURL);
25
+ return client.extend(publicActions).readContract({
26
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
27
+ abi: PaymentAdapter,
28
+ functionName: "settlementToken",
29
+ args: []
30
+ });
31
+ };
32
+ /**
33
+ * The raw currency table entry for a symbol. `decimals` reads zero for a symbol
34
+ * never registered, which is how `resolveAsset` tells "not registered" apart
35
+ * from "registered but withdrawn" (`allowed: false`).
36
+ */
37
+ export const _assets = async (client, symbol) => {
38
+ const chainID = await client.getChainId();
39
+ const rpcURL = client.transport.url;
40
+ const values = _getChainSpecificConstants(chainID, rpcURL);
41
+ const [allowed, isDollarUnit, decimals, token] = await client.extend(publicActions).readContract({
42
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
43
+ abi: PaymentAdapter,
44
+ functionName: "assets",
45
+ args: [symbol]
46
+ });
47
+ return { allowed, isDollarUnit, decimals, token };
48
+ };
49
+ /** A currency's full entry, reverting if the symbol was never registered. */
50
+ export const _resolveAsset = async (client, symbol) => {
51
+ const chainID = await client.getChainId();
52
+ const rpcURL = client.transport.url;
53
+ const values = _getChainSpecificConstants(chainID, rpcURL);
54
+ return client.extend(publicActions).readContract({
55
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
56
+ abi: PaymentAdapter,
57
+ functionName: "resolveAsset",
58
+ args: [symbol]
59
+ });
60
+ };
61
+ /**
62
+ * The amount of `symbol`, in its smallest unit, that a `priceUSDCents` charge
63
+ * currently costs. Worth reading before `recordSettledPurchase`, to size
64
+ * `_tokenAmount` for the backend's own settlement record.
65
+ */
66
+ export const _quote = async (client, symbol, priceUSDCents) => {
67
+ const chainID = await client.getChainId();
68
+ const rpcURL = client.transport.url;
69
+ const values = _getChainSpecificConstants(chainID, rpcURL);
70
+ return client.extend(publicActions).readContract({
71
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
72
+ abi: PaymentAdapter,
73
+ functionName: "quote",
74
+ args: [symbol, priceUSDCents]
75
+ });
76
+ };
77
+ /**
78
+ * Whether a payment reference has already been spent here. Retired from the
79
+ * registry's live purchase paths (`PaymentAdapter.consumePaymentReference` is
80
+ * `onlyRegistry` but nothing calls it any more): replay protection now lives on
81
+ * `Registry.usedPaymentReferences`, scoped per eSIM wallet. Kept for whatever
82
+ * still reads the adapter's own record from before that move.
83
+ */
84
+ export const _usedReferences = async (client, paymentReference) => {
85
+ const chainID = await client.getChainId();
86
+ const rpcURL = client.transport.url;
87
+ const values = _getChainSpecificConstants(chainID, rpcURL);
88
+ return client.extend(publicActions).readContract({
89
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
90
+ abi: PaymentAdapter,
91
+ functionName: "usedReferences",
92
+ args: [paymentReference]
93
+ });
94
+ };
95
+ /** The address holding upgrade authority over this adapter (its owner). */
96
+ export const _upgradeManager = async (client) => {
97
+ const chainID = await client.getChainId();
98
+ const rpcURL = client.transport.url;
99
+ const values = _getChainSpecificConstants(chainID, rpcURL);
100
+ return client.extend(publicActions).readContract({
101
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
102
+ abi: PaymentAdapter,
103
+ functionName: "upgradeManager",
104
+ args: []
105
+ });
106
+ };
@@ -54,8 +54,8 @@ export const _adminDisabled = async (client) => {
54
54
  });
55
55
  };
56
56
  /**
57
- * Whether the protocol is paused. While true, every ETH-moving path on the
58
- * device wallets and eSIM wallets reverts `ProtocolPaused`.
57
+ * Whether the protocol is paused. While true, the purchase and token-pull
58
+ * paths on the device wallets and eSIM wallets revert `ProtocolPaused`.
59
59
  */
60
60
  export const _paused = async (client) => {
61
61
  const chainID = await client.getChainId();
@@ -69,17 +69,17 @@ export const _paused = async (client) => {
69
69
  });
70
70
  };
71
71
  /**
72
- * The fallback price ceiling in wei, applied to any eSIM wallet holding no cap of
73
- * its own. Never zero.
72
+ * The fallback price ceiling in USD cents, applied to any eSIM wallet holding no
73
+ * cap of its own. Never zero.
74
74
  */
75
- export const _defaultDataBundlePriceCap = async (client) => {
75
+ export const _defaultPriceCapUSDCents = async (client) => {
76
76
  const chainID = await client.getChainId();
77
77
  const rpcURL = client.transport.url;
78
78
  const values = _getChainSpecificConstants(chainID, rpcURL);
79
79
  return client.extend(publicActions).readContract({
80
80
  address: values.factoryAddresses.REGISTRY,
81
81
  abi: Registry,
82
- functionName: "defaultDataBundlePriceCap",
82
+ functionName: "defaultPriceCapUSDCents",
83
83
  args: []
84
84
  });
85
85
  };
@@ -414,3 +414,31 @@ export const _upgradeInterfaceVersion = async (client) => {
414
414
  args: []
415
415
  });
416
416
  };
417
+ /** The payment adapter this registry currently points at. */
418
+ export const _paymentAdapter = async (client) => {
419
+ const chainID = await client.getChainId();
420
+ const rpcURL = client.transport.url;
421
+ const values = _getChainSpecificConstants(chainID, rpcURL);
422
+ return client.extend(publicActions).readContract({
423
+ address: values.factoryAddresses.REGISTRY,
424
+ abi: Registry,
425
+ functionName: "paymentAdapter",
426
+ args: []
427
+ });
428
+ };
429
+ /**
430
+ * Whether a payment reference has already been spent for an eSIM wallet.
431
+ * Scoped per wallet: pass the same `keccak256(abi.encode(eSIMWallet, paymentReference))`
432
+ * the contract keys `usedPaymentReferences` by, not the bare reference.
433
+ */
434
+ export const _usedPaymentReferences = async (client, scopedReference) => {
435
+ const chainID = await client.getChainId();
436
+ const rpcURL = client.transport.url;
437
+ const values = _getChainSpecificConstants(chainID, rpcURL);
438
+ return client.extend(publicActions).readContract({
439
+ address: values.factoryAddresses.REGISTRY,
440
+ abi: Registry,
441
+ functionName: "usedPaymentReferences",
442
+ args: [scopedReference]
443
+ });
444
+ };
@@ -110,13 +110,13 @@ export const _enableAdmin = async (client) => {
110
110
  });
111
111
  };
112
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.
113
+ * Stop the purchase and token-pull paths on every device wallet and eSIM
114
+ * wallet. `onlyESIMWalletAdmin`, so this is the one emergency lever the backend
115
+ * key can pull on its own.
116
116
  *
117
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.
118
+ * a compromised backend key holding user funds. Owners can still move their own
119
+ * funds through their device wallet's `execute`, which a pause never reaches.
120
120
  */
121
121
  export const _pause = async (client) => {
122
122
  const chainID = await client.getChainId();
@@ -158,12 +158,12 @@ export const _unpause = async (client) => {
158
158
  /**
159
159
  * Set the price ceiling every eSIM wallet falls back to when it holds none of its
160
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.
161
+ * `buyDataBundleWithToken`, so it must not also be able to raise its own limit.
162
162
  *
163
163
  * Zero reverts `ZeroDataBundlePriceCap`, since a zero would read as "no ceiling"
164
164
  * for every wallet without one of its own.
165
165
  */
166
- export const _setDefaultDataBundlePriceCap = async (client, cap) => {
166
+ export const _setDefaultPriceCapUSDCents = async (client, cap) => {
167
167
  const chainID = await client.getChainId();
168
168
  const rpcURL = client.transport.url;
169
169
  const values = _getChainSpecificConstants(chainID, rpcURL);
@@ -174,7 +174,7 @@ export const _setDefaultDataBundlePriceCap = async (client, cap) => {
174
174
  chain: values.chain,
175
175
  account: client.account.address,
176
176
  abi: Registry,
177
- functionName: 'setDefaultDataBundlePriceCap',
177
+ functionName: 'setDefaultPriceCapUSDCents',
178
178
  args: [cap]
179
179
  });
180
180
  };
@@ -199,6 +199,30 @@ export const _assignESIMIdentifier = async (client, eSIMWalletAddress, eSIMUniqu
199
199
  args: [eSIMWalletAddress, eSIMUniqueIdentifier]
200
200
  });
201
201
  };
202
+ /**
203
+ * Record a data bundle paid for outside the protocol - a card or an external
204
+ * wallet, never the device wallet. `onlyESIMWalletAdmin`. No money moves here:
205
+ * `_dataBundleDetail.settlement` must be `ExternalWallet` or `Fiat` (the
206
+ * contract reverts `SettlementNotAsserted` on `DeviceWallet`, since this call
207
+ * never sees a transfer to prove it), and `_tokenAmount` is recorded for
208
+ * offchain matching but never checked against `_dataBundleDetail.priceUSDCents`.
209
+ * `_paymentReference` is spendable once per eSIM wallet.
210
+ */
211
+ export const _recordSettledPurchase = async (client, eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference) => {
212
+ const chainID = await client.getChainId();
213
+ const rpcURL = client.transport.url;
214
+ const values = _getChainSpecificConstants(chainID, rpcURL);
215
+ if (!client.account)
216
+ throw new MissingEOAWalletError();
217
+ return writeContractOrThrow(client, {
218
+ address: values.factoryAddresses.REGISTRY,
219
+ chain: values.chain,
220
+ account: client.account.address,
221
+ abi: Registry,
222
+ functionName: 'recordSettledPurchase',
223
+ args: [eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference]
224
+ });
225
+ };
202
226
  /**
203
227
  * Step 2 of the 2-step admin handover: the nominee accepts. The chain requires
204
228
  * `msg.sender` to equal the pending admin, so the `client` here must be the
@@ -1,5 +1,5 @@
1
1
  import { InvalidClientError, UnconfiguredChainError, UnsupportedChainError, } from './errors.js';
2
- import { mainnet, sepolia, optimism, optimismSepolia, arbitrum, arbitrumSepolia, base, baseSepolia } from "viem/chains";
2
+ import { base, baseSepolia } from "viem/chains";
3
3
  export const ZERO = BigInt('0');
4
4
  export const SIGNATURE_VALIDITY_SECONDS = 180; // 3 minutes validity
5
5
  // Gas estimation runs before a passkey signature exists, so the account hands the
@@ -18,60 +18,15 @@ export const STUB_VERIFICATION_GAS_PAD = BigInt(60_000);
18
18
  // preVerificationGas is charged whether or not it is used, so this stays close to
19
19
  // the measured shortfall.
20
20
  export const STUB_PRE_VERIFICATION_GAS_PAD = BigInt(15_000);
21
+ // Only the two chains the protocol actually targets. A chain gets an entry here
22
+ // once a deployment exists for it, not before.
21
23
  export var CHAIN_ID;
22
24
  (function (CHAIN_ID) {
23
- CHAIN_ID[CHAIN_ID["MAINNET"] = 1] = "MAINNET";
24
- CHAIN_ID[CHAIN_ID["SEPOLIA"] = 11155111] = "SEPOLIA";
25
- CHAIN_ID[CHAIN_ID["OPTIMISM_MAINNET"] = 10] = "OPTIMISM_MAINNET";
26
- CHAIN_ID[CHAIN_ID["OPTIMISM_SEPOLIA"] = 11155420] = "OPTIMISM_SEPOLIA";
27
25
  CHAIN_ID[CHAIN_ID["BASE_MAINNET"] = 8453] = "BASE_MAINNET";
28
26
  CHAIN_ID[CHAIN_ID["BASE_SEPOLIA"] = 84532] = "BASE_SEPOLIA";
29
- CHAIN_ID[CHAIN_ID["ARBITRUM_ONE"] = 42161] = "ARBITRUM_ONE";
30
- CHAIN_ID[CHAIN_ID["ARBITRUM_SEPOLIA"] = 421614] = "ARBITRUM_SEPOLIA";
31
27
  })(CHAIN_ID || (CHAIN_ID = {}));
32
- // Cleared: the deployment here targets EntryPoint v0.7, which this SDK no longer
33
- // speaks. Refill only after a redeploy on v0.8.
34
- export const sepoliaFactoryAddresses = {
35
- DEVICE_WALLET_FACTORY: '0x',
36
- ESIM_WALLET_FACTORY: '0x',
37
- LAZY_WALLET_REGISTRY: '0x',
38
- REGISTRY: '0x',
39
- ENTRY_POINT: '0x',
40
- SENDER_CREATOR: '0x',
41
- P256VERIFIER: '0x',
42
- PROTOCOL_ADMIN: '0x'
43
- };
44
- export const mainnetFactoryAddresses = {
45
- DEVICE_WALLET_FACTORY: '0x',
46
- ESIM_WALLET_FACTORY: '0x',
47
- LAZY_WALLET_REGISTRY: '0x',
48
- REGISTRY: '0x',
49
- ENTRY_POINT: '0x',
50
- SENDER_CREATOR: '0x',
51
- P256VERIFIER: '0x',
52
- PROTOCOL_ADMIN: '0x'
53
- };
54
- export const optimismMainnetFactoryAddresses = {
55
- DEVICE_WALLET_FACTORY: '0x',
56
- ESIM_WALLET_FACTORY: '0x',
57
- LAZY_WALLET_REGISTRY: '0x',
58
- REGISTRY: '0x',
59
- ENTRY_POINT: '0x',
60
- SENDER_CREATOR: '0x',
61
- P256VERIFIER: '0x',
62
- PROTOCOL_ADMIN: '0x'
63
- };
64
- // Cleared for the same reason as Sepolia: a v0.7 deployment this SDK cannot use.
65
- export const optimismSepoliaFactoryAddresses = {
66
- DEVICE_WALLET_FACTORY: '0x',
67
- ESIM_WALLET_FACTORY: '0x',
68
- LAZY_WALLET_REGISTRY: '0x',
69
- REGISTRY: '0x',
70
- ENTRY_POINT: '0x',
71
- SENDER_CREATOR: '0x',
72
- P256VERIFIER: '0x',
73
- PROTOCOL_ADMIN: '0x'
74
- };
28
+ // Not yet deployed. Kept as a placeholder so mainnet has a documented shape to
29
+ // fill in once the protocol ships there, following Base Sepolia's soak.
75
30
  export const baseMainnetFactoryAddresses = {
76
31
  DEVICE_WALLET_FACTORY: '0x',
77
32
  ESIM_WALLET_FACTORY: '0x',
@@ -80,37 +35,19 @@ export const baseMainnetFactoryAddresses = {
80
35
  ENTRY_POINT: '0x',
81
36
  SENDER_CREATOR: '0x',
82
37
  P256VERIFIER: '0x',
83
- PROTOCOL_ADMIN: '0x'
38
+ PROTOCOL_ADMIN: '0x',
39
+ PAYMENT_ADAPTER: '0x'
84
40
  };
85
41
  export const baseSepoliaFactoryAddresses = {
86
- DEVICE_WALLET_FACTORY: '0xB006c7066C89a5d7Bfc229e9fb0bADf96c8F979f',
87
- ESIM_WALLET_FACTORY: '0x13998C0bb7433c51cE5101922B12EE69F459699A',
88
- LAZY_WALLET_REGISTRY: '0x394177c5cc4762b897c37de1820259B75993e033',
89
- REGISTRY: '0x89e386E3251692F21a2E9048A46518AdC2A5Cb4A',
42
+ DEVICE_WALLET_FACTORY: '0x0BB3BA8D9233514a4aA6D72c243a2473f9cFf0bb',
43
+ ESIM_WALLET_FACTORY: '0x57da54e07705de17c713ec311ac193e83470D5a5',
44
+ LAZY_WALLET_REGISTRY: '0x5bE46Cf216186Bc2E3C220729331D6bE7d186e84',
45
+ REGISTRY: '0x916b6b554119c789EF3026EDeB0E1Ba741b42A49',
90
46
  ENTRY_POINT: '0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108',
91
47
  SENDER_CREATOR: '0x449ED7C3e6Fee6a97311d4b55475DF59C44AdD33',
92
- P256VERIFIER: '0x625561429bD99d647956ccBCA4eBf762aaA142c5',
93
- PROTOCOL_ADMIN: '0x77A1D6f27462c34BF038832d9Cff6b3E94a9Fe6F'
94
- };
95
- export const arbitrumOneFactoryAddresses = {
96
- DEVICE_WALLET_FACTORY: '0x',
97
- ESIM_WALLET_FACTORY: '0x',
98
- LAZY_WALLET_REGISTRY: '0x',
99
- REGISTRY: '0x',
100
- ENTRY_POINT: '0x',
101
- SENDER_CREATOR: '0x',
102
- P256VERIFIER: '0x',
103
- PROTOCOL_ADMIN: '0x'
104
- };
105
- export const arbitrumSepoliaFactoryAddresses = {
106
- DEVICE_WALLET_FACTORY: '0x',
107
- ESIM_WALLET_FACTORY: '0x',
108
- LAZY_WALLET_REGISTRY: '0x',
109
- REGISTRY: '0x',
110
- ENTRY_POINT: '0x',
111
- SENDER_CREATOR: '0x',
112
- P256VERIFIER: '0x',
113
- PROTOCOL_ADMIN: '0x'
48
+ P256VERIFIER: '0x6FA3E7E145476Dc4682734Fd845019A3872b4821',
49
+ PROTOCOL_ADMIN: '0xdDeCC2C1345BC966337B5f4Fe57EC2D5bfad751A',
50
+ PAYMENT_ADAPTER: '0xBFaA666a8074924588E96507c307b680ecCeB2c1'
114
51
  };
115
52
  export const customErrors = {
116
53
  NULL_OR_UNDEFINED_VALUE: "Error: Null or undefined value provided",
@@ -123,19 +60,12 @@ export const _extractChainID = async (client) => {
123
60
  }
124
61
  return client.getChainId();
125
62
  };
126
- // Maps each supported chain id to its factory-address book + viem chain. Chains
127
- // whose addresses are still '0x' placeholders are intentionally listed so the
128
- // guard below can reject them with a clear message rather than leaking '0x'
129
- // into viem calls.
63
+ // Maps each supported chain id to its factory-address book + viem chain. Base
64
+ // Mainnet is listed with '0x' placeholders so the guard below rejects it with a
65
+ // clear message rather than leaking '0x' into viem calls.
130
66
  const CHAIN_CONFIG = {
131
- [CHAIN_ID.SEPOLIA]: { factoryAddresses: sepoliaFactoryAddresses, chain: sepolia },
132
- [CHAIN_ID.MAINNET]: { factoryAddresses: mainnetFactoryAddresses, chain: mainnet },
133
- [CHAIN_ID.OPTIMISM_MAINNET]: { factoryAddresses: optimismMainnetFactoryAddresses, chain: optimism },
134
- [CHAIN_ID.OPTIMISM_SEPOLIA]: { factoryAddresses: optimismSepoliaFactoryAddresses, chain: optimismSepolia },
135
67
  [CHAIN_ID.BASE_MAINNET]: { factoryAddresses: baseMainnetFactoryAddresses, chain: base },
136
68
  [CHAIN_ID.BASE_SEPOLIA]: { factoryAddresses: baseSepoliaFactoryAddresses, chain: baseSepolia },
137
- [CHAIN_ID.ARBITRUM_ONE]: { factoryAddresses: arbitrumOneFactoryAddresses, chain: arbitrum },
138
- [CHAIN_ID.ARBITRUM_SEPOLIA]: { factoryAddresses: arbitrumSepoliaFactoryAddresses, chain: arbitrumSepolia },
139
69
  };
140
70
  // A factory address book is only usable if every entry is a real 20-byte
141
71
  // address - an unconfigured chain leaves '0x' placeholders behind.
@@ -32,18 +32,18 @@ export const _sendUserOperation = async (client, calls) => {
32
32
  calls
33
33
  });
34
34
  };
35
- export const _toggleAccessToETH = async (client, address, eSIMWalletAddress, hasAccessToETH) => {
35
+ export const _toggleAccessToFunds = async (client, address, eSIMWalletAddress, hasAccessToFunds) => {
36
36
  if (!client.account)
37
37
  throw new MissingSmartWalletError();
38
- // UserOp - `onlySelf`; the device wallet toggles ETH access for an eSIM wallet it owns.
38
+ // UserOp - `onlySelf`; the device wallet toggles fund access for an eSIM wallet it owns.
39
39
  return client.sendUserOperation({
40
40
  account: client.account,
41
41
  calls: [{
42
42
  to: address,
43
43
  data: encodeFunctionData({
44
44
  abi: DeviceWallet,
45
- functionName: "toggleAccessToETH",
46
- args: [eSIMWalletAddress, hasAccessToETH]
45
+ functionName: "toggleAccessToFunds",
46
+ args: [eSIMWalletAddress, hasAccessToFunds]
47
47
  })
48
48
  }]
49
49
  });
@@ -51,10 +51,10 @@ export const _toggleAccessToETH = async (client, address, eSIMWalletAddress, has
51
51
  /**
52
52
  * Bind an eSIM wallet this device wallet already owns.
53
53
  *
54
- * A bind never carries ETH access: the contract reverts on a `true` rather than
54
+ * A bind never carries fund access: the contract reverts on a `true` rather than
55
55
  * downgrading it quietly, so the SDK passes `false` and there is nothing to
56
- * choose. `toggleAccessToETH` is the only way to grant it, which is what stops a
57
- * bind from undoing the owner's revocation.
56
+ * choose. `toggleAccessToFunds` is the only way to grant it, which is what stops
57
+ * a bind from undoing the owner's revocation.
58
58
  */
59
59
  export const _addESIMWallet = async (client, address, eSIMWalletAddress) => {
60
60
  if (!client.account)
@@ -196,14 +196,14 @@ export const _isValidESIMWallet = async (client, address, eSIMWalletAddress) =>
196
196
  });
197
197
  };
198
198
  /**
199
- * Whether an eSIM wallet may pull ETH from this one. Binding a wallet never
200
- * grants it, so this stays false until `toggleAccessToETH` says otherwise.
199
+ * Whether an eSIM wallet may pull funds from this one. Binding a wallet never
200
+ * grants it, so this stays false until `toggleAccessToFunds` says otherwise.
201
201
  */
202
- export const _canPullETH = async (client, address, eSIMWalletAddress) => {
202
+ export const _canPullFunds = async (client, address, eSIMWalletAddress) => {
203
203
  return client.readContract({
204
204
  address,
205
205
  abi: DeviceWallet,
206
- functionName: "canPullETH",
206
+ functionName: "canPullFunds",
207
207
  args: [eSIMWalletAddress]
208
208
  });
209
209
  };
@@ -1,7 +1,7 @@
1
1
  import { encodeFunctionData, maxUint256 } from "viem";
2
2
  import { MissingSmartWalletError } from "./errors.js";
3
3
  import { ESIMWallet } from "../abis/index.js";
4
- import { _defaultDataBundlePriceCap } from "./registry.js";
4
+ import { _defaultPriceCapUSDCents } from "./registry.js";
5
5
  // Not exposed on this surface:
6
6
  // - populateHistory and setESIMUniqueIdentifier are `onlyRegistry` - callable
7
7
  // only by the registry contract. Naming an eSIM goes through
@@ -12,15 +12,15 @@ import { _defaultDataBundlePriceCap } from "./registry.js";
12
12
  // The functions below are `onlyDeviceWallet` (the eSIM wallet's owner IS the device
13
13
  // wallet) or otherwise satisfiable by the device-wallet userOp sender, so they succeed.
14
14
  /**
15
- * Cap what this eSIM wallet may be charged for one data bundle. Zero hands it
16
- * back to the registry's ceiling.
15
+ * Cap what this eSIM wallet may be charged for one data bundle, in USD cents.
16
+ * Zero hands it back to the registry's ceiling.
17
17
  *
18
18
  * `onlyDeviceWallet`, so it needs the user's own signature. That is the point:
19
- * the admin names the price on `buyDataBundle`, so it must not be able to raise
20
- * the ceiling on that price too. A handover clears the cap, and the new owner has
21
- * to set it again.
19
+ * the admin names the price on `buyDataBundleWithToken`, so it must not be able
20
+ * to raise the ceiling on that price too. A handover clears the cap, and the new
21
+ * owner has to set it again.
22
22
  */
23
- export const _setDataBundlePriceCap = async (client, address, cap) => {
23
+ export const _setPriceCapUSDCents = async (client, address, cap) => {
24
24
  if (!client.account)
25
25
  throw new MissingSmartWalletError();
26
26
  // UserOp - `onlyDeviceWallet`.
@@ -30,13 +30,22 @@ export const _setDataBundlePriceCap = async (client, address, cap) => {
30
30
  to: address,
31
31
  data: encodeFunctionData({
32
32
  abi: ESIMWallet,
33
- functionName: "setDataBundlePriceCap",
33
+ functionName: "setPriceCapUSDCents",
34
34
  args: [cap]
35
35
  })
36
36
  }]
37
37
  });
38
38
  };
39
- export const _buyDataBundle = async (client, address, dataBundleDetails) => {
39
+ /**
40
+ * Buy a data bundle in `asset`, an ERC-20 the payment adapter accepts.
41
+ *
42
+ * `_maxAmountIn` is the most of `asset` the buyer will spend, in its smallest
43
+ * unit - read it from `paymentAdapter.quote(asset, dataBundleDetails.priceUSDCents)`
44
+ * first. `_paymentReference` ties this purchase to its offchain order and is
45
+ * spendable once per eSIM wallet; the caller supplies it rather than the SDK
46
+ * inventing one.
47
+ */
48
+ export const _buyDataBundleWithToken = async (client, address, dataBundleDetails, asset, maxAmountIn, paymentReference) => {
40
49
  if (!client.account)
41
50
  throw new MissingSmartWalletError();
42
51
  // UserOp - `onlyDeviceWalletOrESIMWalletAdmin`; the owning device wallet may buy.
@@ -46,8 +55,28 @@ export const _buyDataBundle = async (client, address, dataBundleDetails) => {
46
55
  to: address,
47
56
  data: encodeFunctionData({
48
57
  abi: ESIMWallet,
49
- functionName: "buyDataBundle",
50
- args: [dataBundleDetails]
58
+ functionName: "buyDataBundleWithToken",
59
+ args: [dataBundleDetails, asset, maxAmountIn, paymentReference]
60
+ })
61
+ }]
62
+ });
63
+ };
64
+ /**
65
+ * Send an ERC-20 held by this eSIM wallet back to its owning device wallet.
66
+ * `onlyDeviceWallet`.
67
+ */
68
+ export const _sendTokenToDeviceWallet = async (client, address, token, amount) => {
69
+ if (!client.account)
70
+ throw new MissingSmartWalletError();
71
+ // UserOp - `onlyDeviceWallet`.
72
+ return client.sendUserOperation({
73
+ account: client.account,
74
+ calls: [{
75
+ to: address,
76
+ data: encodeFunctionData({
77
+ abi: ESIMWallet,
78
+ functionName: "sendTokenToDeviceWallet",
79
+ args: [token, amount]
51
80
  })
52
81
  }]
53
82
  });
@@ -62,9 +91,9 @@ export const _owner = async (client, address) => {
62
91
  });
63
92
  };
64
93
  /**
65
- * The ceiling that actually applies to this wallet's next purchase, in wei.
66
- * Read it before naming a price on `buyDataBundle`, since a price above the
67
- * ceiling reverts.
94
+ * The ceiling that actually applies to this wallet's next purchase, in USD
95
+ * cents. Read it before naming a price on `buyDataBundleWithToken`, since a
96
+ * price above the ceiling reverts.
68
97
  *
69
98
  * Resolved the way the contract resolves it. The wallet's own cap wins when it
70
99
  * has one. Zero there means "follow the registry", which is where a fresh wallet
@@ -73,20 +102,20 @@ export const _owner = async (client, address) => {
73
102
  * not spend anything".
74
103
  *
75
104
  * That last case cannot happen on a live deployment: the registry refuses a zero
76
- * cap in both `initialize` and `setDefaultDataBundlePriceCap`. It is handled
105
+ * cap in both `initialize` and `setDefaultPriceCapUSDCents`. It is handled
77
106
  * because the contract's own check treats zero as unlimited, not because the
78
107
  * state is reachable.
79
108
  */
80
- export const _dataBundlePriceCap = async (client, address) => {
109
+ export const _priceCapUSDCents = async (client, address) => {
81
110
  const walletCap = await client.readContract({
82
111
  address,
83
112
  abi: ESIMWallet,
84
- functionName: "dataBundlePriceCap",
113
+ functionName: "priceCapUSDCents",
85
114
  args: []
86
115
  });
87
116
  if (walletCap !== 0n)
88
117
  return walletCap;
89
- const registryCap = await _defaultDataBundlePriceCap(client);
118
+ const registryCap = await _defaultPriceCapUSDCents(client);
90
119
  return registryCap === 0n ? maxUint256 : registryCap;
91
120
  };
92
121
  /**
@@ -107,17 +136,17 @@ export const _deviceWallet = async (client, address) => {
107
136
  * pre-deployment purchases the lazy registry copies in.
108
137
  *
109
138
  * There is no length getter on the contract. Read upwards from zero until a call
110
- * reverts, or track the count from the `DataBundleBought` and
111
- * `TransactionHistoryPopulated` events.
139
+ * reverts, or track the count from the `DataBundleBoughtWithToken`,
140
+ * `DataBundleSettlementRecorded` and `TransactionHistoryPopulated` events.
112
141
  */
113
142
  export const _transactionHistory = async (client, address, index) => {
114
- const [dataBundleID, dataBundlePrice] = await client.readContract({
143
+ const [id, priceUSDCents, settlement] = await client.readContract({
115
144
  address,
116
145
  abi: ESIMWallet,
117
146
  functionName: "transactionHistory",
118
147
  args: [index]
119
148
  });
120
- return { dataBundleID, dataBundlePrice };
149
+ return { id, priceUSDCents, settlement };
121
150
  };
122
151
  export const _requestTransferOwnership = async (client, address, newOwner) => {
123
152
  if (!client.account)