@tychilabs/stonfi-gasless-sdk 0.0.1
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/LICENSE +21 -0
- package/README.md +91 -0
- package/dist/index.cjs +369 -0
- package/dist/index.d.cts +218 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.js +308 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tychi Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# STON.fi Gasless SDK
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@tychilabs/stonfi-gasless-sdk)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Universal Gas Framework SDK for paying TON transaction fees with STON tokens.
|
|
7
|
+
|
|
8
|
+
**By Tychi Labs**
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
The STON.fi Gasless SDK enables developers to build applications where users pay transaction fees with STON tokens instead of TON. This eliminates the friction of requiring users to hold native tokens for gas, making blockchain interactions more accessible and user-friendly.
|
|
13
|
+
|
|
14
|
+
## Why UGF?
|
|
15
|
+
|
|
16
|
+
Traditional blockchain transactions create barriers for new users who must acquire native tokens just to pay gas fees. Universal Gas Framework solves this by:
|
|
17
|
+
|
|
18
|
+
- **Simplifying User Experience** - Users can transact using only STON tokens, removing the need to manage multiple token types
|
|
19
|
+
- **Supporting DeFi Ecosystem** - Locked STON provides liquidity to STON.fi, strengthening the broader ecosystem
|
|
20
|
+
- **Enabling Gasless Flows** - Developers can create seamless onboarding experiences where users never think about gas fees
|
|
21
|
+
- **Maintaining Security** - All transactions are cryptographically signed and verified on-chain
|
|
22
|
+
|
|
23
|
+
Whether you're building a DEX, NFT marketplace, or any dApp on TON, UGF enables truly gasless user experiences while leveraging STON's liquidity.
|
|
24
|
+
|
|
25
|
+
## How It Works
|
|
26
|
+
|
|
27
|
+
1. Users lock STON tokens in the UGF escrow contract
|
|
28
|
+
2. Locked STON becomes an x402 balance used for gas payments
|
|
29
|
+
3. Your application requests a quote via the SDK
|
|
30
|
+
4. User signs the payment digest
|
|
31
|
+
5. UGF verifies the signature and deducts STON
|
|
32
|
+
6. The contract pays TON gas fees on the user's behalf
|
|
33
|
+
7. Transaction executes seamlessly
|
|
34
|
+
|
|
35
|
+
No relaying, no wrapped tokens - just direct gas payment with STON.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @tychilabs/stonfi-gasless-sdk
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- Node.js 16 or higher
|
|
46
|
+
- `@ton/core` >= 0.63.0 (peer dependency)
|
|
47
|
+
- API Key - Contact us at support@tychilabs.com to get started
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- **STON Gas Payments** - Pay transaction fees with STON instead of TON
|
|
52
|
+
- **Cryptographic Signing** - Built-in utilities for digest signing and verification
|
|
53
|
+
- **Full TypeScript Support** - Complete type definitions for all API interactions
|
|
54
|
+
- **Simple, Intuitive API** - Clean interfaces that follow modern JavaScript patterns
|
|
55
|
+
- **Comprehensive Error Handling** - Typed error classes for precise error management
|
|
56
|
+
- **Production Ready** - Battle-tested code with proper validation and security measures
|
|
57
|
+
|
|
58
|
+
## Documentation
|
|
59
|
+
|
|
60
|
+
Complete guides and references to get you started:
|
|
61
|
+
|
|
62
|
+
- [Quick Start Guide](./docs/QUICKSTART.md) - Get up and running in 5 minutes
|
|
63
|
+
- [API Reference](./docs/API.md) - Detailed documentation of all SDK methods
|
|
64
|
+
|
|
65
|
+
## Use Cases
|
|
66
|
+
|
|
67
|
+
- **DEX Integrations** - Enable token swaps without requiring TON for gas
|
|
68
|
+
- **NFT Marketplaces** - Let users mint and trade NFTs using only STON
|
|
69
|
+
- **Gaming dApps** - Simplify in-game transactions by eliminating gas fee complexity
|
|
70
|
+
- **Onboarding Flows** - Create seamless signup experiences without native token requirements
|
|
71
|
+
- **DeFi Applications** - Build lending, staking, or yield farming platforms with gasless UX
|
|
72
|
+
|
|
73
|
+
## Coming Soon
|
|
74
|
+
|
|
75
|
+
**ston.universalgasframework.com** - Self-service portal for API key management, analytics, and monitoring
|
|
76
|
+
|
|
77
|
+
## Getting Help
|
|
78
|
+
|
|
79
|
+
We're here to support your integration:
|
|
80
|
+
|
|
81
|
+
- **Technical Issues** - [GitHub Issues](https://github.com/TychiWallet/stonfi-gasless-sdk/issues)
|
|
82
|
+
- **Integration Support** - Email us at yash@tychilabs.com
|
|
83
|
+
- **Feature Requests** - Open a discussion on GitHub
|
|
84
|
+
|
|
85
|
+
## Contributing
|
|
86
|
+
|
|
87
|
+
We welcome contributions from the community. Please see our contributing guidelines and feel free to submit issues or pull requests.
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT © Tychi Labs
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
34
|
+
GasLimitExceededError: () => GasLimitExceededError,
|
|
35
|
+
InsufficientBalanceError: () => InsufficientBalanceError,
|
|
36
|
+
InvalidChainError: () => InvalidChainError,
|
|
37
|
+
InvalidPaymentCoinError: () => InvalidPaymentCoinError,
|
|
38
|
+
InvalidSignatureError: () => InvalidSignatureError,
|
|
39
|
+
MissingDigestError: () => MissingDigestError,
|
|
40
|
+
NetworkError: () => NetworkError,
|
|
41
|
+
PayerMismatchError: () => PayerMismatchError,
|
|
42
|
+
QuoteExpiredError: () => QuoteExpiredError,
|
|
43
|
+
QuoteNotFoundError: () => QuoteNotFoundError,
|
|
44
|
+
StatusNotFoundError: () => StatusNotFoundError,
|
|
45
|
+
UGFClient: () => UGFClient,
|
|
46
|
+
UGFError: () => UGFError,
|
|
47
|
+
UnauthorizedError: () => UnauthorizedError,
|
|
48
|
+
formatSTON: () => formatSTON,
|
|
49
|
+
fromNano: () => fromNano,
|
|
50
|
+
isValidBufferPct: () => isValidBufferPct,
|
|
51
|
+
isValidGasEstimate: () => isValidGasEstimate,
|
|
52
|
+
isValidHex: () => isValidHex,
|
|
53
|
+
isValidTONAddress: () => isValidTONAddress,
|
|
54
|
+
parseBackendError: () => parseBackendError,
|
|
55
|
+
signDigest: () => signDigest,
|
|
56
|
+
toNano: () => toNano,
|
|
57
|
+
verifySignature: () => verifySignature
|
|
58
|
+
});
|
|
59
|
+
module.exports = __toCommonJS(index_exports);
|
|
60
|
+
|
|
61
|
+
// src/utils/errors.ts
|
|
62
|
+
var UGFError = class extends Error {
|
|
63
|
+
constructor(message, code) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.code = code;
|
|
66
|
+
this.name = "UGFError";
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var QuoteNotFoundError = class extends UGFError {
|
|
70
|
+
constructor() {
|
|
71
|
+
super("quote not found or expired", "QUOTE_NOT_FOUND");
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var QuoteExpiredError = class extends UGFError {
|
|
75
|
+
constructor() {
|
|
76
|
+
super("quote expired", "QUOTE_EXPIRED");
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var PayerMismatchError = class extends UGFError {
|
|
80
|
+
constructor() {
|
|
81
|
+
super("payer mismatch", "PAYER_MISMATCH");
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var InvalidSignatureError = class extends UGFError {
|
|
85
|
+
constructor() {
|
|
86
|
+
super("invalid signature", "INVALID_SIGNATURE");
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
var InsufficientBalanceError = class extends UGFError {
|
|
90
|
+
constructor() {
|
|
91
|
+
super("insufficient locked balance", "INSUFFICIENT_BALANCE");
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var InvalidChainError = class extends UGFError {
|
|
95
|
+
constructor() {
|
|
96
|
+
super("invalid chain", "INVALID_CHAIN");
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var InvalidPaymentCoinError = class extends UGFError {
|
|
100
|
+
constructor() {
|
|
101
|
+
super("invalid payment coin", "INVALID_COIN");
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var GasLimitExceededError = class extends UGFError {
|
|
105
|
+
constructor() {
|
|
106
|
+
super("gas exceeds 0.5 TON limit", "GAS_LIMIT_EXCEEDED");
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var MissingDigestError = class extends UGFError {
|
|
110
|
+
constructor() {
|
|
111
|
+
super("missing digest", "MISSING_DIGEST");
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
var StatusNotFoundError = class extends UGFError {
|
|
115
|
+
constructor() {
|
|
116
|
+
super("status not found", "STATUS_NOT_FOUND");
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var UnauthorizedError = class extends UGFError {
|
|
120
|
+
constructor() {
|
|
121
|
+
super("unauthorized", "UNAUTHORIZED");
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var NetworkError = class extends UGFError {
|
|
125
|
+
constructor(message = "network error") {
|
|
126
|
+
super(message, "NETWORK_ERROR");
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
function parseBackendError(message) {
|
|
130
|
+
const lowerMsg = message.toLowerCase();
|
|
131
|
+
if (lowerMsg.includes("quote not found") || lowerMsg.includes("expired")) {
|
|
132
|
+
return new QuoteNotFoundError();
|
|
133
|
+
}
|
|
134
|
+
if (lowerMsg.includes("quote expired")) {
|
|
135
|
+
return new QuoteExpiredError();
|
|
136
|
+
}
|
|
137
|
+
if (lowerMsg.includes("payer mismatch")) {
|
|
138
|
+
return new PayerMismatchError();
|
|
139
|
+
}
|
|
140
|
+
if (lowerMsg.includes("invalid signature")) {
|
|
141
|
+
return new InvalidSignatureError();
|
|
142
|
+
}
|
|
143
|
+
if (lowerMsg.includes("insufficient")) {
|
|
144
|
+
return new InsufficientBalanceError();
|
|
145
|
+
}
|
|
146
|
+
if (lowerMsg.includes("invalid chain")) {
|
|
147
|
+
return new InvalidChainError();
|
|
148
|
+
}
|
|
149
|
+
if (lowerMsg.includes("invalid payment coin")) {
|
|
150
|
+
return new InvalidPaymentCoinError();
|
|
151
|
+
}
|
|
152
|
+
if (lowerMsg.includes("gas exceeds")) {
|
|
153
|
+
return new GasLimitExceededError();
|
|
154
|
+
}
|
|
155
|
+
if (lowerMsg.includes("missing digest")) {
|
|
156
|
+
return new MissingDigestError();
|
|
157
|
+
}
|
|
158
|
+
if (lowerMsg.includes("status not found")) {
|
|
159
|
+
return new StatusNotFoundError();
|
|
160
|
+
}
|
|
161
|
+
if (lowerMsg.includes("unauthorized")) {
|
|
162
|
+
return new UnauthorizedError();
|
|
163
|
+
}
|
|
164
|
+
return new NetworkError(message);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/api/quote.ts
|
|
168
|
+
async function getQuote(apiUrl, request, apiKey) {
|
|
169
|
+
const res = await fetch(`${apiUrl}/quote`, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: {
|
|
172
|
+
"Content-Type": "application/json",
|
|
173
|
+
"X-API-Key": apiKey
|
|
174
|
+
},
|
|
175
|
+
body: JSON.stringify(request)
|
|
176
|
+
});
|
|
177
|
+
if (!res.ok) {
|
|
178
|
+
const text = await res.text();
|
|
179
|
+
throw parseBackendError(text);
|
|
180
|
+
}
|
|
181
|
+
const data = await res.json();
|
|
182
|
+
return data;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/api/payment.ts
|
|
186
|
+
async function submitPayment(apiUrl, request, apiKey) {
|
|
187
|
+
const res = await fetch(`${apiUrl}/payment/submit`, {
|
|
188
|
+
method: "POST",
|
|
189
|
+
headers: {
|
|
190
|
+
"Content-Type": "application/json",
|
|
191
|
+
"X-API-Key": apiKey
|
|
192
|
+
},
|
|
193
|
+
body: JSON.stringify(request)
|
|
194
|
+
});
|
|
195
|
+
if (!res.ok) {
|
|
196
|
+
const text = await res.text();
|
|
197
|
+
throw parseBackendError(text);
|
|
198
|
+
}
|
|
199
|
+
const data = await res.json();
|
|
200
|
+
return data;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/api/status.ts
|
|
204
|
+
async function getPaymentStatus(apiUrl, digest, apiKey) {
|
|
205
|
+
const res = await fetch(`${apiUrl}/payment/status?digest=${digest}`, {
|
|
206
|
+
headers: {
|
|
207
|
+
"X-API-Key": apiKey
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
if (!res.ok) {
|
|
211
|
+
const text = await res.text();
|
|
212
|
+
throw parseBackendError(text);
|
|
213
|
+
}
|
|
214
|
+
const data = await res.json();
|
|
215
|
+
return data;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/api/info.ts
|
|
219
|
+
async function getSTONInfo(apiUrl, address, apiKey) {
|
|
220
|
+
const res = await fetch(`${apiUrl}/ston/info/${address}`, {
|
|
221
|
+
headers: {
|
|
222
|
+
"X-API-Key": apiKey
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
if (!res.ok) {
|
|
226
|
+
const text = await res.text();
|
|
227
|
+
throw parseBackendError(text);
|
|
228
|
+
}
|
|
229
|
+
const data = await res.json();
|
|
230
|
+
return data;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/core/client.ts
|
|
234
|
+
var UGFClient = class {
|
|
235
|
+
/**
|
|
236
|
+
* Create a new UGF client
|
|
237
|
+
* @param config - Configuration with API URL and optional API key
|
|
238
|
+
*/
|
|
239
|
+
constructor(config) {
|
|
240
|
+
this.apiUrl = config.apiUrl;
|
|
241
|
+
if (!config.apiKey) {
|
|
242
|
+
throw new Error("API key is required");
|
|
243
|
+
}
|
|
244
|
+
this.apiKey = config.apiKey;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get a quote for gasless payment
|
|
248
|
+
* @param request - Quote request parameters
|
|
249
|
+
* @returns Quote with digest and payment details
|
|
250
|
+
*/
|
|
251
|
+
async getQuote(request) {
|
|
252
|
+
return getQuote(this.apiUrl, request, this.apiKey);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Submit a signed payment
|
|
256
|
+
* @param request - Payment submission with signature
|
|
257
|
+
* @returns Payment status
|
|
258
|
+
*/
|
|
259
|
+
async submitPayment(request) {
|
|
260
|
+
return submitPayment(this.apiUrl, request, this.apiKey);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Check payment status
|
|
264
|
+
* @param digest - Payment digest
|
|
265
|
+
* @returns Current payment status
|
|
266
|
+
*/
|
|
267
|
+
async getPaymentStatus(digest) {
|
|
268
|
+
return getPaymentStatus(this.apiUrl, digest, this.apiKey);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Get user's STON balance info
|
|
272
|
+
* @param address - User's TON address
|
|
273
|
+
* @returns STON and x402 balance details
|
|
274
|
+
*/
|
|
275
|
+
async getSTONInfo(address) {
|
|
276
|
+
return getSTONInfo(this.apiUrl, address, this.apiKey);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// src/core/config.ts
|
|
281
|
+
var DEFAULT_CONFIG = {
|
|
282
|
+
apiUrl: "https://stonfi.universalgasframework.com"
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// src/utils/formatting.ts
|
|
286
|
+
function toNano(amount) {
|
|
287
|
+
const num = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
288
|
+
return Math.floor(num * 1e9).toString();
|
|
289
|
+
}
|
|
290
|
+
function fromNano(nanotons, decimals = 6) {
|
|
291
|
+
const num = typeof nanotons === "string" ? parseFloat(nanotons) : nanotons;
|
|
292
|
+
return (num / 1e9).toFixed(decimals);
|
|
293
|
+
}
|
|
294
|
+
function formatSTON(amount, decimals = 2) {
|
|
295
|
+
return parseFloat(fromNano(amount)).toFixed(decimals);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/utils/validation.ts
|
|
299
|
+
var import_core = require("@ton/core");
|
|
300
|
+
function isValidTONAddress(address) {
|
|
301
|
+
try {
|
|
302
|
+
import_core.Address.parse(address);
|
|
303
|
+
return true;
|
|
304
|
+
} catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function isValidHex(hex) {
|
|
309
|
+
return /^[0-9a-fA-F]+$/.test(hex) && hex.length % 2 === 0;
|
|
310
|
+
}
|
|
311
|
+
function isValidGasEstimate(nanotons) {
|
|
312
|
+
return nanotons > 0 && nanotons <= 5e8;
|
|
313
|
+
}
|
|
314
|
+
function isValidBufferPct(pct) {
|
|
315
|
+
return pct >= 0 && pct <= 50;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/crypto/signature.ts
|
|
319
|
+
var import_tweetnacl = __toESM(require("tweetnacl"), 1);
|
|
320
|
+
function signDigest(digestHex, privateKeyHex) {
|
|
321
|
+
const digest = hexToBytes(digestHex);
|
|
322
|
+
const secretKey = hexToBytes(privateKeyHex);
|
|
323
|
+
const signature = import_tweetnacl.default.sign.detached(digest, secretKey);
|
|
324
|
+
return bytesToHex(signature);
|
|
325
|
+
}
|
|
326
|
+
function verifySignature(digestHex, signatureHex, publicKeyHex) {
|
|
327
|
+
const digest = hexToBytes(digestHex);
|
|
328
|
+
const signature = hexToBytes(signatureHex);
|
|
329
|
+
const publicKey = hexToBytes(publicKeyHex);
|
|
330
|
+
return import_tweetnacl.default.sign.detached.verify(digest, signature, publicKey);
|
|
331
|
+
}
|
|
332
|
+
function hexToBytes(hex) {
|
|
333
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
334
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
335
|
+
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
|
|
336
|
+
}
|
|
337
|
+
return bytes;
|
|
338
|
+
}
|
|
339
|
+
function bytesToHex(bytes) {
|
|
340
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
341
|
+
}
|
|
342
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
343
|
+
0 && (module.exports = {
|
|
344
|
+
DEFAULT_CONFIG,
|
|
345
|
+
GasLimitExceededError,
|
|
346
|
+
InsufficientBalanceError,
|
|
347
|
+
InvalidChainError,
|
|
348
|
+
InvalidPaymentCoinError,
|
|
349
|
+
InvalidSignatureError,
|
|
350
|
+
MissingDigestError,
|
|
351
|
+
NetworkError,
|
|
352
|
+
PayerMismatchError,
|
|
353
|
+
QuoteExpiredError,
|
|
354
|
+
QuoteNotFoundError,
|
|
355
|
+
StatusNotFoundError,
|
|
356
|
+
UGFClient,
|
|
357
|
+
UGFError,
|
|
358
|
+
UnauthorizedError,
|
|
359
|
+
formatSTON,
|
|
360
|
+
fromNano,
|
|
361
|
+
isValidBufferPct,
|
|
362
|
+
isValidGasEstimate,
|
|
363
|
+
isValidHex,
|
|
364
|
+
isValidTONAddress,
|
|
365
|
+
parseBackendError,
|
|
366
|
+
signDigest,
|
|
367
|
+
toNano,
|
|
368
|
+
verifySignature
|
|
369
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UGF client configuration
|
|
3
|
+
*/
|
|
4
|
+
interface UGFConfig {
|
|
5
|
+
/** UGF API base URL */
|
|
6
|
+
apiUrl: string;
|
|
7
|
+
/** API key for authentication */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Default configuration values
|
|
12
|
+
*/
|
|
13
|
+
declare const DEFAULT_CONFIG: Partial<UGFConfig>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Request to get a gasless payment quote
|
|
17
|
+
*/
|
|
18
|
+
interface QuoteRequest {
|
|
19
|
+
/** Payment token (always "STON") */
|
|
20
|
+
payment_coin: string;
|
|
21
|
+
/** User's TON wallet address */
|
|
22
|
+
payer_address: string;
|
|
23
|
+
/** Blockchain network (always "TON") */
|
|
24
|
+
payment_chain: string;
|
|
25
|
+
/** Optional transaction object */
|
|
26
|
+
tx_object?: string;
|
|
27
|
+
/** Destination chain ID (always "TON") */
|
|
28
|
+
dest_chain_id: string;
|
|
29
|
+
/** Gas estimate in nanotons */
|
|
30
|
+
gas_ton_estimate: number;
|
|
31
|
+
/** Buffer percentage (0-50, default 0) */
|
|
32
|
+
buffer_pct?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Response containing quote details and payment digest
|
|
36
|
+
*/
|
|
37
|
+
interface QuoteResponse {
|
|
38
|
+
/** STON amount required for payment (in nanotons) */
|
|
39
|
+
payment_amount: string;
|
|
40
|
+
/** Payment token (STON) */
|
|
41
|
+
payment_coin: string;
|
|
42
|
+
/** Payment blockchain (TON) */
|
|
43
|
+
payment_chain: string;
|
|
44
|
+
/** Payment destination address */
|
|
45
|
+
payment_to: string;
|
|
46
|
+
/** Payment mode identifier */
|
|
47
|
+
payment_mode: string;
|
|
48
|
+
/** Gas amount in nanotons */
|
|
49
|
+
gas_amount: string;
|
|
50
|
+
/** Unique digest for this quote */
|
|
51
|
+
digest: string;
|
|
52
|
+
/** Unix timestamp when quote expires */
|
|
53
|
+
expires_at: number;
|
|
54
|
+
/** User's locked x402 STON balance */
|
|
55
|
+
locked_x402_ston_balance: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface PaymentRequest {
|
|
59
|
+
digest: string;
|
|
60
|
+
payer_address: string;
|
|
61
|
+
signature: string;
|
|
62
|
+
public_key: string;
|
|
63
|
+
}
|
|
64
|
+
interface PaymentResponse {
|
|
65
|
+
status: string;
|
|
66
|
+
digest: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface StatusResponse {
|
|
70
|
+
status: "pending" | "completed" | "failed";
|
|
71
|
+
digest: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface STONInfoResponse {
|
|
75
|
+
address: string;
|
|
76
|
+
ston_balance: string;
|
|
77
|
+
x402_ston_balance: string;
|
|
78
|
+
escrow_address: string;
|
|
79
|
+
min_bridge_ston: string;
|
|
80
|
+
cached: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Main UGF SDK client for interacting with the Universal Gas Framework API
|
|
85
|
+
*/
|
|
86
|
+
declare class UGFClient {
|
|
87
|
+
private apiUrl;
|
|
88
|
+
private apiKey;
|
|
89
|
+
/**
|
|
90
|
+
* Create a new UGF client
|
|
91
|
+
* @param config - Configuration with API URL and optional API key
|
|
92
|
+
*/
|
|
93
|
+
constructor(config: UGFConfig);
|
|
94
|
+
/**
|
|
95
|
+
* Get a quote for gasless payment
|
|
96
|
+
* @param request - Quote request parameters
|
|
97
|
+
* @returns Quote with digest and payment details
|
|
98
|
+
*/
|
|
99
|
+
getQuote(request: QuoteRequest): Promise<QuoteResponse>;
|
|
100
|
+
/**
|
|
101
|
+
* Submit a signed payment
|
|
102
|
+
* @param request - Payment submission with signature
|
|
103
|
+
* @returns Payment status
|
|
104
|
+
*/
|
|
105
|
+
submitPayment(request: PaymentRequest): Promise<PaymentResponse>;
|
|
106
|
+
/**
|
|
107
|
+
* Check payment status
|
|
108
|
+
* @param digest - Payment digest
|
|
109
|
+
* @returns Current payment status
|
|
110
|
+
*/
|
|
111
|
+
getPaymentStatus(digest: string): Promise<StatusResponse>;
|
|
112
|
+
/**
|
|
113
|
+
* Get user's STON balance info
|
|
114
|
+
* @param address - User's TON address
|
|
115
|
+
* @returns STON and x402 balance details
|
|
116
|
+
*/
|
|
117
|
+
getSTONInfo(address: string): Promise<STONInfoResponse>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Convert TON amount to nanotons
|
|
122
|
+
*/
|
|
123
|
+
declare function toNano(amount: string | number): string;
|
|
124
|
+
/**
|
|
125
|
+
* Convert nanotons to TON
|
|
126
|
+
*/
|
|
127
|
+
declare function fromNano(nanotons: string | number, decimals?: number): string;
|
|
128
|
+
/**
|
|
129
|
+
* Format STON amount (same as TON, 9 decimals)
|
|
130
|
+
*/
|
|
131
|
+
declare function formatSTON(amount: string, decimals?: number): string;
|
|
132
|
+
|
|
133
|
+
/** Validate TON address format */
|
|
134
|
+
declare function isValidTONAddress(address: string): boolean;
|
|
135
|
+
/** Validate hex string */
|
|
136
|
+
declare function isValidHex(hex: string): boolean;
|
|
137
|
+
/** Validate gas estimate (must be > 0 and <= 0.5 TON) */
|
|
138
|
+
declare function isValidGasEstimate(nanotons: number): boolean;
|
|
139
|
+
/** Validate buffer percentage (0-50) */
|
|
140
|
+
declare function isValidBufferPct(pct: number): boolean;
|
|
141
|
+
|
|
142
|
+
/** Base error for all UGF SDK errors */
|
|
143
|
+
declare class UGFError extends Error {
|
|
144
|
+
code?: string | undefined;
|
|
145
|
+
constructor(message: string, code?: string | undefined);
|
|
146
|
+
}
|
|
147
|
+
/** Quote not found or expired */
|
|
148
|
+
declare class QuoteNotFoundError extends UGFError {
|
|
149
|
+
constructor();
|
|
150
|
+
}
|
|
151
|
+
/** Quote has expired */
|
|
152
|
+
declare class QuoteExpiredError extends UGFError {
|
|
153
|
+
constructor();
|
|
154
|
+
}
|
|
155
|
+
/** Payer address doesn't match quote */
|
|
156
|
+
declare class PayerMismatchError extends UGFError {
|
|
157
|
+
constructor();
|
|
158
|
+
}
|
|
159
|
+
/** Signature verification failed */
|
|
160
|
+
declare class InvalidSignatureError extends UGFError {
|
|
161
|
+
constructor();
|
|
162
|
+
}
|
|
163
|
+
/** Insufficient x402 STON balance */
|
|
164
|
+
declare class InsufficientBalanceError extends UGFError {
|
|
165
|
+
constructor();
|
|
166
|
+
}
|
|
167
|
+
/** Invalid blockchain chain */
|
|
168
|
+
declare class InvalidChainError extends UGFError {
|
|
169
|
+
constructor();
|
|
170
|
+
}
|
|
171
|
+
/** Invalid payment coin type */
|
|
172
|
+
declare class InvalidPaymentCoinError extends UGFError {
|
|
173
|
+
constructor();
|
|
174
|
+
}
|
|
175
|
+
/** Gas estimate exceeds 0.5 TON limit */
|
|
176
|
+
declare class GasLimitExceededError extends UGFError {
|
|
177
|
+
constructor();
|
|
178
|
+
}
|
|
179
|
+
/** Missing digest parameter */
|
|
180
|
+
declare class MissingDigestError extends UGFError {
|
|
181
|
+
constructor();
|
|
182
|
+
}
|
|
183
|
+
/** Payment status not found */
|
|
184
|
+
declare class StatusNotFoundError extends UGFError {
|
|
185
|
+
constructor();
|
|
186
|
+
}
|
|
187
|
+
/** Invalid or missing API key */
|
|
188
|
+
declare class UnauthorizedError extends UGFError {
|
|
189
|
+
constructor();
|
|
190
|
+
}
|
|
191
|
+
/** Network or server error */
|
|
192
|
+
declare class NetworkError extends UGFError {
|
|
193
|
+
constructor(message?: string);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Parse backend error message and return specific error type
|
|
197
|
+
* @param message - Error message from backend
|
|
198
|
+
* @returns Specific error instance
|
|
199
|
+
*/
|
|
200
|
+
declare function parseBackendError(message: string): Error;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Sign a digest with ed25519 private key
|
|
204
|
+
* @param digestHex - Hex string of digest to sign
|
|
205
|
+
* @param privateKeyHex - 64-byte ed25519 private key in hex
|
|
206
|
+
* @returns Signature as hex string
|
|
207
|
+
*/
|
|
208
|
+
declare function signDigest(digestHex: string, privateKeyHex: string): string;
|
|
209
|
+
/**
|
|
210
|
+
* Verify a signature
|
|
211
|
+
* @param digestHex - Original digest
|
|
212
|
+
* @param signatureHex - Signature to verify
|
|
213
|
+
* @param publicKeyHex - 32-byte ed25519 public key in hex
|
|
214
|
+
* @returns True if valid
|
|
215
|
+
*/
|
|
216
|
+
declare function verifySignature(digestHex: string, signatureHex: string, publicKeyHex: string): boolean;
|
|
217
|
+
|
|
218
|
+
export { DEFAULT_CONFIG, GasLimitExceededError, InsufficientBalanceError, InvalidChainError, InvalidPaymentCoinError, InvalidSignatureError, MissingDigestError, NetworkError, PayerMismatchError, type PaymentRequest, type PaymentResponse, QuoteExpiredError, QuoteNotFoundError, type QuoteRequest, type QuoteResponse, type STONInfoResponse, StatusNotFoundError, type StatusResponse, UGFClient, type UGFConfig, UGFError, UnauthorizedError, formatSTON, fromNano, isValidBufferPct, isValidGasEstimate, isValidHex, isValidTONAddress, parseBackendError, signDigest, toNano, verifySignature };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UGF client configuration
|
|
3
|
+
*/
|
|
4
|
+
interface UGFConfig {
|
|
5
|
+
/** UGF API base URL */
|
|
6
|
+
apiUrl: string;
|
|
7
|
+
/** API key for authentication */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Default configuration values
|
|
12
|
+
*/
|
|
13
|
+
declare const DEFAULT_CONFIG: Partial<UGFConfig>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Request to get a gasless payment quote
|
|
17
|
+
*/
|
|
18
|
+
interface QuoteRequest {
|
|
19
|
+
/** Payment token (always "STON") */
|
|
20
|
+
payment_coin: string;
|
|
21
|
+
/** User's TON wallet address */
|
|
22
|
+
payer_address: string;
|
|
23
|
+
/** Blockchain network (always "TON") */
|
|
24
|
+
payment_chain: string;
|
|
25
|
+
/** Optional transaction object */
|
|
26
|
+
tx_object?: string;
|
|
27
|
+
/** Destination chain ID (always "TON") */
|
|
28
|
+
dest_chain_id: string;
|
|
29
|
+
/** Gas estimate in nanotons */
|
|
30
|
+
gas_ton_estimate: number;
|
|
31
|
+
/** Buffer percentage (0-50, default 0) */
|
|
32
|
+
buffer_pct?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Response containing quote details and payment digest
|
|
36
|
+
*/
|
|
37
|
+
interface QuoteResponse {
|
|
38
|
+
/** STON amount required for payment (in nanotons) */
|
|
39
|
+
payment_amount: string;
|
|
40
|
+
/** Payment token (STON) */
|
|
41
|
+
payment_coin: string;
|
|
42
|
+
/** Payment blockchain (TON) */
|
|
43
|
+
payment_chain: string;
|
|
44
|
+
/** Payment destination address */
|
|
45
|
+
payment_to: string;
|
|
46
|
+
/** Payment mode identifier */
|
|
47
|
+
payment_mode: string;
|
|
48
|
+
/** Gas amount in nanotons */
|
|
49
|
+
gas_amount: string;
|
|
50
|
+
/** Unique digest for this quote */
|
|
51
|
+
digest: string;
|
|
52
|
+
/** Unix timestamp when quote expires */
|
|
53
|
+
expires_at: number;
|
|
54
|
+
/** User's locked x402 STON balance */
|
|
55
|
+
locked_x402_ston_balance: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface PaymentRequest {
|
|
59
|
+
digest: string;
|
|
60
|
+
payer_address: string;
|
|
61
|
+
signature: string;
|
|
62
|
+
public_key: string;
|
|
63
|
+
}
|
|
64
|
+
interface PaymentResponse {
|
|
65
|
+
status: string;
|
|
66
|
+
digest: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface StatusResponse {
|
|
70
|
+
status: "pending" | "completed" | "failed";
|
|
71
|
+
digest: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface STONInfoResponse {
|
|
75
|
+
address: string;
|
|
76
|
+
ston_balance: string;
|
|
77
|
+
x402_ston_balance: string;
|
|
78
|
+
escrow_address: string;
|
|
79
|
+
min_bridge_ston: string;
|
|
80
|
+
cached: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Main UGF SDK client for interacting with the Universal Gas Framework API
|
|
85
|
+
*/
|
|
86
|
+
declare class UGFClient {
|
|
87
|
+
private apiUrl;
|
|
88
|
+
private apiKey;
|
|
89
|
+
/**
|
|
90
|
+
* Create a new UGF client
|
|
91
|
+
* @param config - Configuration with API URL and optional API key
|
|
92
|
+
*/
|
|
93
|
+
constructor(config: UGFConfig);
|
|
94
|
+
/**
|
|
95
|
+
* Get a quote for gasless payment
|
|
96
|
+
* @param request - Quote request parameters
|
|
97
|
+
* @returns Quote with digest and payment details
|
|
98
|
+
*/
|
|
99
|
+
getQuote(request: QuoteRequest): Promise<QuoteResponse>;
|
|
100
|
+
/**
|
|
101
|
+
* Submit a signed payment
|
|
102
|
+
* @param request - Payment submission with signature
|
|
103
|
+
* @returns Payment status
|
|
104
|
+
*/
|
|
105
|
+
submitPayment(request: PaymentRequest): Promise<PaymentResponse>;
|
|
106
|
+
/**
|
|
107
|
+
* Check payment status
|
|
108
|
+
* @param digest - Payment digest
|
|
109
|
+
* @returns Current payment status
|
|
110
|
+
*/
|
|
111
|
+
getPaymentStatus(digest: string): Promise<StatusResponse>;
|
|
112
|
+
/**
|
|
113
|
+
* Get user's STON balance info
|
|
114
|
+
* @param address - User's TON address
|
|
115
|
+
* @returns STON and x402 balance details
|
|
116
|
+
*/
|
|
117
|
+
getSTONInfo(address: string): Promise<STONInfoResponse>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Convert TON amount to nanotons
|
|
122
|
+
*/
|
|
123
|
+
declare function toNano(amount: string | number): string;
|
|
124
|
+
/**
|
|
125
|
+
* Convert nanotons to TON
|
|
126
|
+
*/
|
|
127
|
+
declare function fromNano(nanotons: string | number, decimals?: number): string;
|
|
128
|
+
/**
|
|
129
|
+
* Format STON amount (same as TON, 9 decimals)
|
|
130
|
+
*/
|
|
131
|
+
declare function formatSTON(amount: string, decimals?: number): string;
|
|
132
|
+
|
|
133
|
+
/** Validate TON address format */
|
|
134
|
+
declare function isValidTONAddress(address: string): boolean;
|
|
135
|
+
/** Validate hex string */
|
|
136
|
+
declare function isValidHex(hex: string): boolean;
|
|
137
|
+
/** Validate gas estimate (must be > 0 and <= 0.5 TON) */
|
|
138
|
+
declare function isValidGasEstimate(nanotons: number): boolean;
|
|
139
|
+
/** Validate buffer percentage (0-50) */
|
|
140
|
+
declare function isValidBufferPct(pct: number): boolean;
|
|
141
|
+
|
|
142
|
+
/** Base error for all UGF SDK errors */
|
|
143
|
+
declare class UGFError extends Error {
|
|
144
|
+
code?: string | undefined;
|
|
145
|
+
constructor(message: string, code?: string | undefined);
|
|
146
|
+
}
|
|
147
|
+
/** Quote not found or expired */
|
|
148
|
+
declare class QuoteNotFoundError extends UGFError {
|
|
149
|
+
constructor();
|
|
150
|
+
}
|
|
151
|
+
/** Quote has expired */
|
|
152
|
+
declare class QuoteExpiredError extends UGFError {
|
|
153
|
+
constructor();
|
|
154
|
+
}
|
|
155
|
+
/** Payer address doesn't match quote */
|
|
156
|
+
declare class PayerMismatchError extends UGFError {
|
|
157
|
+
constructor();
|
|
158
|
+
}
|
|
159
|
+
/** Signature verification failed */
|
|
160
|
+
declare class InvalidSignatureError extends UGFError {
|
|
161
|
+
constructor();
|
|
162
|
+
}
|
|
163
|
+
/** Insufficient x402 STON balance */
|
|
164
|
+
declare class InsufficientBalanceError extends UGFError {
|
|
165
|
+
constructor();
|
|
166
|
+
}
|
|
167
|
+
/** Invalid blockchain chain */
|
|
168
|
+
declare class InvalidChainError extends UGFError {
|
|
169
|
+
constructor();
|
|
170
|
+
}
|
|
171
|
+
/** Invalid payment coin type */
|
|
172
|
+
declare class InvalidPaymentCoinError extends UGFError {
|
|
173
|
+
constructor();
|
|
174
|
+
}
|
|
175
|
+
/** Gas estimate exceeds 0.5 TON limit */
|
|
176
|
+
declare class GasLimitExceededError extends UGFError {
|
|
177
|
+
constructor();
|
|
178
|
+
}
|
|
179
|
+
/** Missing digest parameter */
|
|
180
|
+
declare class MissingDigestError extends UGFError {
|
|
181
|
+
constructor();
|
|
182
|
+
}
|
|
183
|
+
/** Payment status not found */
|
|
184
|
+
declare class StatusNotFoundError extends UGFError {
|
|
185
|
+
constructor();
|
|
186
|
+
}
|
|
187
|
+
/** Invalid or missing API key */
|
|
188
|
+
declare class UnauthorizedError extends UGFError {
|
|
189
|
+
constructor();
|
|
190
|
+
}
|
|
191
|
+
/** Network or server error */
|
|
192
|
+
declare class NetworkError extends UGFError {
|
|
193
|
+
constructor(message?: string);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Parse backend error message and return specific error type
|
|
197
|
+
* @param message - Error message from backend
|
|
198
|
+
* @returns Specific error instance
|
|
199
|
+
*/
|
|
200
|
+
declare function parseBackendError(message: string): Error;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Sign a digest with ed25519 private key
|
|
204
|
+
* @param digestHex - Hex string of digest to sign
|
|
205
|
+
* @param privateKeyHex - 64-byte ed25519 private key in hex
|
|
206
|
+
* @returns Signature as hex string
|
|
207
|
+
*/
|
|
208
|
+
declare function signDigest(digestHex: string, privateKeyHex: string): string;
|
|
209
|
+
/**
|
|
210
|
+
* Verify a signature
|
|
211
|
+
* @param digestHex - Original digest
|
|
212
|
+
* @param signatureHex - Signature to verify
|
|
213
|
+
* @param publicKeyHex - 32-byte ed25519 public key in hex
|
|
214
|
+
* @returns True if valid
|
|
215
|
+
*/
|
|
216
|
+
declare function verifySignature(digestHex: string, signatureHex: string, publicKeyHex: string): boolean;
|
|
217
|
+
|
|
218
|
+
export { DEFAULT_CONFIG, GasLimitExceededError, InsufficientBalanceError, InvalidChainError, InvalidPaymentCoinError, InvalidSignatureError, MissingDigestError, NetworkError, PayerMismatchError, type PaymentRequest, type PaymentResponse, QuoteExpiredError, QuoteNotFoundError, type QuoteRequest, type QuoteResponse, type STONInfoResponse, StatusNotFoundError, type StatusResponse, UGFClient, type UGFConfig, UGFError, UnauthorizedError, formatSTON, fromNano, isValidBufferPct, isValidGasEstimate, isValidHex, isValidTONAddress, parseBackendError, signDigest, toNano, verifySignature };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// src/utils/errors.ts
|
|
2
|
+
var UGFError = class extends Error {
|
|
3
|
+
constructor(message, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.name = "UGFError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
var QuoteNotFoundError = class extends UGFError {
|
|
10
|
+
constructor() {
|
|
11
|
+
super("quote not found or expired", "QUOTE_NOT_FOUND");
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var QuoteExpiredError = class extends UGFError {
|
|
15
|
+
constructor() {
|
|
16
|
+
super("quote expired", "QUOTE_EXPIRED");
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var PayerMismatchError = class extends UGFError {
|
|
20
|
+
constructor() {
|
|
21
|
+
super("payer mismatch", "PAYER_MISMATCH");
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var InvalidSignatureError = class extends UGFError {
|
|
25
|
+
constructor() {
|
|
26
|
+
super("invalid signature", "INVALID_SIGNATURE");
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var InsufficientBalanceError = class extends UGFError {
|
|
30
|
+
constructor() {
|
|
31
|
+
super("insufficient locked balance", "INSUFFICIENT_BALANCE");
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var InvalidChainError = class extends UGFError {
|
|
35
|
+
constructor() {
|
|
36
|
+
super("invalid chain", "INVALID_CHAIN");
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var InvalidPaymentCoinError = class extends UGFError {
|
|
40
|
+
constructor() {
|
|
41
|
+
super("invalid payment coin", "INVALID_COIN");
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var GasLimitExceededError = class extends UGFError {
|
|
45
|
+
constructor() {
|
|
46
|
+
super("gas exceeds 0.5 TON limit", "GAS_LIMIT_EXCEEDED");
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var MissingDigestError = class extends UGFError {
|
|
50
|
+
constructor() {
|
|
51
|
+
super("missing digest", "MISSING_DIGEST");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var StatusNotFoundError = class extends UGFError {
|
|
55
|
+
constructor() {
|
|
56
|
+
super("status not found", "STATUS_NOT_FOUND");
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var UnauthorizedError = class extends UGFError {
|
|
60
|
+
constructor() {
|
|
61
|
+
super("unauthorized", "UNAUTHORIZED");
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var NetworkError = class extends UGFError {
|
|
65
|
+
constructor(message = "network error") {
|
|
66
|
+
super(message, "NETWORK_ERROR");
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
function parseBackendError(message) {
|
|
70
|
+
const lowerMsg = message.toLowerCase();
|
|
71
|
+
if (lowerMsg.includes("quote not found") || lowerMsg.includes("expired")) {
|
|
72
|
+
return new QuoteNotFoundError();
|
|
73
|
+
}
|
|
74
|
+
if (lowerMsg.includes("quote expired")) {
|
|
75
|
+
return new QuoteExpiredError();
|
|
76
|
+
}
|
|
77
|
+
if (lowerMsg.includes("payer mismatch")) {
|
|
78
|
+
return new PayerMismatchError();
|
|
79
|
+
}
|
|
80
|
+
if (lowerMsg.includes("invalid signature")) {
|
|
81
|
+
return new InvalidSignatureError();
|
|
82
|
+
}
|
|
83
|
+
if (lowerMsg.includes("insufficient")) {
|
|
84
|
+
return new InsufficientBalanceError();
|
|
85
|
+
}
|
|
86
|
+
if (lowerMsg.includes("invalid chain")) {
|
|
87
|
+
return new InvalidChainError();
|
|
88
|
+
}
|
|
89
|
+
if (lowerMsg.includes("invalid payment coin")) {
|
|
90
|
+
return new InvalidPaymentCoinError();
|
|
91
|
+
}
|
|
92
|
+
if (lowerMsg.includes("gas exceeds")) {
|
|
93
|
+
return new GasLimitExceededError();
|
|
94
|
+
}
|
|
95
|
+
if (lowerMsg.includes("missing digest")) {
|
|
96
|
+
return new MissingDigestError();
|
|
97
|
+
}
|
|
98
|
+
if (lowerMsg.includes("status not found")) {
|
|
99
|
+
return new StatusNotFoundError();
|
|
100
|
+
}
|
|
101
|
+
if (lowerMsg.includes("unauthorized")) {
|
|
102
|
+
return new UnauthorizedError();
|
|
103
|
+
}
|
|
104
|
+
return new NetworkError(message);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/api/quote.ts
|
|
108
|
+
async function getQuote(apiUrl, request, apiKey) {
|
|
109
|
+
const res = await fetch(`${apiUrl}/quote`, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: {
|
|
112
|
+
"Content-Type": "application/json",
|
|
113
|
+
"X-API-Key": apiKey
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify(request)
|
|
116
|
+
});
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
const text = await res.text();
|
|
119
|
+
throw parseBackendError(text);
|
|
120
|
+
}
|
|
121
|
+
const data = await res.json();
|
|
122
|
+
return data;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/api/payment.ts
|
|
126
|
+
async function submitPayment(apiUrl, request, apiKey) {
|
|
127
|
+
const res = await fetch(`${apiUrl}/payment/submit`, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers: {
|
|
130
|
+
"Content-Type": "application/json",
|
|
131
|
+
"X-API-Key": apiKey
|
|
132
|
+
},
|
|
133
|
+
body: JSON.stringify(request)
|
|
134
|
+
});
|
|
135
|
+
if (!res.ok) {
|
|
136
|
+
const text = await res.text();
|
|
137
|
+
throw parseBackendError(text);
|
|
138
|
+
}
|
|
139
|
+
const data = await res.json();
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/api/status.ts
|
|
144
|
+
async function getPaymentStatus(apiUrl, digest, apiKey) {
|
|
145
|
+
const res = await fetch(`${apiUrl}/payment/status?digest=${digest}`, {
|
|
146
|
+
headers: {
|
|
147
|
+
"X-API-Key": apiKey
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
if (!res.ok) {
|
|
151
|
+
const text = await res.text();
|
|
152
|
+
throw parseBackendError(text);
|
|
153
|
+
}
|
|
154
|
+
const data = await res.json();
|
|
155
|
+
return data;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/api/info.ts
|
|
159
|
+
async function getSTONInfo(apiUrl, address, apiKey) {
|
|
160
|
+
const res = await fetch(`${apiUrl}/ston/info/${address}`, {
|
|
161
|
+
headers: {
|
|
162
|
+
"X-API-Key": apiKey
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
if (!res.ok) {
|
|
166
|
+
const text = await res.text();
|
|
167
|
+
throw parseBackendError(text);
|
|
168
|
+
}
|
|
169
|
+
const data = await res.json();
|
|
170
|
+
return data;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/core/client.ts
|
|
174
|
+
var UGFClient = class {
|
|
175
|
+
/**
|
|
176
|
+
* Create a new UGF client
|
|
177
|
+
* @param config - Configuration with API URL and optional API key
|
|
178
|
+
*/
|
|
179
|
+
constructor(config) {
|
|
180
|
+
this.apiUrl = config.apiUrl;
|
|
181
|
+
if (!config.apiKey) {
|
|
182
|
+
throw new Error("API key is required");
|
|
183
|
+
}
|
|
184
|
+
this.apiKey = config.apiKey;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get a quote for gasless payment
|
|
188
|
+
* @param request - Quote request parameters
|
|
189
|
+
* @returns Quote with digest and payment details
|
|
190
|
+
*/
|
|
191
|
+
async getQuote(request) {
|
|
192
|
+
return getQuote(this.apiUrl, request, this.apiKey);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Submit a signed payment
|
|
196
|
+
* @param request - Payment submission with signature
|
|
197
|
+
* @returns Payment status
|
|
198
|
+
*/
|
|
199
|
+
async submitPayment(request) {
|
|
200
|
+
return submitPayment(this.apiUrl, request, this.apiKey);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Check payment status
|
|
204
|
+
* @param digest - Payment digest
|
|
205
|
+
* @returns Current payment status
|
|
206
|
+
*/
|
|
207
|
+
async getPaymentStatus(digest) {
|
|
208
|
+
return getPaymentStatus(this.apiUrl, digest, this.apiKey);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get user's STON balance info
|
|
212
|
+
* @param address - User's TON address
|
|
213
|
+
* @returns STON and x402 balance details
|
|
214
|
+
*/
|
|
215
|
+
async getSTONInfo(address) {
|
|
216
|
+
return getSTONInfo(this.apiUrl, address, this.apiKey);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// src/core/config.ts
|
|
221
|
+
var DEFAULT_CONFIG = {
|
|
222
|
+
apiUrl: "https://stonfi.universalgasframework.com"
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// src/utils/formatting.ts
|
|
226
|
+
function toNano(amount) {
|
|
227
|
+
const num = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
228
|
+
return Math.floor(num * 1e9).toString();
|
|
229
|
+
}
|
|
230
|
+
function fromNano(nanotons, decimals = 6) {
|
|
231
|
+
const num = typeof nanotons === "string" ? parseFloat(nanotons) : nanotons;
|
|
232
|
+
return (num / 1e9).toFixed(decimals);
|
|
233
|
+
}
|
|
234
|
+
function formatSTON(amount, decimals = 2) {
|
|
235
|
+
return parseFloat(fromNano(amount)).toFixed(decimals);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/utils/validation.ts
|
|
239
|
+
import { Address } from "@ton/core";
|
|
240
|
+
function isValidTONAddress(address) {
|
|
241
|
+
try {
|
|
242
|
+
Address.parse(address);
|
|
243
|
+
return true;
|
|
244
|
+
} catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function isValidHex(hex) {
|
|
249
|
+
return /^[0-9a-fA-F]+$/.test(hex) && hex.length % 2 === 0;
|
|
250
|
+
}
|
|
251
|
+
function isValidGasEstimate(nanotons) {
|
|
252
|
+
return nanotons > 0 && nanotons <= 5e8;
|
|
253
|
+
}
|
|
254
|
+
function isValidBufferPct(pct) {
|
|
255
|
+
return pct >= 0 && pct <= 50;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/crypto/signature.ts
|
|
259
|
+
import nacl from "tweetnacl";
|
|
260
|
+
function signDigest(digestHex, privateKeyHex) {
|
|
261
|
+
const digest = hexToBytes(digestHex);
|
|
262
|
+
const secretKey = hexToBytes(privateKeyHex);
|
|
263
|
+
const signature = nacl.sign.detached(digest, secretKey);
|
|
264
|
+
return bytesToHex(signature);
|
|
265
|
+
}
|
|
266
|
+
function verifySignature(digestHex, signatureHex, publicKeyHex) {
|
|
267
|
+
const digest = hexToBytes(digestHex);
|
|
268
|
+
const signature = hexToBytes(signatureHex);
|
|
269
|
+
const publicKey = hexToBytes(publicKeyHex);
|
|
270
|
+
return nacl.sign.detached.verify(digest, signature, publicKey);
|
|
271
|
+
}
|
|
272
|
+
function hexToBytes(hex) {
|
|
273
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
274
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
275
|
+
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
|
|
276
|
+
}
|
|
277
|
+
return bytes;
|
|
278
|
+
}
|
|
279
|
+
function bytesToHex(bytes) {
|
|
280
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
281
|
+
}
|
|
282
|
+
export {
|
|
283
|
+
DEFAULT_CONFIG,
|
|
284
|
+
GasLimitExceededError,
|
|
285
|
+
InsufficientBalanceError,
|
|
286
|
+
InvalidChainError,
|
|
287
|
+
InvalidPaymentCoinError,
|
|
288
|
+
InvalidSignatureError,
|
|
289
|
+
MissingDigestError,
|
|
290
|
+
NetworkError,
|
|
291
|
+
PayerMismatchError,
|
|
292
|
+
QuoteExpiredError,
|
|
293
|
+
QuoteNotFoundError,
|
|
294
|
+
StatusNotFoundError,
|
|
295
|
+
UGFClient,
|
|
296
|
+
UGFError,
|
|
297
|
+
UnauthorizedError,
|
|
298
|
+
formatSTON,
|
|
299
|
+
fromNano,
|
|
300
|
+
isValidBufferPct,
|
|
301
|
+
isValidGasEstimate,
|
|
302
|
+
isValidHex,
|
|
303
|
+
isValidTONAddress,
|
|
304
|
+
parseBackendError,
|
|
305
|
+
signDigest,
|
|
306
|
+
toNano,
|
|
307
|
+
verifySignature
|
|
308
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tychilabs/stonfi-gasless-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Universal Gas Framework SDK for gasless payments - pay TON transaction fees with STON token",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"ton",
|
|
13
|
+
"tonchain",
|
|
14
|
+
"ston",
|
|
15
|
+
"stonfi",
|
|
16
|
+
"gasless",
|
|
17
|
+
"gas-fee",
|
|
18
|
+
"ugf",
|
|
19
|
+
"universal-gas",
|
|
20
|
+
"defi",
|
|
21
|
+
"blockchain",
|
|
22
|
+
"payments",
|
|
23
|
+
"transaction"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://github.com/TychiWallet/stonfi-gasless-sdk#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/TychiWallet/stonfi-gasless-sdk/issues"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/TychiWallet/stonfi-gasless-sdk.git"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"author": "yash@tychilabs.com",
|
|
35
|
+
"type": "module",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
38
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
39
|
+
"prepublishOnly": "npm run build"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^25.1.0",
|
|
43
|
+
"tsup": "^8.5.1",
|
|
44
|
+
"typescript": "^5.9.3"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"tweetnacl": "^1.0.3"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@ton/core": ">=0.63.0"
|
|
51
|
+
}
|
|
52
|
+
}
|