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.
- package/README.md +21 -7
- package/dist/esm/abis/DeviceWallet.js +60 -59
- package/dist/esm/abis/ESIMWallet.js +263 -58
- package/dist/esm/abis/ESIMWalletFactory.js +2 -2
- package/dist/esm/abis/LazyWalletRegistry.js +68 -24
- package/dist/esm/abis/PaymentAdapter.js +861 -0
- package/dist/esm/abis/Registry.js +294 -18
- package/dist/esm/abis/RegistryHelper.js +101 -9
- package/dist/esm/abis/index.js +2 -1
- package/dist/esm/admin/config-admin.js +4 -0
- package/dist/esm/admin/interface/deviceWalletClass.js +3 -3
- package/dist/esm/admin/interface/eSIMWalletClass.js +6 -6
- package/dist/esm/admin/interface/lazyWalletRegistryClass.js +4 -1
- package/dist/esm/admin/interface/paymentAdapterClass.js +54 -0
- package/dist/esm/admin/interface/protocolAdminClass.js +6 -3
- package/dist/esm/admin/interface/registryClass.js +15 -6
- package/dist/esm/config.js +3 -0
- package/dist/esm/interface/deviceWalletClass.js +5 -5
- package/dist/esm/interface/eSIMWalletClass.js +10 -7
- package/dist/esm/interface/paymentAdapterClass.js +28 -0
- package/dist/esm/interface/registryClass.js +12 -3
- package/dist/esm/logic/admin/eSIMWallet.eoa.js +12 -10
- package/dist/esm/logic/admin/paymentAdapter.eoa.js +123 -0
- package/dist/esm/logic/admin/protocolAdmin.eoa.js +21 -3
- package/dist/esm/logic/admin/reads/deviceWallet.reads.js +2 -2
- package/dist/esm/logic/admin/reads/eSIMWallet.reads.js +8 -7
- package/dist/esm/logic/admin/reads/lazyWalletRegistry.reads.js +19 -2
- package/dist/esm/logic/admin/reads/paymentAdapter.reads.js +106 -0
- package/dist/esm/logic/admin/reads/registry.reads.js +34 -6
- package/dist/esm/logic/admin/registry.eoa.js +32 -8
- package/dist/esm/logic/constants.js +17 -87
- package/dist/esm/logic/deviceWallet.js +11 -11
- package/dist/esm/logic/eSIMWallet.js +51 -22
- package/dist/esm/logic/paymentAdapter.js +116 -0
- package/dist/esm/logic/registry.js +50 -5
- package/dist/esm/types-export.js +1 -1
- package/dist/esm/types.js +11 -1
- package/dist/types/abis/DeviceWallet.d.ts +50 -49
- package/dist/types/abis/ESIMWallet.d.ts +215 -55
- package/dist/types/abis/ESIMWalletFactory.d.ts +2 -2
- package/dist/types/abis/LazyWalletRegistry.d.ts +58 -24
- package/dist/types/abis/PaymentAdapter.d.ts +661 -0
- package/dist/types/abis/Registry.d.ts +233 -18
- package/dist/types/abis/RegistryHelper.d.ts +83 -9
- package/dist/types/abis/index.d.ts +2 -1
- package/dist/types/admin/config-admin.d.ts +2 -0
- package/dist/types/admin/interface/deviceWalletClass.d.ts +1 -1
- package/dist/types/admin/interface/eSIMWalletClass.d.ts +3 -3
- package/dist/types/admin/interface/lazyWalletRegistryClass.d.ts +1 -0
- package/dist/types/admin/interface/paymentAdapterClass.d.ts +20 -0
- package/dist/types/admin/interface/protocolAdminClass.d.ts +2 -1
- package/dist/types/admin/interface/registryClass.d.ts +6 -2
- package/dist/types/config.d.ts +2 -0
- package/dist/types/interface/constantsClass.d.ts +2 -2
- package/dist/types/interface/deviceWalletClass.d.ts +2 -2
- package/dist/types/interface/eSIMWalletClass.d.ts +5 -4
- package/dist/types/interface/paymentAdapterClass.d.ts +13 -0
- package/dist/types/interface/registryClass.d.ts +5 -2
- package/dist/types/logic/admin/eSIMWallet.eoa.d.ts +9 -6
- package/dist/types/logic/admin/paymentAdapter.eoa.d.ts +28 -0
- package/dist/types/logic/admin/protocolAdmin.eoa.d.ts +12 -2
- package/dist/types/logic/admin/reads/deviceWallet.reads.d.ts +1 -1
- package/dist/types/logic/admin/reads/eSIMWallet.reads.d.ts +5 -4
- package/dist/types/logic/admin/reads/lazyWalletRegistry.reads.d.ts +7 -0
- package/dist/types/logic/admin/reads/paymentAdapter.reads.d.ts +30 -0
- package/dist/types/logic/admin/reads/registry.reads.d.ts +13 -5
- package/dist/types/logic/admin/registry.eoa.d.ts +18 -8
- package/dist/types/logic/constants.d.ts +5 -17
- package/dist/types/logic/deviceWallet.d.ts +7 -7
- package/dist/types/logic/eSIMWallet.d.ts +29 -15
- package/dist/types/logic/paymentAdapter.d.ts +40 -0
- package/dist/types/logic/registry.d.ts +20 -5
- package/dist/types/types-export.d.ts +2 -1
- package/dist/types/types.d.ts +20 -2
- 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,
|
|
58
|
-
* device wallets and eSIM wallets
|
|
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
|
|
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
|
|
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: "
|
|
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
|
|
114
|
-
* `onlyESIMWalletAdmin`, so this is the one emergency lever the backend
|
|
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
|
|
119
|
-
*
|
|
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
|
-
* `
|
|
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
|
|
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: '
|
|
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 {
|
|
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
|
-
//
|
|
33
|
-
//
|
|
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: '
|
|
87
|
-
ESIM_WALLET_FACTORY: '
|
|
88
|
-
LAZY_WALLET_REGISTRY: '
|
|
89
|
-
REGISTRY: '
|
|
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: '
|
|
93
|
-
PROTOCOL_ADMIN: '
|
|
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.
|
|
127
|
-
//
|
|
128
|
-
//
|
|
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
|
|
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
|
|
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: "
|
|
46
|
-
args: [eSIMWalletAddress,
|
|
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
|
|
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. `
|
|
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
|
|
200
|
-
* grants it, so this stays false until `
|
|
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
|
|
202
|
+
export const _canPullFunds = async (client, address, eSIMWalletAddress) => {
|
|
203
203
|
return client.readContract({
|
|
204
204
|
address,
|
|
205
205
|
abi: DeviceWallet,
|
|
206
|
-
functionName: "
|
|
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 {
|
|
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
|
|
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 `
|
|
20
|
-
* the ceiling on that price too. A handover clears the cap, and the new
|
|
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
|
|
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: "
|
|
33
|
+
functionName: "setPriceCapUSDCents",
|
|
34
34
|
args: [cap]
|
|
35
35
|
})
|
|
36
36
|
}]
|
|
37
37
|
});
|
|
38
38
|
};
|
|
39
|
-
|
|
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: "
|
|
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
|
|
66
|
-
* Read it before naming a price on `
|
|
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 `
|
|
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
|
|
109
|
+
export const _priceCapUSDCents = async (client, address) => {
|
|
81
110
|
const walletCap = await client.readContract({
|
|
82
111
|
address,
|
|
83
112
|
abi: ESIMWallet,
|
|
84
|
-
functionName: "
|
|
113
|
+
functionName: "priceCapUSDCents",
|
|
85
114
|
args: []
|
|
86
115
|
});
|
|
87
116
|
if (walletCap !== 0n)
|
|
88
117
|
return walletCap;
|
|
89
|
-
const registryCap = await
|
|
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 `
|
|
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 [
|
|
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 {
|
|
149
|
+
return { id, priceUSDCents, settlement };
|
|
121
150
|
};
|
|
122
151
|
export const _requestTransferOwnership = async (client, address, newOwner) => {
|
|
123
152
|
if (!client.account)
|