@mystars-tg/faas-wallet 0.1.2

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.
@@ -0,0 +1,327 @@
1
+ import * as _ton_core from '@ton/core';
2
+ import { MessageRelaxed, SendMode, StateInit, Cell } from '@ton/core';
3
+ import { KeyPair } from '@ton/crypto';
4
+ import { PaymentInstruction, CreateOrderResult, CreateOrderParams, CreateOrderOptions, WaitForOrderOptions, Order } from '@mystars-tg/faas-sdk';
5
+
6
+ /**
7
+ * The minimal TON RPC surface the wallet + payer need. Inject a custom one for
8
+ * tests (so nothing touches the network or moves funds); use `ToncenterRpc` in
9
+ * production.
10
+ */
11
+ /**
12
+ * The minimal TON RPC surface the wallet + payer depend on. Implement it (or
13
+ * use {@link ToncenterRpc}) to read balances/seqno, resolve jetton wallets, and
14
+ * broadcast. Inject a mock in tests so nothing touches the network or moves funds.
15
+ */
16
+ interface TonRpc {
17
+ /** Account TON balance, in nanoTON. */
18
+ getBalance(address: string): Promise<bigint>;
19
+ /** The wallet's current seqno (0 if the wallet contract isn't deployed yet). */
20
+ getSeqno(address: string): Promise<number>;
21
+ /** Resolve an owner's jetton wallet address from the jetton master (get_wallet_address). */
22
+ resolveJettonWallet(owner: string, jettonMaster: string): Promise<string>;
23
+ /** A jetton wallet's token balance, in the jetton's smallest unit (get_wallet_data). */
24
+ getJettonBalance(jettonWallet: string): Promise<bigint>;
25
+ /** Broadcast a serialized external-message BoC. */
26
+ sendBoc(boc: Uint8Array): Promise<void>;
27
+ }
28
+ /** Options for {@link ToncenterRpc}. */
29
+ interface ToncenterRpcOptions {
30
+ /** JSON-RPC endpoint, e.g. `https://toncenter.com/api/v2/jsonRPC` (or a testnet URL). */
31
+ endpoint: string;
32
+ /** toncenter API key (recommended to avoid tight rate limits). */
33
+ apiKey?: string;
34
+ }
35
+ /**
36
+ * Default {@link TonRpc} backed by toncenter via `@ton/ton`'s `TonClient`.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const rpc = new ToncenterRpc({
41
+ * endpoint: "https://toncenter.com/api/v2/jsonRPC",
42
+ * apiKey: process.env.TONCENTER_KEY,
43
+ * });
44
+ * ```
45
+ */
46
+ declare class ToncenterRpc implements TonRpc {
47
+ private readonly client;
48
+ /** @param opts - the {@link ToncenterRpcOptions} (endpoint + optional API key) */
49
+ constructor(opts: ToncenterRpcOptions);
50
+ /** {@inheritDoc TonRpc.getBalance} */
51
+ getBalance(address: string): Promise<bigint>;
52
+ /** {@inheritDoc TonRpc.getSeqno} */
53
+ getSeqno(address: string): Promise<number>;
54
+ /** {@inheritDoc TonRpc.resolveJettonWallet} */
55
+ resolveJettonWallet(owner: string, jettonMaster: string): Promise<string>;
56
+ /** {@inheritDoc TonRpc.getJettonBalance} */
57
+ getJettonBalance(jettonWallet: string): Promise<bigint>;
58
+ /** {@inheritDoc TonRpc.sendBoc} */
59
+ sendBoc(boc: Uint8Array): Promise<void>;
60
+ }
61
+
62
+ /** Base error for the wallet module. */
63
+ declare class WalletError extends Error {
64
+ constructor(message: string);
65
+ }
66
+ /** The wallet doesn't hold enough TON / jetton balance to cover a payment. */
67
+ declare class InsufficientBalanceError extends WalletError {
68
+ }
69
+ /** Arguments for {@link TonWallet.createTransfer} — the unsigned pieces of a wallet-v4 transfer. */
70
+ interface CreateTransferArgs {
71
+ /** The wallet's current seqno (fetch via {@link TonWallet.getSeqno}); 0 deploys the contract. */
72
+ seqno: number;
73
+ /** The internal messages to send in this transfer. */
74
+ messages: MessageRelaxed[];
75
+ /** The send mode flags (`@ton/core` `SendMode`). */
76
+ sendMode: SendMode;
77
+ /** Transfer validity window as a unix-seconds expiry; the wallet rejects it after this. */
78
+ timeout?: number;
79
+ }
80
+ /**
81
+ * An in-memory TON wallet (WalletContractV4). Construct via {@link TonWallet.generate} /
82
+ * {@link TonWallet.fromMnemonic} / {@link TonWallet.fromKeyPair}. Keys never leave memory and are
83
+ * redacted from serialization. See the module overview for the custody stance.
84
+ */
85
+ declare class TonWallet {
86
+ /** The in-memory key pair. Treat `secretKey` as a secret — never log or persist it. */
87
+ readonly keyPair: KeyPair;
88
+ private readonly contract;
89
+ private constructor();
90
+ /** Redacted serialization — NEVER exposes the secret key. */
91
+ toJSON(): {
92
+ address: string;
93
+ publicKey: string;
94
+ };
95
+ /**
96
+ * Generate a NEW wallet. Returns the 24-word mnemonic ONCE — store it securely;
97
+ * it is never persisted.
98
+ *
99
+ * @returns the in-memory `wallet` and its `mnemonic` (the only time you see it)
100
+ * @example
101
+ * ```ts
102
+ * const { wallet, mnemonic } = await TonWallet.generate();
103
+ * await secureVault.store(mnemonic); // YOUR job — it is never written to disk by the SDK
104
+ * console.log("fund this:", wallet.address);
105
+ * ```
106
+ */
107
+ static generate(): Promise<{
108
+ wallet: TonWallet;
109
+ mnemonic: string[];
110
+ }>;
111
+ /** Import a wallet from its 24-word mnemonic. */
112
+ static fromMnemonic(words: string[]): Promise<TonWallet>;
113
+ /** Import a wallet from a raw Ed25519 key pair. */
114
+ static fromKeyPair(publicKey: Uint8Array, secretKey: Uint8Array): TonWallet;
115
+ /** Friendly, non-bounceable (`UQ…`) address — FUND THIS to pay invoices. */
116
+ get address(): string;
117
+ /** Raw `0:hex` address form. */
118
+ get rawAddress(): string;
119
+ /** @internal The contract's StateInit, needed for the first (deploying) transfer. */
120
+ get init(): StateInit;
121
+ /** @internal The contract address as a TON `Address`. */
122
+ get tonAddress(): _ton_core.Address;
123
+ /** @internal Sign a transfer body. */
124
+ createTransfer(args: CreateTransferArgs): Cell;
125
+ /**
126
+ * This wallet's native TON balance.
127
+ *
128
+ * @param rpc - the RPC to query through
129
+ * @returns the balance in nanoTON
130
+ */
131
+ getBalance(rpc: TonRpc): Promise<bigint>;
132
+ /**
133
+ * This wallet's current sequence number.
134
+ *
135
+ * @param rpc - the RPC to query through
136
+ * @returns the seqno (`0` when the wallet contract isn't deployed yet)
137
+ */
138
+ getSeqno(rpc: TonRpc): Promise<number>;
139
+ /**
140
+ * This wallet's balance of a given jetton (e.g. USDT).
141
+ *
142
+ * @param rpc - the RPC to query through
143
+ * @param jettonMaster - the jetton master address (e.g. `DEFAULT_USDT_MASTER` from `@mystars-tg/faas-wallet`)
144
+ * @returns the token balance in the jetton's smallest unit (micro-USDT for USDT)
145
+ */
146
+ getJettonBalance(rpc: TonRpc, jettonMaster: string): Promise<bigint>;
147
+ }
148
+
149
+ /**
150
+ * `OrderPayer` — pay a FaaS order invoice from your own `TonWallet`.
151
+ *
152
+ * Signs ONCE and broadcasts. For `ton` it sends an exact-amount transfer with the
153
+ * order memo as an op-0 comment; for `usdt_ton` it resolves the payer's own USDT
154
+ * jetton wallet and sends a TEP-74 transfer whose `destination` is the FaaS
155
+ * `pay_to_address` and whose forward payload carries the memo.
156
+ *
157
+ * NOTE: this moves the PARTNER's own funds from the partner's own wallet — never
158
+ * the MyStars treasury. Tests use a mock `TonRpc` so nothing broadcasts.
159
+ */
160
+
161
+ /** Mainnet Tether USDT jetton master on TON. Override via `PayOrderOptions.jettonMaster` for testnet. */
162
+ declare const DEFAULT_USDT_MASTER = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";
163
+ /** TON attached to a USDT jetton transfer to cover its gas (excess refunds to the sender). Matches the core invoice builder. */
164
+ declare const DEFAULT_JETTON_GAS_TON = "0.05";
165
+ /** Options for {@link OrderPayer.payOrder} / {@link OrderPayer.planMessages}. */
166
+ interface PayOrderOptions {
167
+ /** The RPC used to resolve the jetton wallet, read balances, and broadcast. */
168
+ rpc: TonRpc;
169
+ /** USDT jetton master (defaults to mainnet Tether). */
170
+ jettonMaster?: string;
171
+ /** TON gas attached to a USDT transfer (default 0.05). */
172
+ jettonGasTon?: string;
173
+ /** Transfer validity window in seconds (default 120 — a non-landed send dies fast, safe to retry). */
174
+ validForSeconds?: number;
175
+ /** Skip the pre-sign balance check (saves 1-2 RPC calls; default false). */
176
+ skipBalanceCheck?: boolean;
177
+ /** Injectable clock (epoch ms) for deterministic tests. */
178
+ now?: number;
179
+ }
180
+ /** The outcome of a broadcast payment from {@link OrderPayer.payOrder}. */
181
+ interface PayOrderResult {
182
+ /** The order id paid. */
183
+ orderId?: string;
184
+ /** The paying wallet address. */
185
+ from: string;
186
+ /** The recipient (FaaS treasury owner) address. */
187
+ to: string;
188
+ /** The smallest-unit amount sent (nanoTON for ton, micro-USDT for usdt_ton). */
189
+ amountSmallestUnit: string;
190
+ }
191
+ /** An internal message the payer will sign into a transfer. Exposed for testing. */
192
+ interface PlannedMessage {
193
+ to: string;
194
+ value: bigint;
195
+ body: Cell;
196
+ bounce: boolean;
197
+ }
198
+ /**
199
+ * Pays a FaaS order invoice from a single {@link TonWallet}.
200
+ *
201
+ * @example
202
+ * ```ts
203
+ * const order = await client.createOrder(
204
+ * { type: "stars", recipient: { username: "durov" }, quantity: 100 },
205
+ * { idempotencyKey: `order-${myId}` },
206
+ * );
207
+ * const rpc = new ToncenterRpc({ endpoint: "https://toncenter.com/api/v2/jsonRPC" });
208
+ * const { from, to, amountSmallestUnit } = await new OrderPayer(wallet).payOrder(order, { rpc });
209
+ * const finished = await client.waitForOrder(order.order_id);
210
+ * ```
211
+ */
212
+ declare class OrderPayer {
213
+ private readonly wallet;
214
+ /** @param wallet - the funded {@link TonWallet} whose own funds will pay invoices */
215
+ constructor(wallet: TonWallet);
216
+ /**
217
+ * Plan the internal message(s) for an order WITHOUT signing or broadcasting.
218
+ * `ton` is pure; `usdt_ton` resolves the payer's jetton wallet via `rpc`.
219
+ */
220
+ planMessages(payment: PaymentInstruction, opts: PayOrderOptions): Promise<PlannedMessage[]>;
221
+ /** Throw `InsufficientBalanceError` if the wallet can't cover the planned payment + gas. */
222
+ private assertFunded;
223
+ /**
224
+ * Build, sign, and broadcast the payment for an order. Signs exactly ONCE.
225
+ *
226
+ * MONEY: this broadcasts a real on-chain transfer of the wallet's OWN funds.
227
+ * It is not idempotent — calling it twice for the same order pays twice. Guard
228
+ * retries at the order layer (a stable `Idempotency-Key` on `createOrder`, or
229
+ * use `fulfill` which only pays an order still `awaiting_payment`).
230
+ *
231
+ * @param order - a `CreateOrderResult`, or any object carrying a `payment` block (and optional `order_id`)
232
+ * @param opts - the {@link PayOrderOptions} (at minimum `rpc`)
233
+ * @returns the {@link PayOrderResult} — `from`/`to` addresses and the smallest-unit amount sent
234
+ * @throws `WalletError` if the order's `payment` block is missing `pay_to_address`/`memo`
235
+ * @throws `MyStarsValidationError` if the amount is non-positive
236
+ * @throws {@link InsufficientBalanceError} if the wallet can't cover the payment + gas (unless `skipBalanceCheck`)
237
+ */
238
+ payOrder(order: CreateOrderResult | {
239
+ payment: PaymentInstruction;
240
+ order_id?: string;
241
+ }, opts: PayOrderOptions): Promise<PayOrderResult>;
242
+ }
243
+
244
+ /**
245
+ * `fulfill()` — the one-call convenience: create an order, pay it from your
246
+ * wallet, and wait until it's delivered (or failed/reversed/expired).
247
+ *
248
+ * RETRY HAZARD (money): this broadcasts a REAL payment. A naive retry of a
249
+ * `fulfill` that already broadcast would create a SECOND order and pay it AGAIN.
250
+ * Two safeguards make retries safe:
251
+ * 1. A stable `idempotencyKey` is REQUIRED — re-running `fulfill` with the same
252
+ * key returns the SAME order from the server (idempotent replay) instead of
253
+ * minting a duplicate.
254
+ * 2. We only broadcast when the (possibly replayed) order is still
255
+ * `awaiting_payment`. If the replay shows the order already advanced
256
+ * (`paid`/`delivered`/…), we SKIP the payment and just wait — so a retry
257
+ * never double-pays an order whose first payment already landed.
258
+ * If anything throws after the order exists, the error carries `order_id` so you
259
+ * re-attach via `getOrder`/`waitForOrder` instead of re-paying.
260
+ */
261
+
262
+ /** The slice of `MyStarsClient` `fulfill` needs (structurally typed so it's easy to mock). */
263
+ interface FulfillClient {
264
+ createOrder(params: CreateOrderParams, opts?: CreateOrderOptions): Promise<CreateOrderResult>;
265
+ waitForOrder(orderId: string, opts?: WaitForOrderOptions): Promise<Order>;
266
+ }
267
+ /** Options for {@link fulfill} — the {@link PayOrderOptions} (e.g. `rpc`) plus order-create/wait wiring. */
268
+ interface FulfillOptions extends PayOrderOptions {
269
+ /**
270
+ * REQUIRED stable idempotency key — derive it from YOUR OWN order id so a retry
271
+ * of `fulfill` reuses the same key and the server returns the same order instead
272
+ * of creating a duplicate. May also be supplied via `createOptions.idempotencyKey`.
273
+ */
274
+ idempotencyKey?: string;
275
+ /** Options forwarded to `createOrder` (e.g. a caller-supplied idempotency key). */
276
+ createOptions?: CreateOrderOptions;
277
+ /** Options forwarded to `waitForOrder`. */
278
+ wait?: WaitForOrderOptions;
279
+ }
280
+ /**
281
+ * An error thrown AFTER the order was created carries the `order_id` so you can
282
+ * re-attach (`getOrder`/`waitForOrder`) instead of re-running `fulfill` (which
283
+ * would re-pay). The error may be any class (a `MyStarsApiError` from the wait, a
284
+ * `WalletError` from the payer, …), so the id rides as an optional property —
285
+ * read it with the type-safe {@link orderIdFromError} accessor.
286
+ */
287
+ type ErrorWithOrderId = Error & {
288
+ order_id?: string;
289
+ };
290
+ /**
291
+ * Read the `order_id` that `fulfill` attaches to a post-create failure. Returns
292
+ * `undefined` when the error carries none. Use it to recover safely after a
293
+ * `fulfill` throw — re-attach with `client.waitForOrder(id)` / `getOrder(id)`
294
+ * rather than re-running `fulfill` (which would broadcast a second payment).
295
+ */
296
+ declare function orderIdFromError(err: unknown): string | undefined;
297
+ /**
298
+ * create → pay → wait-until-terminal, in one call.
299
+ *
300
+ * Creates the order with the required stable `idempotencyKey`, broadcasts the
301
+ * payment from `wallet` ONLY if the (possibly idempotent-replayed) order is still
302
+ * `awaiting_payment`, then polls until the order is terminal. See the module note
303
+ * for the money-retry safeguards.
304
+ *
305
+ * @param client - the order-layer client (a `MyStarsClient`, or any {@link FulfillClient})
306
+ * @param wallet - the funded {@link TonWallet} that pays the invoice
307
+ * @param params - the order to create (`CreateOrderParams`)
308
+ * @param opts - {@link FulfillOptions} — MUST include a stable `idempotencyKey` (or `createOptions.idempotencyKey`) plus `rpc`
309
+ * @returns the final {@link Order} (terminal status — `delivered`/`failed`/`reversed`/`expired`)
310
+ * @throws `MyStarsValidationError` if no stable `idempotencyKey` was supplied
311
+ * @throws `RecipientIneligibleError` (422) if the recipient cannot receive the item (no order created)
312
+ * @throws `InsufficientBalanceError` if the wallet can't cover the payment
313
+ * @throws `OrderWaitTimeoutError` if the order doesn't finish before the wait deadline
314
+ * @throws `MyStarsApiError` on other API failures — after the order exists the thrown error carries `order_id` (read via {@link orderIdFromError}); re-attach with `client.waitForOrder(orderId)` instead of re-running `fulfill`
315
+ * @example
316
+ * ```ts
317
+ * const order = await fulfill(
318
+ * client, wallet,
319
+ * { type: "stars", recipient: { username: "durov" }, quantity: 100 },
320
+ * { rpc, idempotencyKey: `order-${myOrderId}` },
321
+ * );
322
+ * if (order.status !== "delivered") console.warn("not delivered:", order.failure_reason);
323
+ * ```
324
+ */
325
+ declare function fulfill(client: FulfillClient, wallet: TonWallet, params: CreateOrderParams, opts: FulfillOptions): Promise<Order>;
326
+
327
+ export { type CreateTransferArgs, DEFAULT_JETTON_GAS_TON, DEFAULT_USDT_MASTER, type ErrorWithOrderId, type FulfillClient, type FulfillOptions, InsufficientBalanceError, OrderPayer, type PayOrderOptions, type PayOrderResult, type PlannedMessage, type TonRpc, TonWallet, ToncenterRpc, type ToncenterRpcOptions, WalletError, fulfill, orderIdFromError };
@@ -0,0 +1,327 @@
1
+ import * as _ton_core from '@ton/core';
2
+ import { MessageRelaxed, SendMode, StateInit, Cell } from '@ton/core';
3
+ import { KeyPair } from '@ton/crypto';
4
+ import { PaymentInstruction, CreateOrderResult, CreateOrderParams, CreateOrderOptions, WaitForOrderOptions, Order } from '@mystars-tg/faas-sdk';
5
+
6
+ /**
7
+ * The minimal TON RPC surface the wallet + payer need. Inject a custom one for
8
+ * tests (so nothing touches the network or moves funds); use `ToncenterRpc` in
9
+ * production.
10
+ */
11
+ /**
12
+ * The minimal TON RPC surface the wallet + payer depend on. Implement it (or
13
+ * use {@link ToncenterRpc}) to read balances/seqno, resolve jetton wallets, and
14
+ * broadcast. Inject a mock in tests so nothing touches the network or moves funds.
15
+ */
16
+ interface TonRpc {
17
+ /** Account TON balance, in nanoTON. */
18
+ getBalance(address: string): Promise<bigint>;
19
+ /** The wallet's current seqno (0 if the wallet contract isn't deployed yet). */
20
+ getSeqno(address: string): Promise<number>;
21
+ /** Resolve an owner's jetton wallet address from the jetton master (get_wallet_address). */
22
+ resolveJettonWallet(owner: string, jettonMaster: string): Promise<string>;
23
+ /** A jetton wallet's token balance, in the jetton's smallest unit (get_wallet_data). */
24
+ getJettonBalance(jettonWallet: string): Promise<bigint>;
25
+ /** Broadcast a serialized external-message BoC. */
26
+ sendBoc(boc: Uint8Array): Promise<void>;
27
+ }
28
+ /** Options for {@link ToncenterRpc}. */
29
+ interface ToncenterRpcOptions {
30
+ /** JSON-RPC endpoint, e.g. `https://toncenter.com/api/v2/jsonRPC` (or a testnet URL). */
31
+ endpoint: string;
32
+ /** toncenter API key (recommended to avoid tight rate limits). */
33
+ apiKey?: string;
34
+ }
35
+ /**
36
+ * Default {@link TonRpc} backed by toncenter via `@ton/ton`'s `TonClient`.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const rpc = new ToncenterRpc({
41
+ * endpoint: "https://toncenter.com/api/v2/jsonRPC",
42
+ * apiKey: process.env.TONCENTER_KEY,
43
+ * });
44
+ * ```
45
+ */
46
+ declare class ToncenterRpc implements TonRpc {
47
+ private readonly client;
48
+ /** @param opts - the {@link ToncenterRpcOptions} (endpoint + optional API key) */
49
+ constructor(opts: ToncenterRpcOptions);
50
+ /** {@inheritDoc TonRpc.getBalance} */
51
+ getBalance(address: string): Promise<bigint>;
52
+ /** {@inheritDoc TonRpc.getSeqno} */
53
+ getSeqno(address: string): Promise<number>;
54
+ /** {@inheritDoc TonRpc.resolveJettonWallet} */
55
+ resolveJettonWallet(owner: string, jettonMaster: string): Promise<string>;
56
+ /** {@inheritDoc TonRpc.getJettonBalance} */
57
+ getJettonBalance(jettonWallet: string): Promise<bigint>;
58
+ /** {@inheritDoc TonRpc.sendBoc} */
59
+ sendBoc(boc: Uint8Array): Promise<void>;
60
+ }
61
+
62
+ /** Base error for the wallet module. */
63
+ declare class WalletError extends Error {
64
+ constructor(message: string);
65
+ }
66
+ /** The wallet doesn't hold enough TON / jetton balance to cover a payment. */
67
+ declare class InsufficientBalanceError extends WalletError {
68
+ }
69
+ /** Arguments for {@link TonWallet.createTransfer} — the unsigned pieces of a wallet-v4 transfer. */
70
+ interface CreateTransferArgs {
71
+ /** The wallet's current seqno (fetch via {@link TonWallet.getSeqno}); 0 deploys the contract. */
72
+ seqno: number;
73
+ /** The internal messages to send in this transfer. */
74
+ messages: MessageRelaxed[];
75
+ /** The send mode flags (`@ton/core` `SendMode`). */
76
+ sendMode: SendMode;
77
+ /** Transfer validity window as a unix-seconds expiry; the wallet rejects it after this. */
78
+ timeout?: number;
79
+ }
80
+ /**
81
+ * An in-memory TON wallet (WalletContractV4). Construct via {@link TonWallet.generate} /
82
+ * {@link TonWallet.fromMnemonic} / {@link TonWallet.fromKeyPair}. Keys never leave memory and are
83
+ * redacted from serialization. See the module overview for the custody stance.
84
+ */
85
+ declare class TonWallet {
86
+ /** The in-memory key pair. Treat `secretKey` as a secret — never log or persist it. */
87
+ readonly keyPair: KeyPair;
88
+ private readonly contract;
89
+ private constructor();
90
+ /** Redacted serialization — NEVER exposes the secret key. */
91
+ toJSON(): {
92
+ address: string;
93
+ publicKey: string;
94
+ };
95
+ /**
96
+ * Generate a NEW wallet. Returns the 24-word mnemonic ONCE — store it securely;
97
+ * it is never persisted.
98
+ *
99
+ * @returns the in-memory `wallet` and its `mnemonic` (the only time you see it)
100
+ * @example
101
+ * ```ts
102
+ * const { wallet, mnemonic } = await TonWallet.generate();
103
+ * await secureVault.store(mnemonic); // YOUR job — it is never written to disk by the SDK
104
+ * console.log("fund this:", wallet.address);
105
+ * ```
106
+ */
107
+ static generate(): Promise<{
108
+ wallet: TonWallet;
109
+ mnemonic: string[];
110
+ }>;
111
+ /** Import a wallet from its 24-word mnemonic. */
112
+ static fromMnemonic(words: string[]): Promise<TonWallet>;
113
+ /** Import a wallet from a raw Ed25519 key pair. */
114
+ static fromKeyPair(publicKey: Uint8Array, secretKey: Uint8Array): TonWallet;
115
+ /** Friendly, non-bounceable (`UQ…`) address — FUND THIS to pay invoices. */
116
+ get address(): string;
117
+ /** Raw `0:hex` address form. */
118
+ get rawAddress(): string;
119
+ /** @internal The contract's StateInit, needed for the first (deploying) transfer. */
120
+ get init(): StateInit;
121
+ /** @internal The contract address as a TON `Address`. */
122
+ get tonAddress(): _ton_core.Address;
123
+ /** @internal Sign a transfer body. */
124
+ createTransfer(args: CreateTransferArgs): Cell;
125
+ /**
126
+ * This wallet's native TON balance.
127
+ *
128
+ * @param rpc - the RPC to query through
129
+ * @returns the balance in nanoTON
130
+ */
131
+ getBalance(rpc: TonRpc): Promise<bigint>;
132
+ /**
133
+ * This wallet's current sequence number.
134
+ *
135
+ * @param rpc - the RPC to query through
136
+ * @returns the seqno (`0` when the wallet contract isn't deployed yet)
137
+ */
138
+ getSeqno(rpc: TonRpc): Promise<number>;
139
+ /**
140
+ * This wallet's balance of a given jetton (e.g. USDT).
141
+ *
142
+ * @param rpc - the RPC to query through
143
+ * @param jettonMaster - the jetton master address (e.g. `DEFAULT_USDT_MASTER` from `@mystars-tg/faas-wallet`)
144
+ * @returns the token balance in the jetton's smallest unit (micro-USDT for USDT)
145
+ */
146
+ getJettonBalance(rpc: TonRpc, jettonMaster: string): Promise<bigint>;
147
+ }
148
+
149
+ /**
150
+ * `OrderPayer` — pay a FaaS order invoice from your own `TonWallet`.
151
+ *
152
+ * Signs ONCE and broadcasts. For `ton` it sends an exact-amount transfer with the
153
+ * order memo as an op-0 comment; for `usdt_ton` it resolves the payer's own USDT
154
+ * jetton wallet and sends a TEP-74 transfer whose `destination` is the FaaS
155
+ * `pay_to_address` and whose forward payload carries the memo.
156
+ *
157
+ * NOTE: this moves the PARTNER's own funds from the partner's own wallet — never
158
+ * the MyStars treasury. Tests use a mock `TonRpc` so nothing broadcasts.
159
+ */
160
+
161
+ /** Mainnet Tether USDT jetton master on TON. Override via `PayOrderOptions.jettonMaster` for testnet. */
162
+ declare const DEFAULT_USDT_MASTER = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";
163
+ /** TON attached to a USDT jetton transfer to cover its gas (excess refunds to the sender). Matches the core invoice builder. */
164
+ declare const DEFAULT_JETTON_GAS_TON = "0.05";
165
+ /** Options for {@link OrderPayer.payOrder} / {@link OrderPayer.planMessages}. */
166
+ interface PayOrderOptions {
167
+ /** The RPC used to resolve the jetton wallet, read balances, and broadcast. */
168
+ rpc: TonRpc;
169
+ /** USDT jetton master (defaults to mainnet Tether). */
170
+ jettonMaster?: string;
171
+ /** TON gas attached to a USDT transfer (default 0.05). */
172
+ jettonGasTon?: string;
173
+ /** Transfer validity window in seconds (default 120 — a non-landed send dies fast, safe to retry). */
174
+ validForSeconds?: number;
175
+ /** Skip the pre-sign balance check (saves 1-2 RPC calls; default false). */
176
+ skipBalanceCheck?: boolean;
177
+ /** Injectable clock (epoch ms) for deterministic tests. */
178
+ now?: number;
179
+ }
180
+ /** The outcome of a broadcast payment from {@link OrderPayer.payOrder}. */
181
+ interface PayOrderResult {
182
+ /** The order id paid. */
183
+ orderId?: string;
184
+ /** The paying wallet address. */
185
+ from: string;
186
+ /** The recipient (FaaS treasury owner) address. */
187
+ to: string;
188
+ /** The smallest-unit amount sent (nanoTON for ton, micro-USDT for usdt_ton). */
189
+ amountSmallestUnit: string;
190
+ }
191
+ /** An internal message the payer will sign into a transfer. Exposed for testing. */
192
+ interface PlannedMessage {
193
+ to: string;
194
+ value: bigint;
195
+ body: Cell;
196
+ bounce: boolean;
197
+ }
198
+ /**
199
+ * Pays a FaaS order invoice from a single {@link TonWallet}.
200
+ *
201
+ * @example
202
+ * ```ts
203
+ * const order = await client.createOrder(
204
+ * { type: "stars", recipient: { username: "durov" }, quantity: 100 },
205
+ * { idempotencyKey: `order-${myId}` },
206
+ * );
207
+ * const rpc = new ToncenterRpc({ endpoint: "https://toncenter.com/api/v2/jsonRPC" });
208
+ * const { from, to, amountSmallestUnit } = await new OrderPayer(wallet).payOrder(order, { rpc });
209
+ * const finished = await client.waitForOrder(order.order_id);
210
+ * ```
211
+ */
212
+ declare class OrderPayer {
213
+ private readonly wallet;
214
+ /** @param wallet - the funded {@link TonWallet} whose own funds will pay invoices */
215
+ constructor(wallet: TonWallet);
216
+ /**
217
+ * Plan the internal message(s) for an order WITHOUT signing or broadcasting.
218
+ * `ton` is pure; `usdt_ton` resolves the payer's jetton wallet via `rpc`.
219
+ */
220
+ planMessages(payment: PaymentInstruction, opts: PayOrderOptions): Promise<PlannedMessage[]>;
221
+ /** Throw `InsufficientBalanceError` if the wallet can't cover the planned payment + gas. */
222
+ private assertFunded;
223
+ /**
224
+ * Build, sign, and broadcast the payment for an order. Signs exactly ONCE.
225
+ *
226
+ * MONEY: this broadcasts a real on-chain transfer of the wallet's OWN funds.
227
+ * It is not idempotent — calling it twice for the same order pays twice. Guard
228
+ * retries at the order layer (a stable `Idempotency-Key` on `createOrder`, or
229
+ * use `fulfill` which only pays an order still `awaiting_payment`).
230
+ *
231
+ * @param order - a `CreateOrderResult`, or any object carrying a `payment` block (and optional `order_id`)
232
+ * @param opts - the {@link PayOrderOptions} (at minimum `rpc`)
233
+ * @returns the {@link PayOrderResult} — `from`/`to` addresses and the smallest-unit amount sent
234
+ * @throws `WalletError` if the order's `payment` block is missing `pay_to_address`/`memo`
235
+ * @throws `MyStarsValidationError` if the amount is non-positive
236
+ * @throws {@link InsufficientBalanceError} if the wallet can't cover the payment + gas (unless `skipBalanceCheck`)
237
+ */
238
+ payOrder(order: CreateOrderResult | {
239
+ payment: PaymentInstruction;
240
+ order_id?: string;
241
+ }, opts: PayOrderOptions): Promise<PayOrderResult>;
242
+ }
243
+
244
+ /**
245
+ * `fulfill()` — the one-call convenience: create an order, pay it from your
246
+ * wallet, and wait until it's delivered (or failed/reversed/expired).
247
+ *
248
+ * RETRY HAZARD (money): this broadcasts a REAL payment. A naive retry of a
249
+ * `fulfill` that already broadcast would create a SECOND order and pay it AGAIN.
250
+ * Two safeguards make retries safe:
251
+ * 1. A stable `idempotencyKey` is REQUIRED — re-running `fulfill` with the same
252
+ * key returns the SAME order from the server (idempotent replay) instead of
253
+ * minting a duplicate.
254
+ * 2. We only broadcast when the (possibly replayed) order is still
255
+ * `awaiting_payment`. If the replay shows the order already advanced
256
+ * (`paid`/`delivered`/…), we SKIP the payment and just wait — so a retry
257
+ * never double-pays an order whose first payment already landed.
258
+ * If anything throws after the order exists, the error carries `order_id` so you
259
+ * re-attach via `getOrder`/`waitForOrder` instead of re-paying.
260
+ */
261
+
262
+ /** The slice of `MyStarsClient` `fulfill` needs (structurally typed so it's easy to mock). */
263
+ interface FulfillClient {
264
+ createOrder(params: CreateOrderParams, opts?: CreateOrderOptions): Promise<CreateOrderResult>;
265
+ waitForOrder(orderId: string, opts?: WaitForOrderOptions): Promise<Order>;
266
+ }
267
+ /** Options for {@link fulfill} — the {@link PayOrderOptions} (e.g. `rpc`) plus order-create/wait wiring. */
268
+ interface FulfillOptions extends PayOrderOptions {
269
+ /**
270
+ * REQUIRED stable idempotency key — derive it from YOUR OWN order id so a retry
271
+ * of `fulfill` reuses the same key and the server returns the same order instead
272
+ * of creating a duplicate. May also be supplied via `createOptions.idempotencyKey`.
273
+ */
274
+ idempotencyKey?: string;
275
+ /** Options forwarded to `createOrder` (e.g. a caller-supplied idempotency key). */
276
+ createOptions?: CreateOrderOptions;
277
+ /** Options forwarded to `waitForOrder`. */
278
+ wait?: WaitForOrderOptions;
279
+ }
280
+ /**
281
+ * An error thrown AFTER the order was created carries the `order_id` so you can
282
+ * re-attach (`getOrder`/`waitForOrder`) instead of re-running `fulfill` (which
283
+ * would re-pay). The error may be any class (a `MyStarsApiError` from the wait, a
284
+ * `WalletError` from the payer, …), so the id rides as an optional property —
285
+ * read it with the type-safe {@link orderIdFromError} accessor.
286
+ */
287
+ type ErrorWithOrderId = Error & {
288
+ order_id?: string;
289
+ };
290
+ /**
291
+ * Read the `order_id` that `fulfill` attaches to a post-create failure. Returns
292
+ * `undefined` when the error carries none. Use it to recover safely after a
293
+ * `fulfill` throw — re-attach with `client.waitForOrder(id)` / `getOrder(id)`
294
+ * rather than re-running `fulfill` (which would broadcast a second payment).
295
+ */
296
+ declare function orderIdFromError(err: unknown): string | undefined;
297
+ /**
298
+ * create → pay → wait-until-terminal, in one call.
299
+ *
300
+ * Creates the order with the required stable `idempotencyKey`, broadcasts the
301
+ * payment from `wallet` ONLY if the (possibly idempotent-replayed) order is still
302
+ * `awaiting_payment`, then polls until the order is terminal. See the module note
303
+ * for the money-retry safeguards.
304
+ *
305
+ * @param client - the order-layer client (a `MyStarsClient`, or any {@link FulfillClient})
306
+ * @param wallet - the funded {@link TonWallet} that pays the invoice
307
+ * @param params - the order to create (`CreateOrderParams`)
308
+ * @param opts - {@link FulfillOptions} — MUST include a stable `idempotencyKey` (or `createOptions.idempotencyKey`) plus `rpc`
309
+ * @returns the final {@link Order} (terminal status — `delivered`/`failed`/`reversed`/`expired`)
310
+ * @throws `MyStarsValidationError` if no stable `idempotencyKey` was supplied
311
+ * @throws `RecipientIneligibleError` (422) if the recipient cannot receive the item (no order created)
312
+ * @throws `InsufficientBalanceError` if the wallet can't cover the payment
313
+ * @throws `OrderWaitTimeoutError` if the order doesn't finish before the wait deadline
314
+ * @throws `MyStarsApiError` on other API failures — after the order exists the thrown error carries `order_id` (read via {@link orderIdFromError}); re-attach with `client.waitForOrder(orderId)` instead of re-running `fulfill`
315
+ * @example
316
+ * ```ts
317
+ * const order = await fulfill(
318
+ * client, wallet,
319
+ * { type: "stars", recipient: { username: "durov" }, quantity: 100 },
320
+ * { rpc, idempotencyKey: `order-${myOrderId}` },
321
+ * );
322
+ * if (order.status !== "delivered") console.warn("not delivered:", order.failure_reason);
323
+ * ```
324
+ */
325
+ declare function fulfill(client: FulfillClient, wallet: TonWallet, params: CreateOrderParams, opts: FulfillOptions): Promise<Order>;
326
+
327
+ export { type CreateTransferArgs, DEFAULT_JETTON_GAS_TON, DEFAULT_USDT_MASTER, type ErrorWithOrderId, type FulfillClient, type FulfillOptions, InsufficientBalanceError, OrderPayer, type PayOrderOptions, type PayOrderResult, type PlannedMessage, type TonRpc, TonWallet, ToncenterRpc, type ToncenterRpcOptions, WalletError, fulfill, orderIdFromError };