kokio-sdk 2.0.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 (81) 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/deviceWallet.eoa.js +3 -3
  23. package/dist/esm/logic/admin/deviceWalletFactory.eoa.js +6 -6
  24. package/dist/esm/logic/admin/eSIMWallet.eoa.js +14 -12
  25. package/dist/esm/logic/admin/eSIMWalletFactory.eoa.js +4 -4
  26. package/dist/esm/logic/admin/lazyWalletRegistry.eoa.js +7 -7
  27. package/dist/esm/logic/admin/paymentAdapter.eoa.js +123 -0
  28. package/dist/esm/logic/admin/protocolAdmin.eoa.js +34 -16
  29. package/dist/esm/logic/admin/reads/deviceWallet.reads.js +2 -2
  30. package/dist/esm/logic/admin/reads/eSIMWallet.reads.js +8 -7
  31. package/dist/esm/logic/admin/reads/lazyWalletRegistry.reads.js +19 -2
  32. package/dist/esm/logic/admin/reads/paymentAdapter.reads.js +106 -0
  33. package/dist/esm/logic/admin/reads/registry.reads.js +34 -6
  34. package/dist/esm/logic/admin/registry.eoa.js +44 -20
  35. package/dist/esm/logic/constants.js +17 -87
  36. package/dist/esm/logic/deviceWallet.js +11 -11
  37. package/dist/esm/logic/eSIMWallet.js +51 -22
  38. package/dist/esm/logic/errors.js +30 -1
  39. package/dist/esm/logic/paymentAdapter.js +116 -0
  40. package/dist/esm/logic/registry.js +50 -5
  41. package/dist/esm/types-export.js +1 -1
  42. package/dist/esm/types.js +11 -1
  43. package/dist/types/abis/DeviceWallet.d.ts +50 -49
  44. package/dist/types/abis/ESIMWallet.d.ts +215 -55
  45. package/dist/types/abis/ESIMWalletFactory.d.ts +2 -2
  46. package/dist/types/abis/LazyWalletRegistry.d.ts +58 -24
  47. package/dist/types/abis/PaymentAdapter.d.ts +661 -0
  48. package/dist/types/abis/Registry.d.ts +233 -18
  49. package/dist/types/abis/RegistryHelper.d.ts +83 -9
  50. package/dist/types/abis/index.d.ts +2 -1
  51. package/dist/types/admin/config-admin.d.ts +2 -0
  52. package/dist/types/admin/interface/deviceWalletClass.d.ts +1 -1
  53. package/dist/types/admin/interface/eSIMWalletClass.d.ts +3 -3
  54. package/dist/types/admin/interface/lazyWalletRegistryClass.d.ts +1 -0
  55. package/dist/types/admin/interface/paymentAdapterClass.d.ts +20 -0
  56. package/dist/types/admin/interface/protocolAdminClass.d.ts +2 -1
  57. package/dist/types/admin/interface/registryClass.d.ts +6 -2
  58. package/dist/types/config.d.ts +2 -0
  59. package/dist/types/interface/constantsClass.d.ts +2 -2
  60. package/dist/types/interface/deviceWalletClass.d.ts +2 -2
  61. package/dist/types/interface/eSIMWalletClass.d.ts +5 -4
  62. package/dist/types/interface/paymentAdapterClass.d.ts +13 -0
  63. package/dist/types/interface/registryClass.d.ts +5 -2
  64. package/dist/types/logic/admin/eSIMWallet.eoa.d.ts +9 -6
  65. package/dist/types/logic/admin/paymentAdapter.eoa.d.ts +28 -0
  66. package/dist/types/logic/admin/protocolAdmin.eoa.d.ts +12 -2
  67. package/dist/types/logic/admin/reads/deviceWallet.reads.d.ts +1 -1
  68. package/dist/types/logic/admin/reads/eSIMWallet.reads.d.ts +5 -4
  69. package/dist/types/logic/admin/reads/lazyWalletRegistry.reads.d.ts +7 -0
  70. package/dist/types/logic/admin/reads/paymentAdapter.reads.d.ts +30 -0
  71. package/dist/types/logic/admin/reads/registry.reads.d.ts +13 -5
  72. package/dist/types/logic/admin/registry.eoa.d.ts +18 -8
  73. package/dist/types/logic/constants.d.ts +5 -17
  74. package/dist/types/logic/deviceWallet.d.ts +7 -7
  75. package/dist/types/logic/eSIMWallet.d.ts +29 -15
  76. package/dist/types/logic/errors.d.ts +16 -1
  77. package/dist/types/logic/paymentAdapter.d.ts +40 -0
  78. package/dist/types/logic/registry.d.ts +20 -5
  79. package/dist/types/types-export.d.ts +2 -1
  80. package/dist/types/types.d.ts +20 -2
  81. package/package.json +1 -1
