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.
- 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/deviceWallet.eoa.js +3 -3
- package/dist/esm/logic/admin/deviceWalletFactory.eoa.js +6 -6
- package/dist/esm/logic/admin/eSIMWallet.eoa.js +14 -12
- package/dist/esm/logic/admin/eSIMWalletFactory.eoa.js +4 -4
- package/dist/esm/logic/admin/lazyWalletRegistry.eoa.js +7 -7
- package/dist/esm/logic/admin/paymentAdapter.eoa.js +123 -0
- package/dist/esm/logic/admin/protocolAdmin.eoa.js +34 -16
- 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 +44 -20
- 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/errors.js +30 -1
- 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/errors.d.ts +16 -1
- 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
|
@@ -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)
|
package/dist/esm/logic/errors.js
CHANGED
|
@@ -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,
|
|
91
|
-
* wallets and eSIM wallets
|
|
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
|
|
209
|
-
export const
|
|
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: "
|
|
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
|
package/dist/esm/types-export.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { Settlement } from './types';
|
package/dist/esm/types.js
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
|
|
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 = {}));
|