kokio-sdk 3.3.0 → 3.4.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 +3 -1
- package/dist/esm/admin/interface/registryClass.js +2 -2
- package/dist/esm/admin/interface/utilsClass.js +19 -3
- package/dist/esm/logic/admin/registry.eoa.js +79 -5
- package/dist/esm/logic/admin/utils/paymentReference.js +58 -0
- package/dist/esm/logic/errors.js +24 -0
- package/dist/types/admin/config-admin.d.ts +2 -1
- package/dist/types/admin/interface/registryClass.d.ts +13 -1
- package/dist/types/admin/interface/utilsClass.d.ts +19 -4
- package/dist/types/logic/admin/registry.eoa.d.ts +23 -2
- package/dist/types/logic/admin/utils/paymentReference.d.ts +26 -0
- package/dist/types/logic/errors.d.ts +17 -0
- package/package.json +1 -1
|
@@ -12,10 +12,12 @@ import { AdminUtilsSubPackage } from "./interface/utilsClass.js";
|
|
|
12
12
|
// Re-export the typed error surface so backend consumers can `instanceof
|
|
13
13
|
// KokioError` (or a subclass) and decode reverts without reaching into internal
|
|
14
14
|
// module paths - mirroring `config.ts`.
|
|
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
|
+
export { KokioError, NullOrUndefinedValueError, MissingSmartWalletError, MissingEOAWalletError, InvalidClientError, UnsupportedChainError, UnconfiguredChainError, CounterfactualMismatchError, BatchSizeOutOfRangeError, DepositOnResumeError, ESIMWalletNotLazyDeployedError, MissingBatchEventError, StalledBatchError, InvalidAddressError, InvalidSymbolError, TokenNotAcceptedError, NotAProtocolESIMWalletError, UnknownTransactionError, TransactionRevertedError, ReceiptNotCanonicalError, NotAnERC20TokenError, UnmatchedPaymentEventsError, PriceOutOfRangeError, InvalidPaymentReferenceError, CouponSplitOutOfRangeError, ContractRevertError, decodeContractRevert, } from "../logic/errors.js";
|
|
16
16
|
// `getOperationState` answers with this, and comparing against it needs the enum
|
|
17
17
|
// at runtime rather than only in the types.
|
|
18
18
|
export { OperationState } from "../logic/admin/reads/protocolAdmin.reads.js";
|
|
19
|
+
// Passed to `utils.tagPaymentReference` and returned by `utils.parsePaymentReference`.
|
|
20
|
+
export { PaymentReferenceKind } from "../logic/admin/utils/paymentReference.js";
|
|
19
21
|
/**
|
|
20
22
|
* EOA-only entry point for the NodeJS backend.
|
|
21
23
|
*
|
|
@@ -24,8 +24,8 @@ export class AdminRegistrySubPackage {
|
|
|
24
24
|
acceptAdminUpdate() {
|
|
25
25
|
return _acceptAdminUpdate(this.walletClient);
|
|
26
26
|
}
|
|
27
|
-
recordSettledPurchase(eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference) {
|
|
28
|
-
return _recordSettledPurchase(this.walletClient, eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference);
|
|
27
|
+
recordSettledPurchase(eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference, couponUSDCents) {
|
|
28
|
+
return _recordSettledPurchase(this.walletClient, eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference, couponUSDCents);
|
|
29
29
|
}
|
|
30
30
|
assignESIMIdentifier(eSIMWalletAddress, eSIMUniqueIdentifier) {
|
|
31
31
|
return _assignESIMIdentifier(this.walletClient, eSIMWalletAddress, eSIMUniqueIdentifier);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { _verifyERC20Transfer, _verifyProtocolPayment } from "../../logic/admin/utils/tokenTransfer.js";
|
|
2
|
+
import { _parsePaymentReference, _tagPaymentReference, } from "../../logic/admin/utils/paymentReference.js";
|
|
2
3
|
/**
|
|
3
|
-
* Checks what a mined transaction actually paid
|
|
4
|
-
* when it came from a user: a receipt
|
|
5
|
-
* your own node.
|
|
4
|
+
* Checks what a mined transaction actually paid, and builds and reads payment
|
|
5
|
+
* references. Nothing is sent. Pass a hash when it came from a user: a receipt
|
|
6
|
+
* is used as given, so it must come from your own node.
|
|
6
7
|
*/
|
|
7
8
|
export class AdminUtilsSubPackage {
|
|
8
9
|
walletClient;
|
|
@@ -24,4 +25,19 @@ export class AdminUtilsSubPackage {
|
|
|
24
25
|
verifyERC20Transfer(transaction, token, sender, destination) {
|
|
25
26
|
return _verifyERC20Transfer(this.walletClient, transaction, token, sender, destination);
|
|
26
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Marks `reference` with how the purchase was paid for. Its high 20 bytes
|
|
30
|
+
* must be zero. For a split purchase, tag the same reference once as
|
|
31
|
+
* `CouponPart` and once as `Remainder`.
|
|
32
|
+
*/
|
|
33
|
+
tagPaymentReference(reference, kind) {
|
|
34
|
+
return _tagPaymentReference(reference, kind);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* How a purchase was paid for, and the untagged reference it was built
|
|
38
|
+
* from. Throws on a tag this SDK did not write.
|
|
39
|
+
*/
|
|
40
|
+
parsePaymentReference(reference) {
|
|
41
|
+
return _parsePaymentReference(reference);
|
|
42
|
+
}
|
|
27
43
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { encodeAbiParameters, keccak256, publicActions, stringToHex } from "viem";
|
|
1
2
|
import { _chainId, _getChainSpecificConstants } from "../constants.js";
|
|
2
|
-
import { MissingEOAWalletError, writeContractOrThrow } from "../errors.js";
|
|
3
|
+
import { CouponSplitOutOfRangeError, InvalidPaymentReferenceError, MissingEOAWalletError, TransactionRevertedError, writeContractOrThrow, } from "../errors.js";
|
|
3
4
|
import { Registry } from "../../abis/index.js";
|
|
5
|
+
import { Settlement } from "../../types.js";
|
|
6
|
+
import { PaymentReferenceKind, _parsePaymentReference } from "./utils/paymentReference.js";
|
|
4
7
|
// Admin-EOA logic for `Registry`. Most of this is `onlyOwner`, so the `client`
|
|
5
8
|
// must carry the owner EOA. `_acceptAdminUpdate` is the nominee's own call and
|
|
6
9
|
// `_assignESIMIdentifier` is `onlyESIMWalletAdmin`.
|
|
@@ -207,21 +210,92 @@ export const _assignESIMIdentifier = async (client, eSIMWalletAddress, eSIMUniqu
|
|
|
207
210
|
* never sees a transfer to prove it), and `_tokenAmount` is recorded for
|
|
208
211
|
* offchain matching but never checked against `_dataBundleDetail.priceUSDCents`.
|
|
209
212
|
* `_paymentReference` is spendable once per eSIM wallet.
|
|
213
|
+
*
|
|
214
|
+
* Given `[couponRef, remainderRef]` and `couponUSDCents`, records a purchase
|
|
215
|
+
* split between a coupon and another payment as two lines, see
|
|
216
|
+
* {@link _couponSplitLines}. Lines already recorded are skipped, so a retry
|
|
217
|
+
* after a partial failure sends only what is missing. Returns the last hash
|
|
218
|
+
* sent; any earlier one is already mined.
|
|
210
219
|
*/
|
|
211
|
-
export const _recordSettledPurchase = async (client, eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference) => {
|
|
220
|
+
export const _recordSettledPurchase = async (client, eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference, couponUSDCents) => {
|
|
212
221
|
const chainID = await _chainId(client);
|
|
213
222
|
const rpcURL = client.transport.url;
|
|
214
223
|
const values = _getChainSpecificConstants(chainID, rpcURL);
|
|
215
224
|
if (!client.account)
|
|
216
225
|
throw new MissingEOAWalletError();
|
|
217
|
-
|
|
226
|
+
const account = client.account;
|
|
227
|
+
const record = (line) => writeContractOrThrow(client, {
|
|
218
228
|
address: values.factoryAddresses.REGISTRY,
|
|
219
229
|
chain: values.chain,
|
|
220
|
-
account
|
|
230
|
+
account,
|
|
221
231
|
abi: Registry,
|
|
222
232
|
functionName: 'recordSettledPurchase',
|
|
223
|
-
args: [eSIMWalletAddress, dataBundleDetail, asset, tokenAmount, paymentReference]
|
|
233
|
+
args: [eSIMWalletAddress, line.dataBundleDetail, line.asset, line.tokenAmount, line.paymentReference]
|
|
224
234
|
});
|
|
235
|
+
if (typeof paymentReference === "string") {
|
|
236
|
+
return record({ dataBundleDetail, asset, tokenAmount, paymentReference });
|
|
237
|
+
}
|
|
238
|
+
const lines = _couponSplitLines(dataBundleDetail, asset, tokenAmount, paymentReference, couponUSDCents);
|
|
239
|
+
const publicClient = client.extend(publicActions);
|
|
240
|
+
const spent = await Promise.all(lines.map((line) => publicClient.readContract({
|
|
241
|
+
address: values.factoryAddresses.REGISTRY,
|
|
242
|
+
abi: Registry,
|
|
243
|
+
functionName: "usedPaymentReferences",
|
|
244
|
+
args: [keccak256(encodeAbiParameters([{ type: "address" }, { type: "bytes32" }], [eSIMWalletAddress, line.paymentReference]))]
|
|
245
|
+
})));
|
|
246
|
+
const missing = lines.filter((_, i) => !spent[i]);
|
|
247
|
+
// With both lines recorded, the remainder is sent anyway so the contract refuses it with
|
|
248
|
+
// `PaymentReferenceAlreadyUsed`, the same answer a retried single reference gets.
|
|
249
|
+
let hash;
|
|
250
|
+
for (const line of missing.length > 0 ? missing : [lines[0]]) {
|
|
251
|
+
if (hash) {
|
|
252
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
253
|
+
if (receipt.status !== "success")
|
|
254
|
+
throw new TransactionRevertedError(hash);
|
|
255
|
+
}
|
|
256
|
+
hash = await record(line);
|
|
257
|
+
}
|
|
258
|
+
return hash;
|
|
259
|
+
};
|
|
260
|
+
// Coupons are recorded as card payments in USD, whose smallest unit is a cent.
|
|
261
|
+
const COUPON_ASSET = stringToHex("USD", { size: 32 });
|
|
262
|
+
/**
|
|
263
|
+
* Splits one purchase into its remainder line and its coupon line, in that
|
|
264
|
+
* order. `dataBundleDetail` carries the full price and how the remainder was
|
|
265
|
+
* paid; `asset` and `tokenAmount` are what the user paid for the remainder.
|
|
266
|
+
* The coupon line is always `Fiat` in `USD`, with the coupon's cents as its
|
|
267
|
+
* token amount.
|
|
268
|
+
*/
|
|
269
|
+
export const _couponSplitLines = (dataBundleDetail, asset, tokenAmount, [couponRef, remainderRef], couponUSDCents) => {
|
|
270
|
+
const coupon = _parsePaymentReference(couponRef);
|
|
271
|
+
const remainder = _parsePaymentReference(remainderRef);
|
|
272
|
+
if (coupon.kind !== PaymentReferenceKind.CouponPart) {
|
|
273
|
+
throw new InvalidPaymentReferenceError(couponRef, "is not tagged as a coupon part");
|
|
274
|
+
}
|
|
275
|
+
if (remainder.kind !== PaymentReferenceKind.Remainder) {
|
|
276
|
+
throw new InvalidPaymentReferenceError(remainderRef, "is not tagged as a remainder");
|
|
277
|
+
}
|
|
278
|
+
if (coupon.reference !== remainder.reference) {
|
|
279
|
+
throw new InvalidPaymentReferenceError(remainderRef, `belongs to a different order than ${couponRef}`);
|
|
280
|
+
}
|
|
281
|
+
const price = dataBundleDetail.priceUSDCents;
|
|
282
|
+
if (couponUSDCents === undefined || couponUSDCents <= 0n || couponUSDCents >= price) {
|
|
283
|
+
throw new CouponSplitOutOfRangeError(couponUSDCents, price);
|
|
284
|
+
}
|
|
285
|
+
return [
|
|
286
|
+
{
|
|
287
|
+
dataBundleDetail: { ...dataBundleDetail, priceUSDCents: price - couponUSDCents },
|
|
288
|
+
asset,
|
|
289
|
+
tokenAmount,
|
|
290
|
+
paymentReference: remainderRef,
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
dataBundleDetail: { id: dataBundleDetail.id, priceUSDCents: couponUSDCents, settlement: Settlement.Fiat },
|
|
294
|
+
asset: COUPON_ASSET,
|
|
295
|
+
tokenAmount: couponUSDCents,
|
|
296
|
+
paymentReference: couponRef,
|
|
297
|
+
},
|
|
298
|
+
];
|
|
225
299
|
};
|
|
226
300
|
/**
|
|
227
301
|
* Step 2 of the 2-step admin handover: the nominee accepts. The chain requires
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { concat, isHex, pad, size, slice } from "viem";
|
|
2
|
+
import { InvalidPaymentReferenceError } from "../../errors.js";
|
|
3
|
+
/** How a purchase was paid for, as written into its payment reference. */
|
|
4
|
+
export var PaymentReferenceKind;
|
|
5
|
+
(function (PaymentReferenceKind) {
|
|
6
|
+
/** Paid in full without a coupon. Carries no tag. */
|
|
7
|
+
PaymentReferenceKind["Standard"] = "Standard";
|
|
8
|
+
/** Paid in full by a coupon. */
|
|
9
|
+
PaymentReferenceKind["Coupon"] = "Coupon";
|
|
10
|
+
/** The coupon's share of a purchase split between a coupon and another payment. */
|
|
11
|
+
PaymentReferenceKind["CouponPart"] = "CouponPart";
|
|
12
|
+
/** What the user paid on top of the coupon in a split purchase. */
|
|
13
|
+
PaymentReferenceKind["Remainder"] = "Remainder";
|
|
14
|
+
})(PaymentReferenceKind || (PaymentReferenceKind = {}));
|
|
15
|
+
// The backend puts the order id in the low 12 bytes, leaving the high 20 free for a tag.
|
|
16
|
+
// Every coupon tag starts with 0xfee0ff ("fee off") so it stands out on an explorer.
|
|
17
|
+
const TAG_SIZE = 20;
|
|
18
|
+
const TAGS = {
|
|
19
|
+
[PaymentReferenceKind.Standard]: pad("0x", { size: TAG_SIZE }),
|
|
20
|
+
[PaymentReferenceKind.Coupon]: pad("0xfee0ff", { size: TAG_SIZE, dir: "right" }),
|
|
21
|
+
[PaymentReferenceKind.CouponPart]: pad("0xfee0ffc0de", { size: TAG_SIZE, dir: "right" }),
|
|
22
|
+
[PaymentReferenceKind.Remainder]: pad("0xfee0ffba1a5ce0", { size: TAG_SIZE, dir: "right" }),
|
|
23
|
+
};
|
|
24
|
+
// Lowercased so a checksum-style or uppercase input compares equal to the tags above.
|
|
25
|
+
const _splitReference = (reference) => {
|
|
26
|
+
if (!isHex(reference, { strict: true }) || size(reference) !== 32) {
|
|
27
|
+
throw new InvalidPaymentReferenceError(reference, "is not 32 bytes of hex");
|
|
28
|
+
}
|
|
29
|
+
const lower = reference.toLowerCase();
|
|
30
|
+
const order = slice(lower, TAG_SIZE);
|
|
31
|
+
// The contracts refuse a zero reference, and a tag would hide an empty order id from that check.
|
|
32
|
+
if (BigInt(order) === 0n)
|
|
33
|
+
throw new InvalidPaymentReferenceError(reference, "has an empty order part");
|
|
34
|
+
return { tag: slice(lower, 0, TAG_SIZE), order };
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Writes `kind`'s tag into the high 20 bytes of `reference`, which must be zero
|
|
38
|
+
* there. Tag both halves of a split purchase from the same reference, so each
|
|
39
|
+
* spends once onchain and both still point at the same order.
|
|
40
|
+
*/
|
|
41
|
+
export const _tagPaymentReference = (reference, kind) => {
|
|
42
|
+
const { tag, order } = _splitReference(reference);
|
|
43
|
+
if (tag !== TAGS[PaymentReferenceKind.Standard]) {
|
|
44
|
+
throw new InvalidPaymentReferenceError(reference, "is already tagged");
|
|
45
|
+
}
|
|
46
|
+
return concat([TAGS[kind], order]);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Reads back how a purchase was paid for, and the untagged reference it was
|
|
50
|
+
* built from. Throws on a tag this SDK did not write.
|
|
51
|
+
*/
|
|
52
|
+
export const _parsePaymentReference = (reference) => {
|
|
53
|
+
const { tag, order } = _splitReference(reference);
|
|
54
|
+
const kind = Object.keys(TAGS).find((k) => TAGS[k] === tag);
|
|
55
|
+
if (!kind)
|
|
56
|
+
throw new InvalidPaymentReferenceError(reference, "has an unknown tag");
|
|
57
|
+
return { kind, reference: pad(order, { size: 32 }) };
|
|
58
|
+
};
|
package/dist/esm/logic/errors.js
CHANGED
|
@@ -228,6 +228,30 @@ export class PriceOutOfRangeError extends KokioError {
|
|
|
228
228
|
this.priceUSDCents = priceUSDCents;
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* A payment reference that is not 32 bytes, has an empty order part, or
|
|
233
|
+
* carries a tag this SDK cannot write over or read back.
|
|
234
|
+
*/
|
|
235
|
+
export class InvalidPaymentReferenceError extends KokioError {
|
|
236
|
+
reference;
|
|
237
|
+
constructor(reference, reason) {
|
|
238
|
+
super("INVALID_PAYMENT_REFERENCE", `Payment reference "${reference}" ${reason}.`);
|
|
239
|
+
this.reference = reference;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* A coupon that covers none or all of the price. Neither is a split: a coupon
|
|
244
|
+
* covering the whole price is a full-coupon order with a single reference.
|
|
245
|
+
*/
|
|
246
|
+
export class CouponSplitOutOfRangeError extends KokioError {
|
|
247
|
+
couponUSDCents;
|
|
248
|
+
priceUSDCents;
|
|
249
|
+
constructor(couponUSDCents, priceUSDCents) {
|
|
250
|
+
super("COUPON_SPLIT_OUT_OF_RANGE", `A coupon of ${couponUSDCents} cents does not split a price of ${priceUSDCents} cents.`);
|
|
251
|
+
this.couponUSDCents = couponUSDCents;
|
|
252
|
+
this.priceUSDCents = priceUSDCents;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
231
255
|
// Every ABI that can surface a custom error from an on-chain revert. viem's
|
|
232
256
|
// `decodeErrorResult` walks each ABI's `error` fragments to match the 4-byte
|
|
233
257
|
// selector in the revert data.
|
|
@@ -10,9 +10,10 @@ 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
|
+
export { KokioError, NullOrUndefinedValueError, MissingSmartWalletError, MissingEOAWalletError, InvalidClientError, UnsupportedChainError, UnconfiguredChainError, CounterfactualMismatchError, BatchSizeOutOfRangeError, DepositOnResumeError, ESIMWalletNotLazyDeployedError, MissingBatchEventError, StalledBatchError, InvalidAddressError, InvalidSymbolError, TokenNotAcceptedError, NotAProtocolESIMWalletError, UnknownTransactionError, TransactionRevertedError, ReceiptNotCanonicalError, NotAnERC20TokenError, UnmatchedPaymentEventsError, PriceOutOfRangeError, InvalidPaymentReferenceError, CouponSplitOutOfRangeError, ContractRevertError, decodeContractRevert, } from "../logic/errors.js";
|
|
14
14
|
export type { DecodedRevert } from "../logic/errors.js";
|
|
15
15
|
export { OperationState } from "../logic/admin/reads/protocolAdmin.reads.js";
|
|
16
|
+
export { PaymentReferenceKind } from "../logic/admin/utils/paymentReference.js";
|
|
16
17
|
/**
|
|
17
18
|
* EOA-only entry point for the NodeJS backend.
|
|
18
19
|
*
|
|
@@ -10,7 +10,19 @@ export declare class AdminRegistrySubPackage {
|
|
|
10
10
|
disableAdmin(): Promise<`0x${string}`>;
|
|
11
11
|
enableAdmin(): Promise<`0x${string}`>;
|
|
12
12
|
acceptAdminUpdate(): Promise<`0x${string}`>;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Records a purchase paid for by card or an external wallet, or one line of
|
|
15
|
+
* a coupon purchase. One transaction.
|
|
16
|
+
*/
|
|
17
|
+
recordSettledPurchase(eSIMWalletAddress: Address, dataBundleDetail: DataBundleDetails, asset: Hex, tokenAmount: bigint, paymentReference: Hex): Promise<Hex>;
|
|
18
|
+
/**
|
|
19
|
+
* Records a purchase split between a coupon and a card or external wallet
|
|
20
|
+
* payment, as two lines. `dataBundleDetail` carries the full price and how
|
|
21
|
+
* the remainder was paid; `asset` and `tokenAmount` are what the user paid
|
|
22
|
+
* for the remainder. Lines already recorded are skipped. Returns the hash
|
|
23
|
+
* of the last transaction sent; any earlier one is already mined.
|
|
24
|
+
*/
|
|
25
|
+
recordSettledPurchase(eSIMWalletAddress: Address, dataBundleDetail: DataBundleDetails, asset: Hex, tokenAmount: bigint, paymentReferences: readonly [couponRef: Hex, remainderRef: Hex], couponUSDCents: bigint): Promise<Hex>;
|
|
14
26
|
assignESIMIdentifier(eSIMWalletAddress: Address, eSIMUniqueIdentifier: string): Promise<`0x${string}`>;
|
|
15
27
|
pause(): Promise<`0x${string}`>;
|
|
16
28
|
unpause(): Promise<`0x${string}`>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Address, Hash, TransactionReceipt, WalletClient } from "viem";
|
|
1
|
+
import { Address, Hash, Hex, TransactionReceipt, WalletClient } from "viem";
|
|
2
|
+
import { PaymentReferenceKind } from "../../logic/admin/utils/paymentReference.js";
|
|
2
3
|
/**
|
|
3
|
-
* Checks what a mined transaction actually paid
|
|
4
|
-
* when it came from a user: a receipt
|
|
5
|
-
* your own node.
|
|
4
|
+
* Checks what a mined transaction actually paid, and builds and reads payment
|
|
5
|
+
* references. Nothing is sent. Pass a hash when it came from a user: a receipt
|
|
6
|
+
* is used as given, so it must come from your own node.
|
|
6
7
|
*/
|
|
7
8
|
export declare class AdminUtilsSubPackage {
|
|
8
9
|
walletClient: WalletClient;
|
|
@@ -18,4 +19,18 @@ export declare class AdminUtilsSubPackage {
|
|
|
18
19
|
* one token as one dollar, so they mean nothing for a token that is not.
|
|
19
20
|
*/
|
|
20
21
|
verifyERC20Transfer(transaction: Hash | TransactionReceipt, token: Address, sender: Address, destination: Address): Promise<import("../../types.js").ERC20TransferCheck>;
|
|
22
|
+
/**
|
|
23
|
+
* Marks `reference` with how the purchase was paid for. Its high 20 bytes
|
|
24
|
+
* must be zero. For a split purchase, tag the same reference once as
|
|
25
|
+
* `CouponPart` and once as `Remainder`.
|
|
26
|
+
*/
|
|
27
|
+
tagPaymentReference(reference: Hex, kind: PaymentReferenceKind): `0x${string}`;
|
|
28
|
+
/**
|
|
29
|
+
* How a purchase was paid for, and the untagged reference it was built
|
|
30
|
+
* from. Throws on a tag this SDK did not write.
|
|
31
|
+
*/
|
|
32
|
+
parsePaymentReference(reference: Hex): {
|
|
33
|
+
kind: PaymentReferenceKind;
|
|
34
|
+
reference: Hex;
|
|
35
|
+
};
|
|
21
36
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Address, Hex, WalletClient } from "viem";
|
|
2
|
-
import type
|
|
2
|
+
import { type DataBundleDetails, type OwnerCall } from "../../types.js";
|
|
3
3
|
/** Wire (or rewire) the LazyWalletRegistry into the Registry. `onlyOwner`. */
|
|
4
4
|
export declare const _addOrUpdateLazyWalletRegistryAddress: (client: WalletClient, lazyWalletRegistry: Address) => Promise<`0x${string}`>;
|
|
5
5
|
/** Update the vault that receives eSIM payments. `onlyOwner`. */
|
|
@@ -72,8 +72,29 @@ export declare const _assignESIMIdentifier: (client: WalletClient, eSIMWalletAdd
|
|
|
72
72
|
* never sees a transfer to prove it), and `_tokenAmount` is recorded for
|
|
73
73
|
* offchain matching but never checked against `_dataBundleDetail.priceUSDCents`.
|
|
74
74
|
* `_paymentReference` is spendable once per eSIM wallet.
|
|
75
|
+
*
|
|
76
|
+
* Given `[couponRef, remainderRef]` and `couponUSDCents`, records a purchase
|
|
77
|
+
* split between a coupon and another payment as two lines, see
|
|
78
|
+
* {@link _couponSplitLines}. Lines already recorded are skipped, so a retry
|
|
79
|
+
* after a partial failure sends only what is missing. Returns the last hash
|
|
80
|
+
* sent; any earlier one is already mined.
|
|
81
|
+
*/
|
|
82
|
+
export declare const _recordSettledPurchase: (client: WalletClient, eSIMWalletAddress: Address, dataBundleDetail: DataBundleDetails, asset: Hex, tokenAmount: bigint, paymentReference: Hex | readonly [Hex, Hex], couponUSDCents?: bigint) => Promise<Hex>;
|
|
83
|
+
/** One `recordSettledPurchase` call's worth of arguments, past the eSIM wallet. */
|
|
84
|
+
export interface SettledLine {
|
|
85
|
+
dataBundleDetail: DataBundleDetails;
|
|
86
|
+
asset: Hex;
|
|
87
|
+
tokenAmount: bigint;
|
|
88
|
+
paymentReference: Hex;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Splits one purchase into its remainder line and its coupon line, in that
|
|
92
|
+
* order. `dataBundleDetail` carries the full price and how the remainder was
|
|
93
|
+
* paid; `asset` and `tokenAmount` are what the user paid for the remainder.
|
|
94
|
+
* The coupon line is always `Fiat` in `USD`, with the coupon's cents as its
|
|
95
|
+
* token amount.
|
|
75
96
|
*/
|
|
76
|
-
export declare const
|
|
97
|
+
export declare const _couponSplitLines: (dataBundleDetail: DataBundleDetails, asset: Hex, tokenAmount: bigint, [couponRef, remainderRef]: readonly [Hex, Hex], couponUSDCents: bigint | undefined) => [remainder: SettledLine, coupon: SettledLine];
|
|
77
98
|
/**
|
|
78
99
|
* Step 2 of the 2-step admin handover: the nominee accepts. The chain requires
|
|
79
100
|
* `msg.sender` to equal the pending admin, so the `client` here must be the
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Hex } from "viem";
|
|
2
|
+
/** How a purchase was paid for, as written into its payment reference. */
|
|
3
|
+
export declare enum PaymentReferenceKind {
|
|
4
|
+
/** Paid in full without a coupon. Carries no tag. */
|
|
5
|
+
Standard = "Standard",
|
|
6
|
+
/** Paid in full by a coupon. */
|
|
7
|
+
Coupon = "Coupon",
|
|
8
|
+
/** The coupon's share of a purchase split between a coupon and another payment. */
|
|
9
|
+
CouponPart = "CouponPart",
|
|
10
|
+
/** What the user paid on top of the coupon in a split purchase. */
|
|
11
|
+
Remainder = "Remainder"
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Writes `kind`'s tag into the high 20 bytes of `reference`, which must be zero
|
|
15
|
+
* there. Tag both halves of a split purchase from the same reference, so each
|
|
16
|
+
* spends once onchain and both still point at the same order.
|
|
17
|
+
*/
|
|
18
|
+
export declare const _tagPaymentReference: (reference: Hex, kind: PaymentReferenceKind) => Hex;
|
|
19
|
+
/**
|
|
20
|
+
* Reads back how a purchase was paid for, and the untagged reference it was
|
|
21
|
+
* built from. Throws on a tag this SDK did not write.
|
|
22
|
+
*/
|
|
23
|
+
export declare const _parsePaymentReference: (reference: Hex) => {
|
|
24
|
+
kind: PaymentReferenceKind;
|
|
25
|
+
reference: Hex;
|
|
26
|
+
};
|
|
@@ -149,6 +149,23 @@ export declare class PriceOutOfRangeError extends KokioError {
|
|
|
149
149
|
readonly priceUSDCents: bigint;
|
|
150
150
|
constructor(priceUSDCents: bigint);
|
|
151
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* A payment reference that is not 32 bytes, has an empty order part, or
|
|
154
|
+
* carries a tag this SDK cannot write over or read back.
|
|
155
|
+
*/
|
|
156
|
+
export declare class InvalidPaymentReferenceError extends KokioError {
|
|
157
|
+
readonly reference: string;
|
|
158
|
+
constructor(reference: string, reason: string);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A coupon that covers none or all of the price. Neither is a split: a coupon
|
|
162
|
+
* covering the whole price is a full-coupon order with a single reference.
|
|
163
|
+
*/
|
|
164
|
+
export declare class CouponSplitOutOfRangeError extends KokioError {
|
|
165
|
+
readonly couponUSDCents?: bigint;
|
|
166
|
+
readonly priceUSDCents: bigint;
|
|
167
|
+
constructor(couponUSDCents: bigint | undefined, priceUSDCents: bigint);
|
|
168
|
+
}
|
|
152
169
|
export interface DecodedRevert {
|
|
153
170
|
errorName: string;
|
|
154
171
|
args: readonly unknown[];
|