@gasfree-kit/ton-gasless 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -41
- package/dist/index.d.mts +110 -19
- package/dist/index.d.ts +110 -19
- package/dist/index.js +158 -86
- package/dist/index.mjs +157 -86
- package/package.json +15 -2
package/README.md
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
# @gasfree-kit/ton-gasless
|
|
2
2
|
|
|
3
|
+
> **⚠️ Deprecation notice (v2.0.0)** — the positional methods
|
|
4
|
+
> `TonTransfer.transferUSDT`, `TonTransfer.checkBalance`, and
|
|
5
|
+
> `TonTransfer.getTransactionEstimateFee` are **deprecated** in favour of
|
|
6
|
+
> the options-object methods `TonTransfer.send`, `TonTransfer.getBalance`,
|
|
7
|
+
> and `TonTransfer.estimateFee`.
|
|
8
|
+
>
|
|
9
|
+
> **The legacy positional methods will be removed three weeks after the
|
|
10
|
+
> v2.0.0 publish date** in the next major release. The new methods throw
|
|
11
|
+
> on failure and return a flat result (no `{ success, message, data }`
|
|
12
|
+
> wrapper). Public TON endpoints are used by default — API keys are now
|
|
13
|
+
> optional but recommended for production traffic.
|
|
14
|
+
|
|
3
15
|
Gasless USDT transfers on TON through a sponsored relay.
|
|
4
16
|
|
|
5
17
|
With this package, the user only needs USDT. The relay pays the TON gas and charges a small fee back in USDT.
|
|
6
18
|
|
|
7
19
|
## What This Package Does
|
|
8
20
|
|
|
9
|
-
- creates a
|
|
21
|
+
- creates a TON wallet from a seed phrase
|
|
10
22
|
- quotes the relay fee in USDT
|
|
11
23
|
- checks that balance covers transfer amount plus fee
|
|
12
24
|
- submits the gasless transfer
|
|
@@ -26,7 +38,7 @@ With this package, the user only needs USDT. The relay pays the TON gas and char
|
|
|
26
38
|
│ │
|
|
27
39
|
v v
|
|
28
40
|
┌────────────┐ ┌───────────────┐
|
|
29
|
-
│
|
|
41
|
+
│ TON │ │ Relay fee │
|
|
30
42
|
│ wallet │ │ quote (USDT) │
|
|
31
43
|
└──────┬─────┘ └───────┬───────┘
|
|
32
44
|
│ │
|
|
@@ -62,23 +74,27 @@ This package depends on [`@gasfree-kit/core`](../core/README.md) for:
|
|
|
62
74
|
npm install @gasfree-kit/ton-gasless @gasfree-kit/core
|
|
63
75
|
```
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
npm install @tetherto/wdk-wallet-ton-gasless tonweb
|
|
69
|
-
```
|
|
77
|
+
After installing, your package manager will prompt you to install the peer dependencies listed in `package.json`.
|
|
70
78
|
|
|
71
79
|
## Configuration
|
|
72
80
|
|
|
81
|
+
All URL/API-key fields are optional — the SDK falls back to the public
|
|
82
|
+
endpoints exported as `TON_PUBLIC_ENDPOINTS`. For production traffic,
|
|
83
|
+
register keys with toncenter.com / tonapi.io and pass them in.
|
|
84
|
+
|
|
73
85
|
```ts
|
|
74
86
|
import type { TonGaslessClientConfig } from '@gasfree-kit/ton-gasless';
|
|
75
87
|
|
|
88
|
+
// Minimal — uses public endpoints with no API keys.
|
|
89
|
+
const minimalConfig: TonGaslessClientConfig = {};
|
|
90
|
+
|
|
91
|
+
// Production — bring your own keys.
|
|
76
92
|
const config: TonGaslessClientConfig = {
|
|
77
|
-
tonCenterUrl: 'https://toncenter.com/api/v2',
|
|
78
93
|
tonCenterApiKey: 'your-toncenter-api-key',
|
|
79
|
-
tonApiUrl: 'https://tonapi.io',
|
|
80
94
|
tonApiSecretKey: 'your-tonapi-secret-key',
|
|
81
|
-
// Optional:
|
|
95
|
+
// Optional URL overrides (default to public endpoints):
|
|
96
|
+
// tonCenterUrl: 'https://toncenter.com/api/v2/jsonRPC',
|
|
97
|
+
// tonApiUrl: 'https://tonapi.io',
|
|
82
98
|
// transferMaxFee: 1_000_000,
|
|
83
99
|
// usdtAddress: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs',
|
|
84
100
|
};
|
|
@@ -86,14 +102,14 @@ const config: TonGaslessClientConfig = {
|
|
|
86
102
|
|
|
87
103
|
### Config fields
|
|
88
104
|
|
|
89
|
-
| Field | Purpose
|
|
90
|
-
| ----------------- |
|
|
91
|
-
| `tonCenterUrl` | TON Center endpoint
|
|
92
|
-
| `tonCenterApiKey` | TON Center API key
|
|
93
|
-
| `tonApiUrl` | TON API endpoint
|
|
94
|
-
| `tonApiSecretKey` | TON API secret key
|
|
95
|
-
| `transferMaxFee` | Maximum fee limit in base units, capped by the SDK
|
|
96
|
-
| `usdtAddress` | Optional override for the TON USDT root
|
|
105
|
+
| Field | Purpose |
|
|
106
|
+
| ----------------- | -------------------------------------------------------- |
|
|
107
|
+
| `tonCenterUrl` | TON Center endpoint. Defaults to `TON_PUBLIC_ENDPOINTS`. |
|
|
108
|
+
| `tonCenterApiKey` | TON Center API key. Optional; recommended for prod. |
|
|
109
|
+
| `tonApiUrl` | TON API endpoint. Defaults to `TON_PUBLIC_ENDPOINTS`. |
|
|
110
|
+
| `tonApiSecretKey` | TON API secret key. Optional; recommended for prod. |
|
|
111
|
+
| `transferMaxFee` | Maximum fee limit in base units, capped by the SDK. |
|
|
112
|
+
| `usdtAddress` | Optional override for the TON USDT root. |
|
|
97
113
|
|
|
98
114
|
## Quick Start
|
|
99
115
|
|
|
@@ -126,39 +142,46 @@ const walletResult = await setupTonGaslessWallet(seedPhrase, config, "0'/0/0");
|
|
|
126
142
|
```ts
|
|
127
143
|
import { TonTransfer } from '@gasfree-kit/ton-gasless';
|
|
128
144
|
|
|
129
|
-
const balance = await TonTransfer.
|
|
145
|
+
const balance = await TonTransfer.getBalance({ seedPhrase, config });
|
|
130
146
|
|
|
131
|
-
console.log(balance.
|
|
132
|
-
console.log(balance.
|
|
147
|
+
console.log(balance.balance); // "42.50"
|
|
148
|
+
console.log(balance.balanceRaw); // 42500000n
|
|
149
|
+
console.log(balance.address); // wallet address
|
|
133
150
|
```
|
|
134
151
|
|
|
135
152
|
### 4. Estimate the fee
|
|
136
153
|
|
|
137
154
|
```ts
|
|
138
|
-
const estimate = await TonTransfer.
|
|
155
|
+
const estimate = await TonTransfer.estimateFee({
|
|
139
156
|
seedPhrase,
|
|
140
157
|
config,
|
|
141
|
-
'
|
|
142
|
-
'
|
|
143
|
-
);
|
|
158
|
+
to: 'EQRecipient...',
|
|
159
|
+
amount: '10.00',
|
|
160
|
+
});
|
|
144
161
|
|
|
145
|
-
console.log(estimate.
|
|
162
|
+
console.log(estimate.fee); // "0.05" (gasless commission in USDT)
|
|
146
163
|
```
|
|
147
164
|
|
|
148
165
|
### 5. Execute the gasless transfer
|
|
149
166
|
|
|
150
167
|
```ts
|
|
151
|
-
const result = await TonTransfer.
|
|
168
|
+
const result = await TonTransfer.send({
|
|
152
169
|
seedPhrase,
|
|
153
170
|
config,
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
);
|
|
171
|
+
to: 'EQRecipient...',
|
|
172
|
+
amount: '50.00',
|
|
173
|
+
});
|
|
157
174
|
|
|
158
|
-
console.log(result.
|
|
159
|
-
console.log(result.
|
|
175
|
+
console.log(result.transactionHash);
|
|
176
|
+
console.log(result.fee); // bigint, base units
|
|
177
|
+
console.log(result.submittedAt);
|
|
160
178
|
```
|
|
161
179
|
|
|
180
|
+
> The legacy positional methods (`transferUSDT`, `checkBalance`,
|
|
181
|
+
> `getTransactionEstimateFee`) remain available and return the previous
|
|
182
|
+
> `{ success, message, data }` envelope. They are marked `@deprecated` and
|
|
183
|
+
> wrap the new methods above.
|
|
184
|
+
|
|
162
185
|
## How The Flow Behaves
|
|
163
186
|
|
|
164
187
|
1. The SDK validates the recipient address.
|
|
@@ -169,15 +192,15 @@ console.log(result.data.fee);
|
|
|
169
192
|
|
|
170
193
|
## Main Exports
|
|
171
194
|
|
|
172
|
-
| Export | What it does
|
|
173
|
-
| ------------------------ |
|
|
174
|
-
| `setupTonGaslessWallet` | Creates the
|
|
175
|
-
| `TonTransfer` | Checks balances, estimates fees, and sends transfers
|
|
176
|
-
| `getTonGaslessConfig` | Expands and validates the runtime config
|
|
177
|
-
| `tonUsdtTokenRoot` | Built-in TON USDT root address
|
|
178
|
-
| `usdtTonBaseUnits` | Converts human-readable USDT to base units
|
|
179
|
-
| `fromTonBaseUnitsToUsdt` | Converts base units back to readable USDT
|
|
180
|
-
| `getTonUsdtValue` | Utility helper for TON to USDT conversions
|
|
195
|
+
| Export | What it does |
|
|
196
|
+
| ------------------------ | ------------------------------------------------------------------ |
|
|
197
|
+
| `setupTonGaslessWallet` | Creates the TON wallet from a seed phrase and resolves the address |
|
|
198
|
+
| `TonTransfer` | Checks balances, estimates fees, and sends transfers |
|
|
199
|
+
| `getTonGaslessConfig` | Expands and validates the runtime config |
|
|
200
|
+
| `tonUsdtTokenRoot` | Built-in TON USDT root address |
|
|
201
|
+
| `usdtTonBaseUnits` | Converts human-readable USDT to base units |
|
|
202
|
+
| `fromTonBaseUnitsToUsdt` | Converts base units back to readable USDT |
|
|
203
|
+
| `getTonUsdtValue` | Utility helper for TON to USDT conversions |
|
|
181
204
|
|
|
182
205
|
## Safety Notes
|
|
183
206
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import * as _tetherto_wdk_wallet_ton_gasless from '@tetherto/wdk-wallet-ton-gasless';
|
|
2
2
|
|
|
3
3
|
declare const tonUsdtTokenRoot = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";
|
|
4
|
+
/**
|
|
5
|
+
* Public TON mainnet RPC endpoints — used as defaults when the caller
|
|
6
|
+
* omits `tonCenterUrl` / `tonApiUrl`.
|
|
7
|
+
*
|
|
8
|
+
* Public endpoints are rate-limited and unauthenticated. For production
|
|
9
|
+
* traffic, register a key with toncenter.com / tonapi.io and pass it in
|
|
10
|
+
* via {@link TonGaslessClientConfig.tonCenterApiKey} and
|
|
11
|
+
* {@link TonGaslessClientConfig.tonApiSecretKey}.
|
|
12
|
+
*/
|
|
13
|
+
declare const TON_PUBLIC_ENDPOINTS: {
|
|
14
|
+
readonly tonCenterUrl: "https://toncenter.com/api/v2/jsonRPC";
|
|
15
|
+
readonly tonApiUrl: "https://tonapi.io";
|
|
16
|
+
};
|
|
4
17
|
type TonGaslessNetworkConfig = {
|
|
5
18
|
tonClient: {
|
|
6
19
|
url: string;
|
|
@@ -16,20 +29,24 @@ type TonGaslessNetworkConfig = {
|
|
|
16
29
|
};
|
|
17
30
|
};
|
|
18
31
|
type TonGaslessClientConfig = {
|
|
19
|
-
tonCenterUrl
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
/** Toncenter JSON-RPC URL. Defaults to {@link TON_PUBLIC_ENDPOINTS.tonCenterUrl}. */
|
|
33
|
+
tonCenterUrl?: string;
|
|
34
|
+
/** Toncenter API key. Optional for public endpoints; recommended for prod. */
|
|
35
|
+
tonCenterApiKey?: string;
|
|
36
|
+
/** Ton API URL. Defaults to {@link TON_PUBLIC_ENDPOINTS.tonApiUrl}. */
|
|
37
|
+
tonApiUrl?: string;
|
|
38
|
+
/** Ton API secret key. Optional for public endpoints; recommended for prod. */
|
|
39
|
+
tonApiSecretKey?: string;
|
|
23
40
|
transferMaxFee?: number;
|
|
24
41
|
usdtAddress?: string;
|
|
25
42
|
};
|
|
26
43
|
declare function getTonGaslessConfig(config: TonGaslessClientConfig): TonGaslessNetworkConfig;
|
|
27
44
|
|
|
28
45
|
/**
|
|
29
|
-
* Set up a TON gasless wallet
|
|
46
|
+
* Set up a TON gasless wallet.
|
|
30
47
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
48
|
+
* Pins the BIP-44 derivation path at m/44'/607'/0'/0/{index} so the
|
|
49
|
+
* resulting address stays stable across underlying-library upgrades.
|
|
33
50
|
*/
|
|
34
51
|
declare function setupTonGaslessWallet(seedPhrase: string, config: TonGaslessClientConfig, derivationPath?: string): Promise<{
|
|
35
52
|
wallet: _tetherto_wdk_wallet_ton_gasless.default;
|
|
@@ -37,24 +54,99 @@ declare function setupTonGaslessWallet(seedPhrase: string, config: TonGaslessCli
|
|
|
37
54
|
address: string;
|
|
38
55
|
}>;
|
|
39
56
|
|
|
57
|
+
/** Result of a successful gasless USDT transfer on TON. */
|
|
58
|
+
interface TonTransferResult {
|
|
59
|
+
transactionHash: string;
|
|
60
|
+
/** Gasless commission paid in USDT base units. */
|
|
61
|
+
fee: bigint;
|
|
62
|
+
/** Sender wallet address. */
|
|
63
|
+
from: string;
|
|
64
|
+
/** Recipient wallet address. */
|
|
65
|
+
to: string;
|
|
66
|
+
/** Human-readable amount transferred. */
|
|
67
|
+
amount: string;
|
|
68
|
+
/** Wall-clock time when the transfer was submitted (ms since epoch). */
|
|
69
|
+
submittedAt: number;
|
|
70
|
+
}
|
|
71
|
+
/** Result of a TON USDT balance query. */
|
|
72
|
+
interface TonBalanceResult {
|
|
73
|
+
/** Human-readable balance (e.g. `"42.50"`). */
|
|
74
|
+
balance: string;
|
|
75
|
+
/** Raw balance in base units (USDT = 6 decimals). */
|
|
76
|
+
balanceRaw: bigint;
|
|
77
|
+
decimals: number;
|
|
78
|
+
/** Wallet address that holds the balance. */
|
|
79
|
+
address: string;
|
|
80
|
+
}
|
|
81
|
+
/** Result of a TON gasless transfer fee estimate. */
|
|
82
|
+
interface TonFeeEstimate {
|
|
83
|
+
/** Human-readable fee in USDT (paymaster token). */
|
|
84
|
+
fee: string;
|
|
85
|
+
feeRaw: bigint;
|
|
86
|
+
}
|
|
87
|
+
/** Options for `TonTransfer.send`. */
|
|
88
|
+
interface TonSendOptions {
|
|
89
|
+
seedPhrase: string;
|
|
90
|
+
config: TonGaslessClientConfig;
|
|
91
|
+
to: string;
|
|
92
|
+
/** Human-readable USDT amount (e.g. `"10.00"`). */
|
|
93
|
+
amount: string;
|
|
94
|
+
}
|
|
95
|
+
/** Options for `TonTransfer.getBalance`. */
|
|
96
|
+
interface TonBalanceOptions {
|
|
97
|
+
seedPhrase: string;
|
|
98
|
+
config: TonGaslessClientConfig;
|
|
99
|
+
/** Override the USDT jetton root. Defaults to the canonical mainnet USDT. */
|
|
100
|
+
tokenAddress?: string;
|
|
101
|
+
}
|
|
102
|
+
/** Options for `TonTransfer.estimateFee`. */
|
|
103
|
+
interface TonEstimateFeeOptions {
|
|
104
|
+
seedPhrase: string;
|
|
105
|
+
config: TonGaslessClientConfig;
|
|
106
|
+
to: string;
|
|
107
|
+
amount: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
40
110
|
declare class TonTransfer {
|
|
41
111
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
112
|
+
* Execute a gasless USDT transfer on TON.
|
|
113
|
+
*
|
|
114
|
+
* Throws `InsufficientBalanceError` / `TransactionFailedError` on failure;
|
|
115
|
+
* resolves with a flat `TonTransferResult` on success.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* const receipt = await TonTransfer.send({
|
|
120
|
+
* seedPhrase,
|
|
121
|
+
* config: { tonCenterApiKey, tonApiSecretKey }, // uses public endpoints by default
|
|
122
|
+
* to: 'UQ...',
|
|
123
|
+
* amount: '10.00',
|
|
124
|
+
* });
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
static send(options: TonSendOptions): Promise<TonTransferResult>;
|
|
128
|
+
/** Query the USDT balance on TON for the wallet derived from `seedPhrase`. */
|
|
129
|
+
static getBalance(options: TonBalanceOptions): Promise<TonBalanceResult>;
|
|
130
|
+
/** Estimate the gasless commission (USDT) for a transfer. */
|
|
131
|
+
static estimateFee(options: TonEstimateFeeOptions): Promise<TonFeeEstimate>;
|
|
132
|
+
/**
|
|
133
|
+
* @deprecated Use {@link TonTransfer.estimateFee} instead. Returns the
|
|
134
|
+
* legacy `{ success, message, data: { fee } }` shape.
|
|
44
135
|
*/
|
|
45
136
|
static getTransactionEstimateFee(seedPhrase: string, config: TonGaslessClientConfig, amountInUsdt: string, recipientAddress: string): Promise<{
|
|
46
137
|
message: string;
|
|
47
|
-
success:
|
|
138
|
+
success: true;
|
|
48
139
|
data: {
|
|
49
140
|
fee: string;
|
|
50
141
|
};
|
|
51
142
|
}>;
|
|
52
143
|
/**
|
|
53
|
-
*
|
|
144
|
+
* @deprecated Use {@link TonTransfer.getBalance} instead. Returns the
|
|
145
|
+
* legacy `{ success, message, data }` shape.
|
|
54
146
|
*/
|
|
55
147
|
static checkBalance(seedPhrase: string, config: TonGaslessClientConfig, tokenAddress?: string): Promise<{
|
|
56
148
|
message: string;
|
|
57
|
-
success:
|
|
149
|
+
success: true;
|
|
58
150
|
data: {
|
|
59
151
|
tokenBalance: string;
|
|
60
152
|
decimals: number;
|
|
@@ -62,17 +154,16 @@ declare class TonTransfer {
|
|
|
62
154
|
};
|
|
63
155
|
}>;
|
|
64
156
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* User only needs USDT — balance must cover amount + gas commission.
|
|
157
|
+
* @deprecated Use {@link TonTransfer.send} instead — accepts an options
|
|
158
|
+
* object and returns a flat `TonTransferResult` (no `success`/`message`
|
|
159
|
+
* wrapper).
|
|
69
160
|
*/
|
|
70
161
|
static transferUSDT(seedPhrase: string, config: TonGaslessClientConfig, amountInUsdt: string, recipientAddress: string): Promise<{
|
|
71
162
|
message: string;
|
|
72
|
-
success:
|
|
163
|
+
success: true;
|
|
73
164
|
data: {
|
|
74
165
|
transactionHash: string;
|
|
75
|
-
fee:
|
|
166
|
+
fee: bigint;
|
|
76
167
|
tonTransferContext: {
|
|
77
168
|
wdkHash: string;
|
|
78
169
|
walletAddress: string;
|
|
@@ -98,4 +189,4 @@ declare function fromTonBaseUnitsToUsdt(amount: bigint, dec?: number): string;
|
|
|
98
189
|
*/
|
|
99
190
|
declare function getTonUsdtValue(amountInBaseUnits: bigint, exchangeRate: number): number;
|
|
100
191
|
|
|
101
|
-
export { type TonGaslessClientConfig, type TonGaslessNetworkConfig, TonTransfer, fromTonBaseUnitsToUsdt, getTonGaslessConfig, getTonUsdtValue, setupTonGaslessWallet, tonUsdtTokenRoot, usdtTonBaseUnits };
|
|
192
|
+
export { TON_PUBLIC_ENDPOINTS, type TonBalanceOptions, type TonBalanceResult, type TonEstimateFeeOptions, type TonFeeEstimate, type TonGaslessClientConfig, type TonGaslessNetworkConfig, type TonSendOptions, TonTransfer, type TonTransferResult, fromTonBaseUnitsToUsdt, getTonGaslessConfig, getTonUsdtValue, setupTonGaslessWallet, tonUsdtTokenRoot, usdtTonBaseUnits };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import * as _tetherto_wdk_wallet_ton_gasless from '@tetherto/wdk-wallet-ton-gasless';
|
|
2
2
|
|
|
3
3
|
declare const tonUsdtTokenRoot = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";
|
|
4
|
+
/**
|
|
5
|
+
* Public TON mainnet RPC endpoints — used as defaults when the caller
|
|
6
|
+
* omits `tonCenterUrl` / `tonApiUrl`.
|
|
7
|
+
*
|
|
8
|
+
* Public endpoints are rate-limited and unauthenticated. For production
|
|
9
|
+
* traffic, register a key with toncenter.com / tonapi.io and pass it in
|
|
10
|
+
* via {@link TonGaslessClientConfig.tonCenterApiKey} and
|
|
11
|
+
* {@link TonGaslessClientConfig.tonApiSecretKey}.
|
|
12
|
+
*/
|
|
13
|
+
declare const TON_PUBLIC_ENDPOINTS: {
|
|
14
|
+
readonly tonCenterUrl: "https://toncenter.com/api/v2/jsonRPC";
|
|
15
|
+
readonly tonApiUrl: "https://tonapi.io";
|
|
16
|
+
};
|
|
4
17
|
type TonGaslessNetworkConfig = {
|
|
5
18
|
tonClient: {
|
|
6
19
|
url: string;
|
|
@@ -16,20 +29,24 @@ type TonGaslessNetworkConfig = {
|
|
|
16
29
|
};
|
|
17
30
|
};
|
|
18
31
|
type TonGaslessClientConfig = {
|
|
19
|
-
tonCenterUrl
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
/** Toncenter JSON-RPC URL. Defaults to {@link TON_PUBLIC_ENDPOINTS.tonCenterUrl}. */
|
|
33
|
+
tonCenterUrl?: string;
|
|
34
|
+
/** Toncenter API key. Optional for public endpoints; recommended for prod. */
|
|
35
|
+
tonCenterApiKey?: string;
|
|
36
|
+
/** Ton API URL. Defaults to {@link TON_PUBLIC_ENDPOINTS.tonApiUrl}. */
|
|
37
|
+
tonApiUrl?: string;
|
|
38
|
+
/** Ton API secret key. Optional for public endpoints; recommended for prod. */
|
|
39
|
+
tonApiSecretKey?: string;
|
|
23
40
|
transferMaxFee?: number;
|
|
24
41
|
usdtAddress?: string;
|
|
25
42
|
};
|
|
26
43
|
declare function getTonGaslessConfig(config: TonGaslessClientConfig): TonGaslessNetworkConfig;
|
|
27
44
|
|
|
28
45
|
/**
|
|
29
|
-
* Set up a TON gasless wallet
|
|
46
|
+
* Set up a TON gasless wallet.
|
|
30
47
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
48
|
+
* Pins the BIP-44 derivation path at m/44'/607'/0'/0/{index} so the
|
|
49
|
+
* resulting address stays stable across underlying-library upgrades.
|
|
33
50
|
*/
|
|
34
51
|
declare function setupTonGaslessWallet(seedPhrase: string, config: TonGaslessClientConfig, derivationPath?: string): Promise<{
|
|
35
52
|
wallet: _tetherto_wdk_wallet_ton_gasless.default;
|
|
@@ -37,24 +54,99 @@ declare function setupTonGaslessWallet(seedPhrase: string, config: TonGaslessCli
|
|
|
37
54
|
address: string;
|
|
38
55
|
}>;
|
|
39
56
|
|
|
57
|
+
/** Result of a successful gasless USDT transfer on TON. */
|
|
58
|
+
interface TonTransferResult {
|
|
59
|
+
transactionHash: string;
|
|
60
|
+
/** Gasless commission paid in USDT base units. */
|
|
61
|
+
fee: bigint;
|
|
62
|
+
/** Sender wallet address. */
|
|
63
|
+
from: string;
|
|
64
|
+
/** Recipient wallet address. */
|
|
65
|
+
to: string;
|
|
66
|
+
/** Human-readable amount transferred. */
|
|
67
|
+
amount: string;
|
|
68
|
+
/** Wall-clock time when the transfer was submitted (ms since epoch). */
|
|
69
|
+
submittedAt: number;
|
|
70
|
+
}
|
|
71
|
+
/** Result of a TON USDT balance query. */
|
|
72
|
+
interface TonBalanceResult {
|
|
73
|
+
/** Human-readable balance (e.g. `"42.50"`). */
|
|
74
|
+
balance: string;
|
|
75
|
+
/** Raw balance in base units (USDT = 6 decimals). */
|
|
76
|
+
balanceRaw: bigint;
|
|
77
|
+
decimals: number;
|
|
78
|
+
/** Wallet address that holds the balance. */
|
|
79
|
+
address: string;
|
|
80
|
+
}
|
|
81
|
+
/** Result of a TON gasless transfer fee estimate. */
|
|
82
|
+
interface TonFeeEstimate {
|
|
83
|
+
/** Human-readable fee in USDT (paymaster token). */
|
|
84
|
+
fee: string;
|
|
85
|
+
feeRaw: bigint;
|
|
86
|
+
}
|
|
87
|
+
/** Options for `TonTransfer.send`. */
|
|
88
|
+
interface TonSendOptions {
|
|
89
|
+
seedPhrase: string;
|
|
90
|
+
config: TonGaslessClientConfig;
|
|
91
|
+
to: string;
|
|
92
|
+
/** Human-readable USDT amount (e.g. `"10.00"`). */
|
|
93
|
+
amount: string;
|
|
94
|
+
}
|
|
95
|
+
/** Options for `TonTransfer.getBalance`. */
|
|
96
|
+
interface TonBalanceOptions {
|
|
97
|
+
seedPhrase: string;
|
|
98
|
+
config: TonGaslessClientConfig;
|
|
99
|
+
/** Override the USDT jetton root. Defaults to the canonical mainnet USDT. */
|
|
100
|
+
tokenAddress?: string;
|
|
101
|
+
}
|
|
102
|
+
/** Options for `TonTransfer.estimateFee`. */
|
|
103
|
+
interface TonEstimateFeeOptions {
|
|
104
|
+
seedPhrase: string;
|
|
105
|
+
config: TonGaslessClientConfig;
|
|
106
|
+
to: string;
|
|
107
|
+
amount: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
40
110
|
declare class TonTransfer {
|
|
41
111
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
112
|
+
* Execute a gasless USDT transfer on TON.
|
|
113
|
+
*
|
|
114
|
+
* Throws `InsufficientBalanceError` / `TransactionFailedError` on failure;
|
|
115
|
+
* resolves with a flat `TonTransferResult` on success.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* const receipt = await TonTransfer.send({
|
|
120
|
+
* seedPhrase,
|
|
121
|
+
* config: { tonCenterApiKey, tonApiSecretKey }, // uses public endpoints by default
|
|
122
|
+
* to: 'UQ...',
|
|
123
|
+
* amount: '10.00',
|
|
124
|
+
* });
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
static send(options: TonSendOptions): Promise<TonTransferResult>;
|
|
128
|
+
/** Query the USDT balance on TON for the wallet derived from `seedPhrase`. */
|
|
129
|
+
static getBalance(options: TonBalanceOptions): Promise<TonBalanceResult>;
|
|
130
|
+
/** Estimate the gasless commission (USDT) for a transfer. */
|
|
131
|
+
static estimateFee(options: TonEstimateFeeOptions): Promise<TonFeeEstimate>;
|
|
132
|
+
/**
|
|
133
|
+
* @deprecated Use {@link TonTransfer.estimateFee} instead. Returns the
|
|
134
|
+
* legacy `{ success, message, data: { fee } }` shape.
|
|
44
135
|
*/
|
|
45
136
|
static getTransactionEstimateFee(seedPhrase: string, config: TonGaslessClientConfig, amountInUsdt: string, recipientAddress: string): Promise<{
|
|
46
137
|
message: string;
|
|
47
|
-
success:
|
|
138
|
+
success: true;
|
|
48
139
|
data: {
|
|
49
140
|
fee: string;
|
|
50
141
|
};
|
|
51
142
|
}>;
|
|
52
143
|
/**
|
|
53
|
-
*
|
|
144
|
+
* @deprecated Use {@link TonTransfer.getBalance} instead. Returns the
|
|
145
|
+
* legacy `{ success, message, data }` shape.
|
|
54
146
|
*/
|
|
55
147
|
static checkBalance(seedPhrase: string, config: TonGaslessClientConfig, tokenAddress?: string): Promise<{
|
|
56
148
|
message: string;
|
|
57
|
-
success:
|
|
149
|
+
success: true;
|
|
58
150
|
data: {
|
|
59
151
|
tokenBalance: string;
|
|
60
152
|
decimals: number;
|
|
@@ -62,17 +154,16 @@ declare class TonTransfer {
|
|
|
62
154
|
};
|
|
63
155
|
}>;
|
|
64
156
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* User only needs USDT — balance must cover amount + gas commission.
|
|
157
|
+
* @deprecated Use {@link TonTransfer.send} instead — accepts an options
|
|
158
|
+
* object and returns a flat `TonTransferResult` (no `success`/`message`
|
|
159
|
+
* wrapper).
|
|
69
160
|
*/
|
|
70
161
|
static transferUSDT(seedPhrase: string, config: TonGaslessClientConfig, amountInUsdt: string, recipientAddress: string): Promise<{
|
|
71
162
|
message: string;
|
|
72
|
-
success:
|
|
163
|
+
success: true;
|
|
73
164
|
data: {
|
|
74
165
|
transactionHash: string;
|
|
75
|
-
fee:
|
|
166
|
+
fee: bigint;
|
|
76
167
|
tonTransferContext: {
|
|
77
168
|
wdkHash: string;
|
|
78
169
|
walletAddress: string;
|
|
@@ -98,4 +189,4 @@ declare function fromTonBaseUnitsToUsdt(amount: bigint, dec?: number): string;
|
|
|
98
189
|
*/
|
|
99
190
|
declare function getTonUsdtValue(amountInBaseUnits: bigint, exchangeRate: number): number;
|
|
100
191
|
|
|
101
|
-
export { type TonGaslessClientConfig, type TonGaslessNetworkConfig, TonTransfer, fromTonBaseUnitsToUsdt, getTonGaslessConfig, getTonUsdtValue, setupTonGaslessWallet, tonUsdtTokenRoot, usdtTonBaseUnits };
|
|
192
|
+
export { TON_PUBLIC_ENDPOINTS, type TonBalanceOptions, type TonBalanceResult, type TonEstimateFeeOptions, type TonFeeEstimate, type TonGaslessClientConfig, type TonGaslessNetworkConfig, type TonSendOptions, TonTransfer, type TonTransferResult, fromTonBaseUnitsToUsdt, getTonGaslessConfig, getTonUsdtValue, setupTonGaslessWallet, tonUsdtTokenRoot, usdtTonBaseUnits };
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
TON_PUBLIC_ENDPOINTS: () => TON_PUBLIC_ENDPOINTS,
|
|
33
34
|
TonTransfer: () => TonTransfer,
|
|
34
35
|
fromTonBaseUnitsToUsdt: () => fromTonBaseUnitsToUsdt,
|
|
35
36
|
getTonGaslessConfig: () => getTonGaslessConfig,
|
|
@@ -42,6 +43,10 @@ module.exports = __toCommonJS(index_exports);
|
|
|
42
43
|
|
|
43
44
|
// src/tonNetworkConfiguration.ts
|
|
44
45
|
var tonUsdtTokenRoot = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";
|
|
46
|
+
var TON_PUBLIC_ENDPOINTS = {
|
|
47
|
+
tonCenterUrl: "https://toncenter.com/api/v2/jsonRPC",
|
|
48
|
+
tonApiUrl: "https://tonapi.io"
|
|
49
|
+
};
|
|
45
50
|
var MAX_TRANSFER_FEE = 5e7;
|
|
46
51
|
var DEFAULT_TRANSFER_FEE = 1e6;
|
|
47
52
|
function getTonGaslessConfig(config) {
|
|
@@ -53,16 +58,16 @@ function getTonGaslessConfig(config) {
|
|
|
53
58
|
}
|
|
54
59
|
return {
|
|
55
60
|
tonClient: {
|
|
56
|
-
url: config.tonCenterUrl,
|
|
57
|
-
secretKey: config.tonCenterApiKey
|
|
61
|
+
url: config.tonCenterUrl ?? TON_PUBLIC_ENDPOINTS.tonCenterUrl,
|
|
62
|
+
secretKey: config.tonCenterApiKey ?? ""
|
|
58
63
|
},
|
|
59
64
|
transferMaxFee: fee,
|
|
60
65
|
paymasterToken: {
|
|
61
66
|
address: config.usdtAddress ?? tonUsdtTokenRoot
|
|
62
67
|
},
|
|
63
68
|
tonApiClient: {
|
|
64
|
-
url: config.tonApiUrl,
|
|
65
|
-
secretKey: config.tonApiSecretKey
|
|
69
|
+
url: config.tonApiUrl ?? TON_PUBLIC_ENDPOINTS.tonApiUrl,
|
|
70
|
+
secretKey: config.tonApiSecretKey ?? ""
|
|
66
71
|
}
|
|
67
72
|
};
|
|
68
73
|
}
|
|
@@ -113,123 +118,190 @@ function getTonUsdtValue(amountInBaseUnits, exchangeRate) {
|
|
|
113
118
|
|
|
114
119
|
// src/tonGaslessTetherTransfer.ts
|
|
115
120
|
var TonTransfer = class {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
*/
|
|
120
|
-
static async getTransactionEstimateFee(seedPhrase, config, amountInUsdt, recipientAddress) {
|
|
121
|
-
(0, import_core.validateTonAddress)(recipientAddress, "recipient address");
|
|
122
|
-
const amount = usdtTonBaseUnits(amountInUsdt);
|
|
123
|
-
const { account, wallet } = await setupTonGaslessWallet(seedPhrase, config);
|
|
124
|
-
try {
|
|
125
|
-
const transferQuote = await account.quoteTransfer({
|
|
126
|
-
token: tonUsdtTokenRoot,
|
|
127
|
-
recipient: recipientAddress,
|
|
128
|
-
amount
|
|
129
|
-
});
|
|
130
|
-
const feeInUsdt = fromTonBaseUnitsToUsdt(transferQuote.fee);
|
|
131
|
-
return {
|
|
132
|
-
message: "Transaction fee estimate retrieved successfully",
|
|
133
|
-
success: true,
|
|
134
|
-
data: { fee: feeInUsdt }
|
|
135
|
-
};
|
|
136
|
-
} finally {
|
|
137
|
-
account.dispose();
|
|
138
|
-
wallet.dispose();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Check USDT balance on TON.
|
|
143
|
-
*/
|
|
144
|
-
static async checkBalance(seedPhrase, config, tokenAddress = tonUsdtTokenRoot) {
|
|
145
|
-
const { account, wallet } = await setupTonGaslessWallet(seedPhrase, config);
|
|
146
|
-
try {
|
|
147
|
-
const balance = await account.getTokenBalance(tokenAddress);
|
|
148
|
-
const usdBalance = fromTonBaseUnitsToUsdt(balance);
|
|
149
|
-
return {
|
|
150
|
-
message: "TON balances retrieved successfully",
|
|
151
|
-
success: true,
|
|
152
|
-
data: {
|
|
153
|
-
tokenBalance: balance.toString(),
|
|
154
|
-
decimals: 6,
|
|
155
|
-
usdBalance: String(usdBalance)
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
} finally {
|
|
159
|
-
account.dispose();
|
|
160
|
-
wallet.dispose();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
121
|
+
// -----------------------------------------------------------------------
|
|
122
|
+
// New ergonomic API (v1)
|
|
123
|
+
// -----------------------------------------------------------------------
|
|
163
124
|
/**
|
|
164
125
|
* Execute a gasless USDT transfer on TON.
|
|
165
126
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
127
|
+
* Throws `InsufficientBalanceError` / `TransactionFailedError` on failure;
|
|
128
|
+
* resolves with a flat `TonTransferResult` on success.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* const receipt = await TonTransfer.send({
|
|
133
|
+
* seedPhrase,
|
|
134
|
+
* config: { tonCenterApiKey, tonApiSecretKey }, // uses public endpoints by default
|
|
135
|
+
* to: 'UQ...',
|
|
136
|
+
* amount: '10.00',
|
|
137
|
+
* });
|
|
138
|
+
* ```
|
|
168
139
|
*/
|
|
169
|
-
static async
|
|
170
|
-
const amount =
|
|
140
|
+
static async send(options) {
|
|
141
|
+
const { seedPhrase, config, to, amount } = options;
|
|
142
|
+
const amountRaw = usdtTonBaseUnits(amount);
|
|
171
143
|
const {
|
|
172
144
|
account,
|
|
173
145
|
wallet,
|
|
174
146
|
address: walletAddress
|
|
175
147
|
} = await setupTonGaslessWallet(seedPhrase, config);
|
|
176
148
|
try {
|
|
177
|
-
(0, import_core.validateTonAddress)(
|
|
178
|
-
if (
|
|
149
|
+
(0, import_core.validateTonAddress)(to, "recipient address");
|
|
150
|
+
if (to.toLowerCase() === walletAddress.toLowerCase()) {
|
|
179
151
|
throw new import_core.TransactionFailedError("ton", "Cannot transfer to your own address");
|
|
180
152
|
}
|
|
181
153
|
const balance = await account.getTokenBalance(tonUsdtTokenRoot);
|
|
182
154
|
const quote = await account.quoteTransfer({
|
|
183
155
|
token: tonUsdtTokenRoot,
|
|
184
|
-
recipient:
|
|
185
|
-
amount
|
|
156
|
+
recipient: to,
|
|
157
|
+
amount: amountRaw
|
|
186
158
|
});
|
|
187
|
-
if (balance <
|
|
159
|
+
if (balance < amountRaw + quote.fee) {
|
|
188
160
|
throw new import_core.InsufficientBalanceError("ton", "USDT (amount + gas fee)");
|
|
189
161
|
}
|
|
190
162
|
const result = await account.transfer({
|
|
191
163
|
token: tonUsdtTokenRoot,
|
|
192
|
-
recipient:
|
|
193
|
-
amount
|
|
164
|
+
recipient: to,
|
|
165
|
+
amount: amountRaw
|
|
194
166
|
});
|
|
195
167
|
return {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
wdkHash: result.hash,
|
|
203
|
-
walletAddress,
|
|
204
|
-
recipientAddress,
|
|
205
|
-
amountBaseUnits: amount.toString(),
|
|
206
|
-
transferTimestamp: Date.now()
|
|
207
|
-
}
|
|
208
|
-
}
|
|
168
|
+
transactionHash: result.hash,
|
|
169
|
+
fee: BigInt(result.fee),
|
|
170
|
+
from: walletAddress,
|
|
171
|
+
to,
|
|
172
|
+
amount,
|
|
173
|
+
submittedAt: Date.now()
|
|
209
174
|
};
|
|
210
175
|
} catch (error) {
|
|
211
176
|
if (error instanceof import_core.InsufficientBalanceError || error instanceof import_core.TransactionFailedError) {
|
|
212
177
|
throw error;
|
|
213
178
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
179
|
+
throw new import_core.TransactionFailedError("ton", mapTonError(error));
|
|
180
|
+
} finally {
|
|
181
|
+
account.dispose();
|
|
182
|
+
wallet.dispose();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** Query the USDT balance on TON for the wallet derived from `seedPhrase`. */
|
|
186
|
+
static async getBalance(options) {
|
|
187
|
+
const { seedPhrase, config } = options;
|
|
188
|
+
const tokenAddress = options.tokenAddress ?? tonUsdtTokenRoot;
|
|
189
|
+
const { account, wallet, address } = await setupTonGaslessWallet(seedPhrase, config);
|
|
190
|
+
try {
|
|
191
|
+
const raw = await account.getTokenBalance(tokenAddress);
|
|
192
|
+
return {
|
|
193
|
+
balance: String(fromTonBaseUnitsToUsdt(raw)),
|
|
194
|
+
balanceRaw: BigInt(raw),
|
|
195
|
+
decimals: 6,
|
|
196
|
+
address
|
|
197
|
+
};
|
|
198
|
+
} finally {
|
|
199
|
+
account.dispose();
|
|
200
|
+
wallet.dispose();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/** Estimate the gasless commission (USDT) for a transfer. */
|
|
204
|
+
static async estimateFee(options) {
|
|
205
|
+
const { seedPhrase, config, to, amount } = options;
|
|
206
|
+
(0, import_core.validateTonAddress)(to, "recipient address");
|
|
207
|
+
const amountRaw = usdtTonBaseUnits(amount);
|
|
208
|
+
const { account, wallet } = await setupTonGaslessWallet(seedPhrase, config);
|
|
209
|
+
try {
|
|
210
|
+
const quote = await account.quoteTransfer({
|
|
211
|
+
token: tonUsdtTokenRoot,
|
|
212
|
+
recipient: to,
|
|
213
|
+
amount: amountRaw
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
fee: String(fromTonBaseUnitsToUsdt(quote.fee)),
|
|
217
|
+
feeRaw: BigInt(quote.fee)
|
|
218
|
+
};
|
|
225
219
|
} finally {
|
|
226
220
|
account.dispose();
|
|
227
221
|
wallet.dispose();
|
|
228
222
|
}
|
|
229
223
|
}
|
|
224
|
+
// -----------------------------------------------------------------------
|
|
225
|
+
// Legacy positional API — preserved for backwards compatibility.
|
|
226
|
+
// -----------------------------------------------------------------------
|
|
227
|
+
/**
|
|
228
|
+
* @deprecated Use {@link TonTransfer.estimateFee} instead. Returns the
|
|
229
|
+
* legacy `{ success, message, data: { fee } }` shape.
|
|
230
|
+
*/
|
|
231
|
+
static async getTransactionEstimateFee(seedPhrase, config, amountInUsdt, recipientAddress) {
|
|
232
|
+
const result = await this.estimateFee({
|
|
233
|
+
seedPhrase,
|
|
234
|
+
config,
|
|
235
|
+
to: recipientAddress,
|
|
236
|
+
amount: amountInUsdt
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
message: "Transaction fee estimate retrieved successfully",
|
|
240
|
+
success: true,
|
|
241
|
+
data: { fee: result.fee }
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* @deprecated Use {@link TonTransfer.getBalance} instead. Returns the
|
|
246
|
+
* legacy `{ success, message, data }` shape.
|
|
247
|
+
*/
|
|
248
|
+
static async checkBalance(seedPhrase, config, tokenAddress = tonUsdtTokenRoot) {
|
|
249
|
+
const result = await this.getBalance({ seedPhrase, config, tokenAddress });
|
|
250
|
+
return {
|
|
251
|
+
message: "TON balances retrieved successfully",
|
|
252
|
+
success: true,
|
|
253
|
+
data: {
|
|
254
|
+
tokenBalance: result.balanceRaw.toString(),
|
|
255
|
+
decimals: result.decimals,
|
|
256
|
+
usdBalance: result.balance
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* @deprecated Use {@link TonTransfer.send} instead — accepts an options
|
|
262
|
+
* object and returns a flat `TonTransferResult` (no `success`/`message`
|
|
263
|
+
* wrapper).
|
|
264
|
+
*/
|
|
265
|
+
static async transferUSDT(seedPhrase, config, amountInUsdt, recipientAddress) {
|
|
266
|
+
const result = await this.send({
|
|
267
|
+
seedPhrase,
|
|
268
|
+
config,
|
|
269
|
+
to: recipientAddress,
|
|
270
|
+
amount: amountInUsdt
|
|
271
|
+
});
|
|
272
|
+
return {
|
|
273
|
+
message: "USDT transfer on TON successful (gasless)",
|
|
274
|
+
success: true,
|
|
275
|
+
data: {
|
|
276
|
+
transactionHash: result.transactionHash,
|
|
277
|
+
fee: result.fee,
|
|
278
|
+
tonTransferContext: {
|
|
279
|
+
wdkHash: result.transactionHash,
|
|
280
|
+
walletAddress: result.from,
|
|
281
|
+
recipientAddress: result.to,
|
|
282
|
+
amountBaseUnits: usdtTonBaseUnits(result.amount).toString(),
|
|
283
|
+
transferTimestamp: result.submittedAt
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
230
288
|
};
|
|
289
|
+
function mapTonError(error) {
|
|
290
|
+
if (!(error instanceof Error)) return "Failed to transfer USDT on TON";
|
|
291
|
+
if (error.message.includes("insufficient balance") || error.message.includes("Failed to unpack account state")) {
|
|
292
|
+
return "Insufficient USDT balance for network fees";
|
|
293
|
+
}
|
|
294
|
+
if (error.message.includes("invalid address")) {
|
|
295
|
+
return "The recipient TON address is invalid";
|
|
296
|
+
}
|
|
297
|
+
if (error.message.includes("timeout")) {
|
|
298
|
+
return "Network timeout - please try again";
|
|
299
|
+
}
|
|
300
|
+
return "Failed to transfer USDT on TON";
|
|
301
|
+
}
|
|
231
302
|
// Annotate the CommonJS export names for ESM import in node:
|
|
232
303
|
0 && (module.exports = {
|
|
304
|
+
TON_PUBLIC_ENDPOINTS,
|
|
233
305
|
TonTransfer,
|
|
234
306
|
fromTonBaseUnitsToUsdt,
|
|
235
307
|
getTonGaslessConfig,
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// src/tonNetworkConfiguration.ts
|
|
2
2
|
var tonUsdtTokenRoot = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";
|
|
3
|
+
var TON_PUBLIC_ENDPOINTS = {
|
|
4
|
+
tonCenterUrl: "https://toncenter.com/api/v2/jsonRPC",
|
|
5
|
+
tonApiUrl: "https://tonapi.io"
|
|
6
|
+
};
|
|
3
7
|
var MAX_TRANSFER_FEE = 5e7;
|
|
4
8
|
var DEFAULT_TRANSFER_FEE = 1e6;
|
|
5
9
|
function getTonGaslessConfig(config) {
|
|
@@ -11,16 +15,16 @@ function getTonGaslessConfig(config) {
|
|
|
11
15
|
}
|
|
12
16
|
return {
|
|
13
17
|
tonClient: {
|
|
14
|
-
url: config.tonCenterUrl,
|
|
15
|
-
secretKey: config.tonCenterApiKey
|
|
18
|
+
url: config.tonCenterUrl ?? TON_PUBLIC_ENDPOINTS.tonCenterUrl,
|
|
19
|
+
secretKey: config.tonCenterApiKey ?? ""
|
|
16
20
|
},
|
|
17
21
|
transferMaxFee: fee,
|
|
18
22
|
paymasterToken: {
|
|
19
23
|
address: config.usdtAddress ?? tonUsdtTokenRoot
|
|
20
24
|
},
|
|
21
25
|
tonApiClient: {
|
|
22
|
-
url: config.tonApiUrl,
|
|
23
|
-
secretKey: config.tonApiSecretKey
|
|
26
|
+
url: config.tonApiUrl ?? TON_PUBLIC_ENDPOINTS.tonApiUrl,
|
|
27
|
+
secretKey: config.tonApiSecretKey ?? ""
|
|
24
28
|
}
|
|
25
29
|
};
|
|
26
30
|
}
|
|
@@ -75,122 +79,189 @@ function getTonUsdtValue(amountInBaseUnits, exchangeRate) {
|
|
|
75
79
|
|
|
76
80
|
// src/tonGaslessTetherTransfer.ts
|
|
77
81
|
var TonTransfer = class {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*/
|
|
82
|
-
static async getTransactionEstimateFee(seedPhrase, config, amountInUsdt, recipientAddress) {
|
|
83
|
-
validateTonAddress(recipientAddress, "recipient address");
|
|
84
|
-
const amount = usdtTonBaseUnits(amountInUsdt);
|
|
85
|
-
const { account, wallet } = await setupTonGaslessWallet(seedPhrase, config);
|
|
86
|
-
try {
|
|
87
|
-
const transferQuote = await account.quoteTransfer({
|
|
88
|
-
token: tonUsdtTokenRoot,
|
|
89
|
-
recipient: recipientAddress,
|
|
90
|
-
amount
|
|
91
|
-
});
|
|
92
|
-
const feeInUsdt = fromTonBaseUnitsToUsdt(transferQuote.fee);
|
|
93
|
-
return {
|
|
94
|
-
message: "Transaction fee estimate retrieved successfully",
|
|
95
|
-
success: true,
|
|
96
|
-
data: { fee: feeInUsdt }
|
|
97
|
-
};
|
|
98
|
-
} finally {
|
|
99
|
-
account.dispose();
|
|
100
|
-
wallet.dispose();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Check USDT balance on TON.
|
|
105
|
-
*/
|
|
106
|
-
static async checkBalance(seedPhrase, config, tokenAddress = tonUsdtTokenRoot) {
|
|
107
|
-
const { account, wallet } = await setupTonGaslessWallet(seedPhrase, config);
|
|
108
|
-
try {
|
|
109
|
-
const balance = await account.getTokenBalance(tokenAddress);
|
|
110
|
-
const usdBalance = fromTonBaseUnitsToUsdt(balance);
|
|
111
|
-
return {
|
|
112
|
-
message: "TON balances retrieved successfully",
|
|
113
|
-
success: true,
|
|
114
|
-
data: {
|
|
115
|
-
tokenBalance: balance.toString(),
|
|
116
|
-
decimals: 6,
|
|
117
|
-
usdBalance: String(usdBalance)
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
} finally {
|
|
121
|
-
account.dispose();
|
|
122
|
-
wallet.dispose();
|
|
123
|
-
}
|
|
124
|
-
}
|
|
82
|
+
// -----------------------------------------------------------------------
|
|
83
|
+
// New ergonomic API (v1)
|
|
84
|
+
// -----------------------------------------------------------------------
|
|
125
85
|
/**
|
|
126
86
|
* Execute a gasless USDT transfer on TON.
|
|
127
87
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
88
|
+
* Throws `InsufficientBalanceError` / `TransactionFailedError` on failure;
|
|
89
|
+
* resolves with a flat `TonTransferResult` on success.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const receipt = await TonTransfer.send({
|
|
94
|
+
* seedPhrase,
|
|
95
|
+
* config: { tonCenterApiKey, tonApiSecretKey }, // uses public endpoints by default
|
|
96
|
+
* to: 'UQ...',
|
|
97
|
+
* amount: '10.00',
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
130
100
|
*/
|
|
131
|
-
static async
|
|
132
|
-
const amount =
|
|
101
|
+
static async send(options) {
|
|
102
|
+
const { seedPhrase, config, to, amount } = options;
|
|
103
|
+
const amountRaw = usdtTonBaseUnits(amount);
|
|
133
104
|
const {
|
|
134
105
|
account,
|
|
135
106
|
wallet,
|
|
136
107
|
address: walletAddress
|
|
137
108
|
} = await setupTonGaslessWallet(seedPhrase, config);
|
|
138
109
|
try {
|
|
139
|
-
validateTonAddress(
|
|
140
|
-
if (
|
|
110
|
+
validateTonAddress(to, "recipient address");
|
|
111
|
+
if (to.toLowerCase() === walletAddress.toLowerCase()) {
|
|
141
112
|
throw new TransactionFailedError("ton", "Cannot transfer to your own address");
|
|
142
113
|
}
|
|
143
114
|
const balance = await account.getTokenBalance(tonUsdtTokenRoot);
|
|
144
115
|
const quote = await account.quoteTransfer({
|
|
145
116
|
token: tonUsdtTokenRoot,
|
|
146
|
-
recipient:
|
|
147
|
-
amount
|
|
117
|
+
recipient: to,
|
|
118
|
+
amount: amountRaw
|
|
148
119
|
});
|
|
149
|
-
if (balance <
|
|
120
|
+
if (balance < amountRaw + quote.fee) {
|
|
150
121
|
throw new InsufficientBalanceError("ton", "USDT (amount + gas fee)");
|
|
151
122
|
}
|
|
152
123
|
const result = await account.transfer({
|
|
153
124
|
token: tonUsdtTokenRoot,
|
|
154
|
-
recipient:
|
|
155
|
-
amount
|
|
125
|
+
recipient: to,
|
|
126
|
+
amount: amountRaw
|
|
156
127
|
});
|
|
157
128
|
return {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
wdkHash: result.hash,
|
|
165
|
-
walletAddress,
|
|
166
|
-
recipientAddress,
|
|
167
|
-
amountBaseUnits: amount.toString(),
|
|
168
|
-
transferTimestamp: Date.now()
|
|
169
|
-
}
|
|
170
|
-
}
|
|
129
|
+
transactionHash: result.hash,
|
|
130
|
+
fee: BigInt(result.fee),
|
|
131
|
+
from: walletAddress,
|
|
132
|
+
to,
|
|
133
|
+
amount,
|
|
134
|
+
submittedAt: Date.now()
|
|
171
135
|
};
|
|
172
136
|
} catch (error) {
|
|
173
137
|
if (error instanceof InsufficientBalanceError || error instanceof TransactionFailedError) {
|
|
174
138
|
throw error;
|
|
175
139
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
140
|
+
throw new TransactionFailedError("ton", mapTonError(error));
|
|
141
|
+
} finally {
|
|
142
|
+
account.dispose();
|
|
143
|
+
wallet.dispose();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Query the USDT balance on TON for the wallet derived from `seedPhrase`. */
|
|
147
|
+
static async getBalance(options) {
|
|
148
|
+
const { seedPhrase, config } = options;
|
|
149
|
+
const tokenAddress = options.tokenAddress ?? tonUsdtTokenRoot;
|
|
150
|
+
const { account, wallet, address } = await setupTonGaslessWallet(seedPhrase, config);
|
|
151
|
+
try {
|
|
152
|
+
const raw = await account.getTokenBalance(tokenAddress);
|
|
153
|
+
return {
|
|
154
|
+
balance: String(fromTonBaseUnitsToUsdt(raw)),
|
|
155
|
+
balanceRaw: BigInt(raw),
|
|
156
|
+
decimals: 6,
|
|
157
|
+
address
|
|
158
|
+
};
|
|
159
|
+
} finally {
|
|
160
|
+
account.dispose();
|
|
161
|
+
wallet.dispose();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Estimate the gasless commission (USDT) for a transfer. */
|
|
165
|
+
static async estimateFee(options) {
|
|
166
|
+
const { seedPhrase, config, to, amount } = options;
|
|
167
|
+
validateTonAddress(to, "recipient address");
|
|
168
|
+
const amountRaw = usdtTonBaseUnits(amount);
|
|
169
|
+
const { account, wallet } = await setupTonGaslessWallet(seedPhrase, config);
|
|
170
|
+
try {
|
|
171
|
+
const quote = await account.quoteTransfer({
|
|
172
|
+
token: tonUsdtTokenRoot,
|
|
173
|
+
recipient: to,
|
|
174
|
+
amount: amountRaw
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
fee: String(fromTonBaseUnitsToUsdt(quote.fee)),
|
|
178
|
+
feeRaw: BigInt(quote.fee)
|
|
179
|
+
};
|
|
187
180
|
} finally {
|
|
188
181
|
account.dispose();
|
|
189
182
|
wallet.dispose();
|
|
190
183
|
}
|
|
191
184
|
}
|
|
185
|
+
// -----------------------------------------------------------------------
|
|
186
|
+
// Legacy positional API — preserved for backwards compatibility.
|
|
187
|
+
// -----------------------------------------------------------------------
|
|
188
|
+
/**
|
|
189
|
+
* @deprecated Use {@link TonTransfer.estimateFee} instead. Returns the
|
|
190
|
+
* legacy `{ success, message, data: { fee } }` shape.
|
|
191
|
+
*/
|
|
192
|
+
static async getTransactionEstimateFee(seedPhrase, config, amountInUsdt, recipientAddress) {
|
|
193
|
+
const result = await this.estimateFee({
|
|
194
|
+
seedPhrase,
|
|
195
|
+
config,
|
|
196
|
+
to: recipientAddress,
|
|
197
|
+
amount: amountInUsdt
|
|
198
|
+
});
|
|
199
|
+
return {
|
|
200
|
+
message: "Transaction fee estimate retrieved successfully",
|
|
201
|
+
success: true,
|
|
202
|
+
data: { fee: result.fee }
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* @deprecated Use {@link TonTransfer.getBalance} instead. Returns the
|
|
207
|
+
* legacy `{ success, message, data }` shape.
|
|
208
|
+
*/
|
|
209
|
+
static async checkBalance(seedPhrase, config, tokenAddress = tonUsdtTokenRoot) {
|
|
210
|
+
const result = await this.getBalance({ seedPhrase, config, tokenAddress });
|
|
211
|
+
return {
|
|
212
|
+
message: "TON balances retrieved successfully",
|
|
213
|
+
success: true,
|
|
214
|
+
data: {
|
|
215
|
+
tokenBalance: result.balanceRaw.toString(),
|
|
216
|
+
decimals: result.decimals,
|
|
217
|
+
usdBalance: result.balance
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* @deprecated Use {@link TonTransfer.send} instead — accepts an options
|
|
223
|
+
* object and returns a flat `TonTransferResult` (no `success`/`message`
|
|
224
|
+
* wrapper).
|
|
225
|
+
*/
|
|
226
|
+
static async transferUSDT(seedPhrase, config, amountInUsdt, recipientAddress) {
|
|
227
|
+
const result = await this.send({
|
|
228
|
+
seedPhrase,
|
|
229
|
+
config,
|
|
230
|
+
to: recipientAddress,
|
|
231
|
+
amount: amountInUsdt
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
message: "USDT transfer on TON successful (gasless)",
|
|
235
|
+
success: true,
|
|
236
|
+
data: {
|
|
237
|
+
transactionHash: result.transactionHash,
|
|
238
|
+
fee: result.fee,
|
|
239
|
+
tonTransferContext: {
|
|
240
|
+
wdkHash: result.transactionHash,
|
|
241
|
+
walletAddress: result.from,
|
|
242
|
+
recipientAddress: result.to,
|
|
243
|
+
amountBaseUnits: usdtTonBaseUnits(result.amount).toString(),
|
|
244
|
+
transferTimestamp: result.submittedAt
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
192
249
|
};
|
|
250
|
+
function mapTonError(error) {
|
|
251
|
+
if (!(error instanceof Error)) return "Failed to transfer USDT on TON";
|
|
252
|
+
if (error.message.includes("insufficient balance") || error.message.includes("Failed to unpack account state")) {
|
|
253
|
+
return "Insufficient USDT balance for network fees";
|
|
254
|
+
}
|
|
255
|
+
if (error.message.includes("invalid address")) {
|
|
256
|
+
return "The recipient TON address is invalid";
|
|
257
|
+
}
|
|
258
|
+
if (error.message.includes("timeout")) {
|
|
259
|
+
return "Network timeout - please try again";
|
|
260
|
+
}
|
|
261
|
+
return "Failed to transfer USDT on TON";
|
|
262
|
+
}
|
|
193
263
|
export {
|
|
264
|
+
TON_PUBLIC_ENDPOINTS,
|
|
194
265
|
TonTransfer,
|
|
195
266
|
fromTonBaseUnitsToUsdt,
|
|
196
267
|
getTonGaslessConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gasfree-kit/ton-gasless",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Gasless USDT transfers on TON via sponsored relay",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -17,12 +17,25 @@
|
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@gasfree-kit/core": "0.2.
|
|
20
|
+
"@gasfree-kit/core": "0.2.1"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"@tetherto/wdk-wallet-ton-gasless": "^1.0.0-beta.4",
|
|
24
24
|
"tonweb": "^0.0.66"
|
|
25
25
|
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"gasless",
|
|
28
|
+
"gas-free",
|
|
29
|
+
"ton",
|
|
30
|
+
"toncoin",
|
|
31
|
+
"usdt",
|
|
32
|
+
"transfer",
|
|
33
|
+
"sponsored",
|
|
34
|
+
"relay",
|
|
35
|
+
"web3",
|
|
36
|
+
"sdk",
|
|
37
|
+
"meta-transactions"
|
|
38
|
+
],
|
|
26
39
|
"license": "MIT",
|
|
27
40
|
"publishConfig": {
|
|
28
41
|
"access": "public"
|