kokio-sdk 3.2.1 → 3.3.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/dist/esm/admin/config-admin.js +6 -1
- package/dist/esm/admin/interface/utilsClass.js +27 -0
- package/dist/esm/logic/admin/utils/tokenTransfer.js +157 -0
- package/dist/esm/logic/errors.js +96 -0
- package/dist/types/admin/config-admin.d.ts +4 -1
- package/dist/types/admin/interface/utilsClass.d.ts +21 -0
- package/dist/types/logic/admin/utils/tokenTransfer.d.ts +14 -0
- package/dist/types/logic/errors.d.ts +62 -0
- package/dist/types/types-export.d.ts +1 -1
- package/dist/types/types.d.ts +35 -0
- package/package.json +1 -1
|
@@ -8,10 +8,11 @@ import { AdminDeviceWalletSubPackage } from "./interface/deviceWalletClass.js";
|
|
|
8
8
|
import { AdminESIMWalletSubPackage } from "./interface/eSIMWalletClass.js";
|
|
9
9
|
import { AdminPaymentAdapterSubPackage } from "./interface/paymentAdapterClass.js";
|
|
10
10
|
import { AdminCallsSubPackage } from "./interface/callsClass.js";
|
|
11
|
+
import { AdminUtilsSubPackage } from "./interface/utilsClass.js";
|
|
11
12
|
// Re-export the typed error surface so backend consumers can `instanceof
|
|
12
13
|
// KokioError` (or a subclass) and decode reverts without reaching into internal
|
|
13
14
|
// module paths - mirroring `config.ts`.
|
|
14
|
-
export { KokioError, NullOrUndefinedValueError, MissingSmartWalletError, MissingEOAWalletError, InvalidClientError, UnsupportedChainError, UnconfiguredChainError, CounterfactualMismatchError, BatchSizeOutOfRangeError, DepositOnResumeError, ESIMWalletNotLazyDeployedError, MissingBatchEventError, StalledBatchError, ContractRevertError, decodeContractRevert, } from "../logic/errors.js";
|
|
15
|
+
export { KokioError, NullOrUndefinedValueError, MissingSmartWalletError, MissingEOAWalletError, InvalidClientError, UnsupportedChainError, UnconfiguredChainError, CounterfactualMismatchError, BatchSizeOutOfRangeError, DepositOnResumeError, ESIMWalletNotLazyDeployedError, MissingBatchEventError, StalledBatchError, InvalidAddressError, InvalidSymbolError, TokenNotAcceptedError, NotAProtocolESIMWalletError, UnknownTransactionError, TransactionRevertedError, ReceiptNotCanonicalError, NotAnERC20TokenError, UnmatchedPaymentEventsError, PriceOutOfRangeError, ContractRevertError, decodeContractRevert, } from "../logic/errors.js";
|
|
15
16
|
// `getOperationState` answers with this, and comparing against it needs the enum
|
|
16
17
|
// at runtime rather than only in the types.
|
|
17
18
|
export { OperationState } from "../logic/admin/reads/protocolAdmin.reads.js";
|
|
@@ -45,6 +46,8 @@ export class KokioAdmin {
|
|
|
45
46
|
paymentAdapter;
|
|
46
47
|
/** Calls for the app to sign as a user operation, built here so the backend holds the purchase logic. */
|
|
47
48
|
calls;
|
|
49
|
+
/** Checks what a mined transaction paid, before the backend acts on it. */
|
|
50
|
+
utils;
|
|
48
51
|
// Instance-scoped surfaces - undefined until their address is known.
|
|
49
52
|
deviceWallet;
|
|
50
53
|
eSIMWallet;
|
|
@@ -60,6 +63,7 @@ export class KokioAdmin {
|
|
|
60
63
|
this.protocolAdmin = new AdminProtocolAdminSubPackage(walletClient);
|
|
61
64
|
this.paymentAdapter = new AdminPaymentAdapterSubPackage(walletClient);
|
|
62
65
|
this.calls = new AdminCallsSubPackage(walletClient);
|
|
66
|
+
this.utils = new AdminUtilsSubPackage(walletClient);
|
|
63
67
|
this.deviceWallet = deviceWalletAddress ? new AdminDeviceWalletSubPackage(walletClient, deviceWalletAddress) : undefined;
|
|
64
68
|
this.eSIMWallet = eSIMWalletAddress ? new AdminESIMWalletSubPackage(walletClient, eSIMWalletAddress) : undefined;
|
|
65
69
|
}
|
|
@@ -111,6 +115,7 @@ export class KokioAdmin {
|
|
|
111
115
|
this.protocolAdmin = new AdminProtocolAdminSubPackage(walletClient);
|
|
112
116
|
this.paymentAdapter = new AdminPaymentAdapterSubPackage(walletClient);
|
|
113
117
|
this.calls = new AdminCallsSubPackage(walletClient);
|
|
118
|
+
this.utils = new AdminUtilsSubPackage(walletClient);
|
|
114
119
|
this.deviceWallet = this.deviceWalletAddress ? new AdminDeviceWalletSubPackage(walletClient, this.deviceWalletAddress) : undefined;
|
|
115
120
|
this.eSIMWallet = this.eSIMWalletAddress ? new AdminESIMWalletSubPackage(walletClient, this.eSIMWalletAddress) : undefined;
|
|
116
121
|
return this;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { _verifyERC20Transfer, _verifyProtocolPayment } from "../../logic/admin/utils/tokenTransfer.js";
|
|
2
|
+
/**
|
|
3
|
+
* Checks what a mined transaction actually paid. Nothing is sent. Pass a hash
|
|
4
|
+
* when it came from a user: a receipt is used as given, so it must come from
|
|
5
|
+
* your own node.
|
|
6
|
+
*/
|
|
7
|
+
export class AdminUtilsSubPackage {
|
|
8
|
+
walletClient;
|
|
9
|
+
constructor(walletClient) {
|
|
10
|
+
this.walletClient = walletClient;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The purchases `eSIMWallet` paid for through the protocol in `symbol`
|
|
14
|
+
* ("USDC", "USDCt"), with each one's `paymentReference` and price in cents.
|
|
15
|
+
* Throws if the symbol is not an onchain currency on the payment adapter.
|
|
16
|
+
*/
|
|
17
|
+
verifyProtocolPayment(transaction, symbol, eSIMWallet) {
|
|
18
|
+
return _verifyProtocolPayment(this.walletClient, transaction, symbol, eSIMWallet);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What `sender` sent `destination` directly in any ERC-20. The cents count
|
|
22
|
+
* one token as one dollar, so they mean nothing for a token that is not.
|
|
23
|
+
*/
|
|
24
|
+
verifyERC20Transfer(transaction, token, sender, destination) {
|
|
25
|
+
return _verifyERC20Transfer(this.walletClient, transaction, token, sender, destination);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { BaseError, ContractFunctionRevertedError, ContractFunctionZeroDataError, AbiDecodingDataSizeTooSmallError, AbiDecodingZeroDataError, TransactionReceiptNotFoundError, erc20Abi, isAddress, isAddressEqual, isHash, parseEventLogs, publicActions, stringToHex, zeroAddress, } from "viem";
|
|
2
|
+
import { _chainId, _getChainSpecificConstants } from "../../constants.js";
|
|
3
|
+
import { ESIMWallet, PaymentAdapter, Registry } from "../../../abis/index.js";
|
|
4
|
+
import { InvalidAddressError, InvalidSymbolError, NotAProtocolESIMWalletError, NotAnERC20TokenError, PriceOutOfRangeError, ReceiptNotCanonicalError, TokenNotAcceptedError, TransactionRevertedError, UnknownTransactionError, UnmatchedPaymentEventsError, } from "../../errors.js";
|
|
5
|
+
// The contracts price everything in uint64 cents.
|
|
6
|
+
const MAX_UINT64 = 2n ** 64n - 1n;
|
|
7
|
+
const _checkAddress = (what, value) => {
|
|
8
|
+
if (!isAddress(value))
|
|
9
|
+
throw new InvalidAddressError(what, value, "is not a valid address");
|
|
10
|
+
if (isAddressEqual(value, zeroAddress))
|
|
11
|
+
throw new InvalidAddressError(what, value, "is the zero address");
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
// Same bytes as Solidity's `bytes32("USDC")`: the text on the left, zeros after.
|
|
15
|
+
const _symbolToBytes32 = (symbol) => {
|
|
16
|
+
if (!symbol)
|
|
17
|
+
throw new InvalidSymbolError(symbol);
|
|
18
|
+
try {
|
|
19
|
+
return stringToHex(symbol, { size: 32 });
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new InvalidSymbolError(symbol);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const _checkTransaction = (transaction) => {
|
|
26
|
+
if (typeof transaction === "string" && !isHash(transaction))
|
|
27
|
+
throw new UnknownTransactionError(transaction);
|
|
28
|
+
};
|
|
29
|
+
// A receipt passed in is used as given, so it must come from the caller's own node.
|
|
30
|
+
const _receipt = async (client, transaction) => {
|
|
31
|
+
let receipt = transaction;
|
|
32
|
+
if (typeof transaction === "string") {
|
|
33
|
+
try {
|
|
34
|
+
receipt = await client.extend(publicActions).getTransactionReceipt({ hash: transaction });
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
if (err instanceof TransactionReceiptNotFoundError)
|
|
38
|
+
throw new UnknownTransactionError(transaction);
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (receipt.status !== "success")
|
|
43
|
+
throw new TransactionRevertedError(receipt.transactionHash);
|
|
44
|
+
return receipt;
|
|
45
|
+
};
|
|
46
|
+
// The receipt plus how far its block has settled, read alongside it. A receipt passed in
|
|
47
|
+
// is also checked against the chain's block at its height, since it may be from a block
|
|
48
|
+
// since reorged out. One fetched by hash comes from the chain as it is now.
|
|
49
|
+
const _receiptWithFinality = async (client, transaction) => {
|
|
50
|
+
const publicClient = client.extend(publicActions);
|
|
51
|
+
const given = typeof transaction === "string" ? undefined : transaction;
|
|
52
|
+
const [receipt, safe, finalized, canonical] = await Promise.all([
|
|
53
|
+
_receipt(client, transaction),
|
|
54
|
+
publicClient.getBlock({ blockTag: "safe" }),
|
|
55
|
+
publicClient.getBlock({ blockTag: "finalized" }),
|
|
56
|
+
given && publicClient.getBlock({ blockNumber: given.blockNumber }),
|
|
57
|
+
]);
|
|
58
|
+
if (canonical && canonical.hash !== receipt.blockHash) {
|
|
59
|
+
throw new ReceiptNotCanonicalError(receipt.transactionHash, receipt.blockHash);
|
|
60
|
+
}
|
|
61
|
+
const finality = receipt.blockNumber <= finalized.number ? "finalized"
|
|
62
|
+
: receipt.blockNumber <= safe.number ? "safe"
|
|
63
|
+
: "latest";
|
|
64
|
+
return { receipt, landed: { finality, blockNumber: receipt.blockNumber, blockHash: receipt.blockHash } };
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Every purchase `eSIMWallet` paid for in `symbol` within one transaction, read
|
|
68
|
+
* from the payment adapter's `PaymentSettled` and the eSIM wallet's
|
|
69
|
+
* `DataBundleBoughtWithToken`. Cents and references are the contracts' own.
|
|
70
|
+
*/
|
|
71
|
+
export const _verifyProtocolPayment = async (client, transaction, symbol, eSIMWallet) => {
|
|
72
|
+
const wallet = _checkAddress("eSIM wallet", eSIMWallet);
|
|
73
|
+
const asset = _symbolToBytes32(symbol);
|
|
74
|
+
_checkTransaction(transaction);
|
|
75
|
+
const publicClient = client.extend(publicActions);
|
|
76
|
+
const reads = async () => {
|
|
77
|
+
const F = _getChainSpecificConstants(await _chainId(client), client.transport.url).factoryAddresses;
|
|
78
|
+
const [deviceWallet, entry] = await Promise.all([
|
|
79
|
+
publicClient.readContract({ address: F.REGISTRY, abi: Registry, functionName: "isESIMWalletValid", args: [wallet] }),
|
|
80
|
+
publicClient.readContract({ address: F.PAYMENT_ADAPTER, abi: PaymentAdapter, functionName: "assets", args: [asset] }),
|
|
81
|
+
]);
|
|
82
|
+
return { adapter: F.PAYMENT_ADAPTER, deviceWallet, entry };
|
|
83
|
+
};
|
|
84
|
+
const [{ receipt, landed }, { adapter, deviceWallet, entry: [, , decimals, token] }] = await Promise.all([
|
|
85
|
+
_receiptWithFinality(client, transaction),
|
|
86
|
+
reads(),
|
|
87
|
+
]);
|
|
88
|
+
if (deviceWallet === zeroAddress)
|
|
89
|
+
throw new NotAProtocolESIMWalletError(wallet);
|
|
90
|
+
// A registered symbol always has non-zero decimals, even once withdrawn.
|
|
91
|
+
if (decimals === 0)
|
|
92
|
+
throw new TokenNotAcceptedError(symbol, "NOT_REGISTERED");
|
|
93
|
+
if (token === zeroAddress)
|
|
94
|
+
throw new TokenNotAcceptedError(symbol, "NOT_ONCHAIN");
|
|
95
|
+
// Filtered on the emitting address, so a look-alike event from any other contract is ignored.
|
|
96
|
+
const settled = parseEventLogs({ abi: PaymentAdapter, eventName: "PaymentSettled", logs: receipt.logs })
|
|
97
|
+
.filter((log) => isAddressEqual(log.address, adapter) && log.args._symbol === asset && isAddressEqual(log.args._eSIMWallet, wallet));
|
|
98
|
+
const bought = parseEventLogs({ abi: ESIMWallet, eventName: "DataBundleBoughtWithToken", logs: receipt.logs })
|
|
99
|
+
.filter((log) => isAddressEqual(log.address, wallet) && log.args._asset === asset);
|
|
100
|
+
// The adapter settles first and the wallet emits after, once per purchase.
|
|
101
|
+
if (settled.length !== bought.length)
|
|
102
|
+
throw new UnmatchedPaymentEventsError(receipt.transactionHash);
|
|
103
|
+
const payments = settled.map((s, i) => {
|
|
104
|
+
const b = bought[i];
|
|
105
|
+
if (s.logIndex > b.logIndex || s.args._priceUSDCents !== b.args._priceUSDCents || s.args._spent !== b.args._amountSpent) {
|
|
106
|
+
throw new UnmatchedPaymentEventsError(receipt.transactionHash);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
paymentReference: b.args._paymentReference,
|
|
110
|
+
dataBundleId: b.args._dataBundleID,
|
|
111
|
+
priceUSDCents: b.args._priceUSDCents,
|
|
112
|
+
amountSpent: b.args._amountSpent,
|
|
113
|
+
vault: s.args._vault,
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
return { priceUSDCents: payments.reduce((sum, p) => sum + p.priceUSDCents, 0n), payments, ...landed };
|
|
117
|
+
};
|
|
118
|
+
// A revert or an empty answer means the address is not an ERC-20. Anything else, like a
|
|
119
|
+
// network failure, is passed on as-is.
|
|
120
|
+
const _erc20Read = (read, token) => read.catch((err) => {
|
|
121
|
+
const notAToken = err instanceof BaseError && err.walk((e) => e instanceof ContractFunctionRevertedError ||
|
|
122
|
+
e instanceof ContractFunctionZeroDataError ||
|
|
123
|
+
e instanceof AbiDecodingZeroDataError ||
|
|
124
|
+
e instanceof AbiDecodingDataSizeTooSmallError);
|
|
125
|
+
throw notAToken ? new NotAnERC20TokenError(token) : err;
|
|
126
|
+
});
|
|
127
|
+
/**
|
|
128
|
+
* What `sender` sent `destination` directly in `token` within one transaction.
|
|
129
|
+
* Cents treat one token as one dollar, the way the payment adapter prices a
|
|
130
|
+
* dollar currency, so they mean nothing for a token that is not.
|
|
131
|
+
*/
|
|
132
|
+
export const _verifyERC20Transfer = async (client, transaction, token, sender, destination) => {
|
|
133
|
+
const tokenAddress = _checkAddress("Token", token);
|
|
134
|
+
const from = _checkAddress("Sender", sender);
|
|
135
|
+
const to = _checkAddress("Destination", destination);
|
|
136
|
+
if (isAddressEqual(from, to))
|
|
137
|
+
throw new InvalidAddressError("Destination", to, "is the same as the sender");
|
|
138
|
+
_checkTransaction(transaction);
|
|
139
|
+
const publicClient = client.extend(publicActions);
|
|
140
|
+
// decimals() is what an ERC-721 lacks, and an address with no code answers neither.
|
|
141
|
+
const [{ receipt, landed }, decimals] = await Promise.all([
|
|
142
|
+
_receiptWithFinality(client, transaction),
|
|
143
|
+
_erc20Read(publicClient.readContract({ address: tokenAddress, abi: erc20Abi, functionName: "decimals" }), tokenAddress),
|
|
144
|
+
_erc20Read(publicClient.readContract({ address: tokenAddress, abi: erc20Abi, functionName: "totalSupply" }), tokenAddress),
|
|
145
|
+
]);
|
|
146
|
+
// Strict parsing skips an ERC-721 Transfer, whose third topic is the token id.
|
|
147
|
+
const amount = parseEventLogs({ abi: erc20Abi, eventName: "Transfer", logs: receipt.logs })
|
|
148
|
+
.filter((log) => isAddressEqual(log.address, tokenAddress) && isAddressEqual(log.args.from, from) && isAddressEqual(log.args.to, to))
|
|
149
|
+
.reduce((sum, log) => sum + log.args.value, 0n);
|
|
150
|
+
// amount * 100 / 10^decimals, without the large intermediate.
|
|
151
|
+
const priceUSDCents = decimals >= 2
|
|
152
|
+
? amount / 10n ** BigInt(decimals - 2)
|
|
153
|
+
: amount * 10n ** BigInt(2 - decimals);
|
|
154
|
+
if (priceUSDCents > MAX_UINT64)
|
|
155
|
+
throw new PriceOutOfRangeError(priceUSDCents);
|
|
156
|
+
return { priceUSDCents, amount, ...landed };
|
|
157
|
+
};
|
package/dist/esm/logic/errors.js
CHANGED
|
@@ -132,6 +132,102 @@ export class StalledBatchError extends KokioError {
|
|
|
132
132
|
this.remaining = remaining;
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
+
/** An address argument is malformed, zero, or the same as the other side of the transfer. */
|
|
136
|
+
export class InvalidAddressError extends KokioError {
|
|
137
|
+
value;
|
|
138
|
+
constructor(what, value, reason) {
|
|
139
|
+
super("INVALID_ADDRESS", `${what} ${value} ${reason}.`);
|
|
140
|
+
this.value = value;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** A currency symbol is empty or does not fit the contracts' 32-byte symbol. */
|
|
144
|
+
export class InvalidSymbolError extends KokioError {
|
|
145
|
+
symbol;
|
|
146
|
+
constructor(symbol) {
|
|
147
|
+
super("INVALID_SYMBOL", `Symbol "${symbol}" must be 1 to 32 bytes long.`);
|
|
148
|
+
this.symbol = symbol;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The payment adapter cannot have taken this symbol as an onchain payment:
|
|
153
|
+
* `NOT_REGISTERED` if it was never added, `NOT_ONCHAIN` for fiat and non-EVM
|
|
154
|
+
* entries, which have no token address.
|
|
155
|
+
*/
|
|
156
|
+
export class TokenNotAcceptedError extends KokioError {
|
|
157
|
+
symbol;
|
|
158
|
+
reason;
|
|
159
|
+
constructor(symbol, reason) {
|
|
160
|
+
super("TOKEN_NOT_ACCEPTED", reason === "NOT_REGISTERED"
|
|
161
|
+
? `Symbol "${symbol}" is not registered on the payment adapter.`
|
|
162
|
+
: `Symbol "${symbol}" has no token address on the payment adapter, so it cannot be paid onchain.`);
|
|
163
|
+
this.symbol = symbol;
|
|
164
|
+
this.reason = reason;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** The registry has no record of this address as an eSIM wallet. */
|
|
168
|
+
export class NotAProtocolESIMWalletError extends KokioError {
|
|
169
|
+
address;
|
|
170
|
+
constructor(address) {
|
|
171
|
+
super("NOT_A_PROTOCOL_ESIM_WALLET", `${address} is not an eSIM wallet the registry knows.`);
|
|
172
|
+
this.address = address;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** No mined transaction with this hash on the client's chain. */
|
|
176
|
+
export class UnknownTransactionError extends KokioError {
|
|
177
|
+
hash;
|
|
178
|
+
constructor(hash) {
|
|
179
|
+
super("UNKNOWN_TRANSACTION", `No mined transaction ${hash} on this chain.`);
|
|
180
|
+
this.hash = hash;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/** The transaction was mined but reverted, so nothing in it moved. */
|
|
184
|
+
export class TransactionRevertedError extends KokioError {
|
|
185
|
+
hash;
|
|
186
|
+
constructor(hash) {
|
|
187
|
+
super("TRANSACTION_REVERTED", `Transaction ${hash} reverted.`);
|
|
188
|
+
this.hash = hash;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* A receipt passed in is from a block the chain no longer has at that height,
|
|
193
|
+
* so it was reorged out. The payment may have landed again elsewhere.
|
|
194
|
+
*/
|
|
195
|
+
export class ReceiptNotCanonicalError extends KokioError {
|
|
196
|
+
hash;
|
|
197
|
+
blockHash;
|
|
198
|
+
constructor(hash, blockHash) {
|
|
199
|
+
super("RECEIPT_NOT_CANONICAL", `The receipt for ${hash} is from block ${blockHash}, which is no longer on the chain. Check again by hash.`);
|
|
200
|
+
this.hash = hash;
|
|
201
|
+
this.blockHash = blockHash;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/** The address does not answer `decimals()` and `totalSupply()` like an ERC-20. */
|
|
205
|
+
export class NotAnERC20TokenError extends KokioError {
|
|
206
|
+
token;
|
|
207
|
+
constructor(token) {
|
|
208
|
+
super("NOT_AN_ERC20_TOKEN", `${token} is not an ERC-20 token on this chain.`);
|
|
209
|
+
this.token = token;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* A purchase's adapter event and eSIM wallet event disagree or do not pair up.
|
|
214
|
+
* The current contracts always emit them together, so this means they changed.
|
|
215
|
+
*/
|
|
216
|
+
export class UnmatchedPaymentEventsError extends KokioError {
|
|
217
|
+
hash;
|
|
218
|
+
constructor(hash) {
|
|
219
|
+
super("UNMATCHED_PAYMENT_EVENTS", `Transaction ${hash} has payment events that do not pair up.`);
|
|
220
|
+
this.hash = hash;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** A cent figure too large for the uint64 the contracts price in. */
|
|
224
|
+
export class PriceOutOfRangeError extends KokioError {
|
|
225
|
+
priceUSDCents;
|
|
226
|
+
constructor(priceUSDCents) {
|
|
227
|
+
super("PRICE_OUT_OF_RANGE", `${priceUSDCents} cents does not fit the contracts' uint64 price.`);
|
|
228
|
+
this.priceUSDCents = priceUSDCents;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
135
231
|
// Every ABI that can surface a custom error from an on-chain revert. viem's
|
|
136
232
|
// `decodeErrorResult` walks each ABI's `error` fragments to match the 4-byte
|
|
137
233
|
// selector in the revert data.
|
|
@@ -9,7 +9,8 @@ import { AdminDeviceWalletSubPackage } from "./interface/deviceWalletClass.js";
|
|
|
9
9
|
import { AdminESIMWalletSubPackage } from "./interface/eSIMWalletClass.js";
|
|
10
10
|
import { AdminPaymentAdapterSubPackage } from "./interface/paymentAdapterClass.js";
|
|
11
11
|
import { AdminCallsSubPackage } from "./interface/callsClass.js";
|
|
12
|
-
|
|
12
|
+
import { AdminUtilsSubPackage } from "./interface/utilsClass.js";
|
|
13
|
+
export { KokioError, NullOrUndefinedValueError, MissingSmartWalletError, MissingEOAWalletError, InvalidClientError, UnsupportedChainError, UnconfiguredChainError, CounterfactualMismatchError, BatchSizeOutOfRangeError, DepositOnResumeError, ESIMWalletNotLazyDeployedError, MissingBatchEventError, StalledBatchError, InvalidAddressError, InvalidSymbolError, TokenNotAcceptedError, NotAProtocolESIMWalletError, UnknownTransactionError, TransactionRevertedError, ReceiptNotCanonicalError, NotAnERC20TokenError, UnmatchedPaymentEventsError, PriceOutOfRangeError, ContractRevertError, decodeContractRevert, } from "../logic/errors.js";
|
|
13
14
|
export type { DecodedRevert } from "../logic/errors.js";
|
|
14
15
|
export { OperationState } from "../logic/admin/reads/protocolAdmin.reads.js";
|
|
15
16
|
/**
|
|
@@ -41,6 +42,8 @@ export declare class KokioAdmin {
|
|
|
41
42
|
paymentAdapter: AdminPaymentAdapterSubPackage;
|
|
42
43
|
/** Calls for the app to sign as a user operation, built here so the backend holds the purchase logic. */
|
|
43
44
|
calls: AdminCallsSubPackage;
|
|
45
|
+
/** Checks what a mined transaction paid, before the backend acts on it. */
|
|
46
|
+
utils: AdminUtilsSubPackage;
|
|
44
47
|
deviceWallet?: AdminDeviceWalletSubPackage;
|
|
45
48
|
eSIMWallet?: AdminESIMWalletSubPackage;
|
|
46
49
|
constructor(walletClient: WalletClient, deviceWalletAddress?: Address, eSIMWalletAddress?: Address);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Address, Hash, TransactionReceipt, WalletClient } from "viem";
|
|
2
|
+
/**
|
|
3
|
+
* Checks what a mined transaction actually paid. Nothing is sent. Pass a hash
|
|
4
|
+
* when it came from a user: a receipt is used as given, so it must come from
|
|
5
|
+
* your own node.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AdminUtilsSubPackage {
|
|
8
|
+
walletClient: WalletClient;
|
|
9
|
+
constructor(walletClient: WalletClient);
|
|
10
|
+
/**
|
|
11
|
+
* The purchases `eSIMWallet` paid for through the protocol in `symbol`
|
|
12
|
+
* ("USDC", "USDCt"), with each one's `paymentReference` and price in cents.
|
|
13
|
+
* Throws if the symbol is not an onchain currency on the payment adapter.
|
|
14
|
+
*/
|
|
15
|
+
verifyProtocolPayment(transaction: Hash | TransactionReceipt, symbol: string, eSIMWallet: Address): Promise<import("../../types.js").ProtocolPaymentCheck>;
|
|
16
|
+
/**
|
|
17
|
+
* What `sender` sent `destination` directly in any ERC-20. The cents count
|
|
18
|
+
* one token as one dollar, so they mean nothing for a token that is not.
|
|
19
|
+
*/
|
|
20
|
+
verifyERC20Transfer(transaction: Hash | TransactionReceipt, token: Address, sender: Address, destination: Address): Promise<import("../../types.js").ERC20TransferCheck>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Address, Hash, TransactionReceipt, WalletClient } from "viem";
|
|
2
|
+
import { ERC20TransferCheck, ProtocolPaymentCheck } from "../../../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Every purchase `eSIMWallet` paid for in `symbol` within one transaction, read
|
|
5
|
+
* from the payment adapter's `PaymentSettled` and the eSIM wallet's
|
|
6
|
+
* `DataBundleBoughtWithToken`. Cents and references are the contracts' own.
|
|
7
|
+
*/
|
|
8
|
+
export declare const _verifyProtocolPayment: (client: WalletClient, transaction: Hash | TransactionReceipt, symbol: string, eSIMWallet: Address) => Promise<ProtocolPaymentCheck>;
|
|
9
|
+
/**
|
|
10
|
+
* What `sender` sent `destination` directly in `token` within one transaction.
|
|
11
|
+
* Cents treat one token as one dollar, the way the payment adapter prices a
|
|
12
|
+
* dollar currency, so they mean nothing for a token that is not.
|
|
13
|
+
*/
|
|
14
|
+
export declare const _verifyERC20Transfer: (client: WalletClient, transaction: Hash | TransactionReceipt, token: Address, sender: Address, destination: Address) => Promise<ERC20TransferCheck>;
|
|
@@ -87,6 +87,68 @@ export declare class StalledBatchError extends KokioError {
|
|
|
87
87
|
readonly remaining: bigint;
|
|
88
88
|
constructor(hash: Hex, remaining: bigint);
|
|
89
89
|
}
|
|
90
|
+
/** An address argument is malformed, zero, or the same as the other side of the transfer. */
|
|
91
|
+
export declare class InvalidAddressError extends KokioError {
|
|
92
|
+
readonly value: string;
|
|
93
|
+
constructor(what: string, value: string, reason: string);
|
|
94
|
+
}
|
|
95
|
+
/** A currency symbol is empty or does not fit the contracts' 32-byte symbol. */
|
|
96
|
+
export declare class InvalidSymbolError extends KokioError {
|
|
97
|
+
readonly symbol: string;
|
|
98
|
+
constructor(symbol: string);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The payment adapter cannot have taken this symbol as an onchain payment:
|
|
102
|
+
* `NOT_REGISTERED` if it was never added, `NOT_ONCHAIN` for fiat and non-EVM
|
|
103
|
+
* entries, which have no token address.
|
|
104
|
+
*/
|
|
105
|
+
export declare class TokenNotAcceptedError extends KokioError {
|
|
106
|
+
readonly symbol: string;
|
|
107
|
+
readonly reason: "NOT_REGISTERED" | "NOT_ONCHAIN";
|
|
108
|
+
constructor(symbol: string, reason: "NOT_REGISTERED" | "NOT_ONCHAIN");
|
|
109
|
+
}
|
|
110
|
+
/** The registry has no record of this address as an eSIM wallet. */
|
|
111
|
+
export declare class NotAProtocolESIMWalletError extends KokioError {
|
|
112
|
+
readonly address: string;
|
|
113
|
+
constructor(address: string);
|
|
114
|
+
}
|
|
115
|
+
/** No mined transaction with this hash on the client's chain. */
|
|
116
|
+
export declare class UnknownTransactionError extends KokioError {
|
|
117
|
+
readonly hash: Hex;
|
|
118
|
+
constructor(hash: Hex);
|
|
119
|
+
}
|
|
120
|
+
/** The transaction was mined but reverted, so nothing in it moved. */
|
|
121
|
+
export declare class TransactionRevertedError extends KokioError {
|
|
122
|
+
readonly hash: Hex;
|
|
123
|
+
constructor(hash: Hex);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A receipt passed in is from a block the chain no longer has at that height,
|
|
127
|
+
* so it was reorged out. The payment may have landed again elsewhere.
|
|
128
|
+
*/
|
|
129
|
+
export declare class ReceiptNotCanonicalError extends KokioError {
|
|
130
|
+
readonly hash: Hex;
|
|
131
|
+
readonly blockHash: Hex;
|
|
132
|
+
constructor(hash: Hex, blockHash: Hex);
|
|
133
|
+
}
|
|
134
|
+
/** The address does not answer `decimals()` and `totalSupply()` like an ERC-20. */
|
|
135
|
+
export declare class NotAnERC20TokenError extends KokioError {
|
|
136
|
+
readonly token: string;
|
|
137
|
+
constructor(token: string);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A purchase's adapter event and eSIM wallet event disagree or do not pair up.
|
|
141
|
+
* The current contracts always emit them together, so this means they changed.
|
|
142
|
+
*/
|
|
143
|
+
export declare class UnmatchedPaymentEventsError extends KokioError {
|
|
144
|
+
readonly hash: Hex;
|
|
145
|
+
constructor(hash: Hex);
|
|
146
|
+
}
|
|
147
|
+
/** A cent figure too large for the uint64 the contracts price in. */
|
|
148
|
+
export declare class PriceOutOfRangeError extends KokioError {
|
|
149
|
+
readonly priceUSDCents: bigint;
|
|
150
|
+
constructor(priceUSDCents: bigint);
|
|
151
|
+
}
|
|
90
152
|
export interface DecodedRevert {
|
|
91
153
|
errorName: string;
|
|
92
154
|
args: readonly unknown[];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { P256Key, Call, WebAuthnSignature, P256Credential, DataBundleDetails, Asset, SignedRequest, KokioSmartAccount, KokioSmartAccountClient, OwnerCall, OperationOptions, ScheduledOperation, ScheduledBatchOperation, LazyDeploymentBatch, LazyDeployment, LazyHistoryBatch, LazyHistoryCopy } from './types.js';
|
|
1
|
+
export type { P256Key, Call, WebAuthnSignature, P256Credential, DataBundleDetails, Asset, SignedRequest, KokioSmartAccount, KokioSmartAccountClient, OwnerCall, OperationOptions, ScheduledOperation, ScheduledBatchOperation, LazyDeploymentBatch, LazyDeployment, LazyHistoryBatch, LazyHistoryCopy, Finality, TransactionFinality, ProtocolPayment, ProtocolPaymentCheck, ERC20TransferCheck } from './types.js';
|
|
2
2
|
export type { KokioConstants } from './interface/constantsClass.js';
|
|
3
3
|
export { Settlement } from './types.js';
|
package/dist/types/types.d.ts
CHANGED
|
@@ -154,6 +154,41 @@ export type LazyHistoryCopy = {
|
|
|
154
154
|
/** The history was already fully copied, so nothing was sent. */
|
|
155
155
|
alreadyComplete: boolean;
|
|
156
156
|
};
|
|
157
|
+
/** One data bundle an eSIM wallet paid for through the protocol. */
|
|
158
|
+
export type ProtocolPayment = {
|
|
159
|
+
/** The offchain order id the purchase spent. */
|
|
160
|
+
paymentReference: Hex;
|
|
161
|
+
dataBundleId: Hex;
|
|
162
|
+
/** 123456n is $1234.56. */
|
|
163
|
+
priceUSDCents: bigint;
|
|
164
|
+
/** What reached the vault, in the token's smallest unit. */
|
|
165
|
+
amountSpent: bigint;
|
|
166
|
+
vault: Address;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* How far a block has settled. `latest` can still be reorged out, `safe` only by
|
|
170
|
+
* an L1 reorg, and `finalized` not at all.
|
|
171
|
+
*/
|
|
172
|
+
export type Finality = "latest" | "safe" | "finalized";
|
|
173
|
+
/** Where a checked transaction landed, to store and check again later. */
|
|
174
|
+
export type TransactionFinality = {
|
|
175
|
+
finality: Finality;
|
|
176
|
+
blockNumber: bigint;
|
|
177
|
+
blockHash: Hex;
|
|
178
|
+
};
|
|
179
|
+
/** Every protocol purchase one eSIM wallet made in one transaction and currency. */
|
|
180
|
+
export type ProtocolPaymentCheck = TransactionFinality & {
|
|
181
|
+
/** Total across `payments`, 0n if there were none. */
|
|
182
|
+
priceUSDCents: bigint;
|
|
183
|
+
payments: readonly ProtocolPayment[];
|
|
184
|
+
};
|
|
185
|
+
/** What one address sent another directly in one ERC-20, in one transaction. */
|
|
186
|
+
export type ERC20TransferCheck = TransactionFinality & {
|
|
187
|
+
/** `amount` as cents, counting one token as one dollar and rounding down. */
|
|
188
|
+
priceUSDCents: bigint;
|
|
189
|
+
/** In the token's smallest unit, 0n if nothing was sent. */
|
|
190
|
+
amount: bigint;
|
|
191
|
+
};
|
|
157
192
|
export type SignedRequest = {
|
|
158
193
|
body: string;
|
|
159
194
|
stamp: {
|