@@ -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)
@@ -1,4 +1,4 @@
1
- import { decodeErrorResult, isHex } from "viem";
1
+ import { BaseError, ContractFunctionRevertedError, decodeErrorResult, isHex, } from "viem";
2
2
  import { DeviceWallet, DeviceWalletFactory, ESIMWallet, ESIMWalletFactory, LazyWalletRegistry, P256Verifier, Registry, RegistryHelper, } from "../abis/index.js";
3
3
  /**
4
4
  * Base class for every error the Kokio SDK throws deliberately. Consumers can
@@ -180,3 +180,32 @@ export class ContractRevertError extends KokioError {
180
180
  this.decoded = decoded;
181
181
  }
182
182
  }
183
+ /**
184
+ * Pulls a ContractRevertError out of an error thrown by viem, or `null` if it
185
+ * isn't a revert viem could decode a selector for.
186
+ */
187
+ export const toContractRevertError = (err) => {
188
+ if (!(err instanceof BaseError))
189
+ return null;
190
+ const revert = err.walk((e) => e instanceof ContractFunctionRevertedError);
191
+ if (!(revert instanceof ContractFunctionRevertedError) || !revert.raw)
192
+ return null;
193
+ return new ContractRevertError(revert.raw);
194
+ };
195
+ /**
196
+ * `client.writeContract`, but a recognised on-chain revert comes back as a
197
+ * ContractRevertError instead of viem's raw error chain. Anything else -
198
+ * network failures, an unrecognised revert selector - is rethrown as-is.
199
+ *
200
+ * Mirrors `WalletClient["writeContract"]`'s own generics rather than reading
201
+ * them off `Parameters<...>`, since that would collapse the per-call overload
202
+ * (e.g. a payable function's `value` field) to a single, wrong shape.
203
+ */
204
+ export const writeContractOrThrow = async (client, request) => {
205
+ try {
206
+ return await client.writeContract(request);
207
+ }
208
+ catch (err) {
209
+ throw toContractRevertError(err) ?? err;
210
+ }
211
+ };
@@ -0,0 +1,116 @@
1
+ import { _getChainSpecificConstants } from "./constants.js";
2
+ import { PaymentAdapter } from "../abis/index.js";
3
+ // Every function here is a view. The adapter's only writes reachable from a
4
+ // userOp are `settle` (`onlyESIMWallet`, called from inside
5
+ // `eSIMWallet.buyDataBundleWithToken`, never directly) and
6
+ // `consumePaymentReference` (`onlyRegistry`), so neither is exposed on this
7
+ // surface. `registerAsset`/`updateAsset` are `onlyOwner`, reachable only through
8
+ // `KokioAdmin.protocolAdmin`'s timelock payloads.
9
+ /**
10
+ * The registry this adapter reads `vault()` and `isESIMWalletValid()` from.
11
+ */
12
+ export const _registry = async (client) => {
13
+ const chainID = await client.getChainId();
14
+ const rpcURL = client.transport.url;
15
+ const values = _getChainSpecificConstants(chainID, rpcURL);
16
+ return client.readContract({
17
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
18
+ abi: PaymentAdapter,
19
+ functionName: "registry",
20
+ args: []
21
+ });
22
+ };
23
+ /** The ERC-20 registered under the `USDC` symbol at configure time. */
24
+ export const _settlementToken = async (client) => {
25
+ const chainID = await client.getChainId();
26
+ const rpcURL = client.transport.url;
27
+ const values = _getChainSpecificConstants(chainID, rpcURL);
28
+ return client.readContract({
29
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
30
+ abi: PaymentAdapter,
31
+ functionName: "settlementToken",
32
+ args: []
33
+ });
34
+ };
35
+ /**
36
+ * The raw currency table entry for a symbol. `decimals` reads zero for a symbol
37
+ * never registered, which is how `resolveAsset` tells "not registered" apart
38
+ * from "registered but withdrawn" (`allowed: false`).
39
+ */
40
+ export const _assets = async (client, symbol) => {
41
+ const chainID = await client.getChainId();
42
+ const rpcURL = client.transport.url;
43
+ const values = _getChainSpecificConstants(chainID, rpcURL);
44
+ const [allowed, isDollarUnit, decimals, token] = await client.readContract({
45
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
46
+ abi: PaymentAdapter,
47
+ functionName: "assets",
48
+ args: [symbol]
49
+ });
50
+ return { allowed, isDollarUnit, decimals, token };
51
+ };
52
+ /**
53
+ * A currency's full entry, reverting if the symbol was never registered.
54
+ * `_asset` is not required for `buyDataBundleWithToken`, but is worth reading
55
+ * first: `token` being the zero address means the currency is fiat-only and the
56
+ * purchase will revert with `AssetNotTransferable`.
57
+ */
58
+ export const _resolveAsset = async (client, symbol) => {
59
+ const chainID = await client.getChainId();
60
+ const rpcURL = client.transport.url;
61
+ const values = _getChainSpecificConstants(chainID, rpcURL);
62
+ return client.readContract({
63
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
64
+ abi: PaymentAdapter,
65
+ functionName: "resolveAsset",
66
+ args: [symbol]
67
+ });
68
+ };
69
+ /**
70
+ * The amount of `symbol`, in its smallest unit, that a `priceUSDCents` charge
71
+ * currently costs. Read this before calling `buyDataBundleWithToken`, and pass
72
+ * the result (or a value at least this large) as `_maxAmountIn`: nothing today
73
+ * moves the price between the quote and the purchase, so the two always agree,
74
+ * but the contract will not assume that on the caller's behalf.
75
+ */
76
+ export const _quote = async (client, symbol, priceUSDCents) => {
77
+ const chainID = await client.getChainId();
78
+ const rpcURL = client.transport.url;
79
+ const values = _getChainSpecificConstants(chainID, rpcURL);
80
+ return client.readContract({
81
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
82
+ abi: PaymentAdapter,
83
+ functionName: "quote",
84
+ args: [symbol, priceUSDCents]
85
+ });
86
+ };
87
+ /**
88
+ * Whether a payment reference has already been spent here. Retired from the
89
+ * registry's live purchase paths (`PaymentAdapter.consumePaymentReference` is
90
+ * `onlyRegistry` but nothing calls it any more): replay protection now lives on
91
+ * `Registry.usedPaymentReferences`, scoped per eSIM wallet. Kept for whatever
92
+ * still reads the adapter's own record from before that move.
93
+ */
94
+ export const _usedReferences = async (client, paymentReference) => {
95
+ const chainID = await client.getChainId();
96
+ const rpcURL = client.transport.url;
97
+ const values = _getChainSpecificConstants(chainID, rpcURL);
98
+ return client.readContract({
99
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
100
+ abi: PaymentAdapter,
101
+ functionName: "usedReferences",
102
+ args: [paymentReference]
103
+ });
104
+ };
105
+ /** The address holding upgrade authority over this adapter (its owner). */
106
+ export const _upgradeManager = async (client) => {
107
+ const chainID = await client.getChainId();
108
+ const rpcURL = client.transport.url;
109
+ const values = _getChainSpecificConstants(chainID, rpcURL);
110
+ return client.readContract({
111
+ address: values.factoryAddresses.PAYMENT_ADAPTER,
112
+ abi: PaymentAdapter,
113
+ functionName: "upgradeManager",
114
+ args: []
115
+ });
116
+ };
@@ -87,8 +87,9 @@ export const _isDeviceIdentifierAlreadyUsed = async (client, deviceUniqueIdentif
87
87
  });
88
88
  };
89
89
  /**
90
- * Whether the protocol is paused. While true, every ETH-moving path on the device
91
- * wallets and eSIM wallets reverts, so check this before offering a purchase.
90
+ * Whether the protocol is paused. While true, the purchase and token-pull paths
91
+ * on the device wallets and eSIM wallets revert, so check this before offering a
92
+ * purchase.
92
93
  */
93
94
  export const _paused = async (client) => {
94
95
  const chainID = await client.getChainId();
@@ -205,18 +206,62 @@ export const _eSIMWalletForIdentifier = async (client, eSIMUniqueIdentifier) =>
205
206
  args: [eSIMUniqueIdentifier]
206
207
  });
207
208
  };
208
- /** The fallback price ceiling in wei for a wallet holding no cap of its own. */
209
- export const _defaultDataBundlePriceCap = async (client) => {
209
+ /** The fallback price ceiling in USD cents for a wallet holding no cap of its own. */
210
+ export const _defaultPriceCapUSDCents = async (client) => {
210
211
  const chainID = await client.getChainId();
211
212
  const rpcURL = client.transport.url;
212
213
  const values = _getChainSpecificConstants(chainID, rpcURL);
213
214
  return client.readContract({
214
215
  address: values.factoryAddresses.REGISTRY,
215
216
  abi: Registry,
216
- functionName: "defaultDataBundlePriceCap",
217
+ functionName: "defaultPriceCapUSDCents",
217
218
  args: []
218
219
  });
219
220
  };
221
+ /** The payment adapter this registry currently points at. */
222
+ export const _paymentAdapter = async (client) => {
223
+ const chainID = await client.getChainId();
224
+ const rpcURL = client.transport.url;
225
+ const values = _getChainSpecificConstants(chainID, rpcURL);
226
+ return client.readContract({
227
+ address: values.factoryAddresses.REGISTRY,
228
+ abi: Registry,
229
+ functionName: "paymentAdapter",
230
+ args: []
231
+ });
232
+ };
233
+ /**
234
+ * Whether a payment reference has already been spent for an eSIM wallet.
235
+ * Scoped per wallet: pass the same `keccak256(abi.encode(eSIMWallet, paymentReference))`
236
+ * the contract keys `usedPaymentReferences` by, not the bare reference.
237
+ */
238
+ export const _usedPaymentReferences = async (client, scopedReference) => {
239
+ const chainID = await client.getChainId();
240
+ const rpcURL = client.transport.url;
241
+ const values = _getChainSpecificConstants(chainID, rpcURL);
242
+ return client.readContract({
243
+ address: values.factoryAddresses.REGISTRY,
244
+ abi: Registry,
245
+ functionName: "usedPaymentReferences",
246
+ args: [scopedReference]
247
+ });
248
+ };
249
+ /**
250
+ * Throws if `eSIMWallet` still has lazy-deployment history waiting to be copied
251
+ * in. `buyDataBundleWithToken` checks this itself before writing a new entry, so
252
+ * calling it first only turns that revert into a typed error ahead of a userOp.
253
+ */
254
+ export const _requireLazyHistoryCopied = async (client, eSIMWallet) => {
255
+ const chainID = await client.getChainId();
256
+ const rpcURL = client.transport.url;
257
+ const values = _getChainSpecificConstants(chainID, rpcURL);
258
+ await client.readContract({
259
+ address: values.factoryAddresses.REGISTRY,
260
+ abi: Registry,
261
+ functionName: "requireLazyHistoryCopied",
262
+ args: [eSIMWallet]
263
+ });
264
+ };
220
265
  /**
221
266
  * Throws if a fiat user's eSIMs are already waiting on this device identifier.
222
267
  * Worth calling before a deployment: taking a reserved identifier strands the
@@ -1 +1 @@
1
- export {};
1
+ export { Settlement } from './types';
package/dist/esm/types.js CHANGED
@@ -1 +1,11 @@
1
- export {};
1
+ /**
2
+ * Which contract, if any, saw the money for a data bundle move. Mirrors the
3
+ * on-chain `Settlement` enum (CustomStructs.sol) - viem decodes a Solidity enum
4
+ * as its `uint8` position, so the member order here must match exactly.
5
+ */
6
+ export var Settlement;
7
+ (function (Settlement) {
8
+ Settlement[Settlement["DeviceWallet"] = 0] = "DeviceWallet";
9
+ Settlement[Settlement["ExternalWallet"] = 1] = "ExternalWallet";
10
+ Settlement[Settlement["Fiat"] = 2] = "Fiat";
11
+ })(Settlement || (Settlement = {}));