@velumx/sdk 2.0.0 → 2.1.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 +596 -186
- package/dist/VelumXClient.js +3 -0
- package/package.json +2 -2
- package/src/VelumXClient.ts +3 -0
package/README.md
CHANGED
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @velumx/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Gasless transaction SDK for Stacks - Pay fees in USDCx, not STX
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@velumx/sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- **Account Abstraction**: Smart Wallet pattern with SIP-018 signing
|
|
9
|
-
- **Simple Integration**: 3 lines of code to add gasless support
|
|
10
|
-
- **Production Ready**: Battle-tested relayer infrastructure
|
|
11
|
-
- **Developer Friendly**: TypeScript SDK with full type safety
|
|
8
|
+
## Overview
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
VelumX SDK enables gasless transactions on Stacks blockchain. Users pay transaction fees in USDCx instead of STX using Stacks' native sponsored transaction feature.
|
|
11
|
+
|
|
12
|
+
### Key Features
|
|
13
|
+
|
|
14
|
+
- � **Zero STX Required** - Users only need USDCx
|
|
15
|
+
- ⚡ **Native Sponsorship** - Uses Stacks' built-in `sponsored` flag
|
|
16
|
+
- 🔧 **Simple Integration** - 3 lines of code
|
|
17
|
+
- 📦 **Lightweight** - ~50KB minified
|
|
18
|
+
- � **Secure** - No smart wallet complexity
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
14
21
|
|
|
15
22
|
```bash
|
|
16
23
|
npm install @velumx/sdk
|
|
17
24
|
```
|
|
18
25
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
### 1. Get API Key
|
|
26
|
+
## Quick Start
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
### 2. Initialize Client
|
|
28
|
+
### 1. Initialize Client
|
|
26
29
|
|
|
27
30
|
```typescript
|
|
28
|
-
import {
|
|
31
|
+
import { getVelumXClient } from '@velumx/sdk';
|
|
29
32
|
|
|
30
|
-
const velumx =
|
|
31
|
-
coreApiUrl: 'https://api.testnet.hiro.so',
|
|
32
|
-
network: 'testnet',
|
|
33
|
-
paymasterUrl: 'https://relayer.velumx.com/api/v1',
|
|
34
|
-
apiKey: 'your-api-key-here'
|
|
35
|
-
});
|
|
33
|
+
const velumx = getVelumXClient();
|
|
36
34
|
```
|
|
37
35
|
|
|
38
|
-
###
|
|
36
|
+
### 2. Estimate Fee
|
|
39
37
|
|
|
40
38
|
```typescript
|
|
41
39
|
const estimate = await velumx.estimateFee({
|
|
@@ -43,263 +41,675 @@ const estimate = await velumx.estimateFee({
|
|
|
43
41
|
});
|
|
44
42
|
|
|
45
43
|
console.log(`Fee: ${estimate.maxFeeUSDCx} micro-USDCx`);
|
|
44
|
+
// Output: Fee: 540000 micro-USDCx (0.54 USDCx)
|
|
46
45
|
```
|
|
47
46
|
|
|
48
|
-
###
|
|
47
|
+
### 3. Execute Gasless Transaction
|
|
49
48
|
|
|
50
49
|
```typescript
|
|
51
|
-
import {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
import { openContractCall } from '@stacks/connect';
|
|
51
|
+
import { Cl } from '@stacks/transactions';
|
|
52
|
+
|
|
53
|
+
// Call paymaster contract with sponsored=true
|
|
54
|
+
const result = await openContractCall({
|
|
55
|
+
contractAddress: 'STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P',
|
|
56
|
+
contractName: 'simple-paymaster-v1',
|
|
57
|
+
functionName: 'bridge-gasless',
|
|
58
|
+
functionArgs: [
|
|
59
|
+
Cl.uint(10000000), // 10 USDCx
|
|
60
|
+
Cl.buffer(recipientBytes),
|
|
61
|
+
Cl.uint(estimate.maxFeeUSDCx),
|
|
62
|
+
Cl.principal('STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P'),
|
|
63
|
+
Cl.principal('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usdcx')
|
|
64
|
+
],
|
|
65
|
+
sponsored: true, // Enable gasless mode
|
|
66
|
+
network: 'testnet',
|
|
67
|
+
onFinish: async (data) => {
|
|
68
|
+
// Submit to relayer for sponsorship
|
|
69
|
+
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
70
|
+
console.log(`Transaction: ${tx.txid}`);
|
|
71
|
+
}
|
|
57
72
|
});
|
|
73
|
+
```
|
|
58
74
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
payload: serializeCV(payload),
|
|
63
|
-
maxFeeUSDCx: estimate.maxFeeUSDCx,
|
|
64
|
-
nonce: currentNonce
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Sign with user's wallet (SIP-018)
|
|
68
|
-
const signature = await signWithWallet(intent);
|
|
69
|
-
|
|
70
|
-
// Submit to relayer
|
|
71
|
-
const result = await velumx.submitIntent({
|
|
72
|
-
...intent,
|
|
73
|
-
signature
|
|
74
|
-
});
|
|
75
|
+
## How It Works
|
|
76
|
+
|
|
77
|
+
### Architecture
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
```
|
|
80
|
+
┌──────────┐ ┌──────────┐
|
|
81
|
+
│ User │ │ Relayer │
|
|
82
|
+
└────┬─────┘ └────┬─────┘
|
|
83
|
+
│ │
|
|
84
|
+
│ 1. Request fee estimate │
|
|
85
|
+
├──────────────────────────────────────────────► │
|
|
86
|
+
│ │
|
|
87
|
+
│ 2. Return fee in USDCx │
|
|
88
|
+
│ ◄──────────────────────────────────────────────┤
|
|
89
|
+
│ │
|
|
90
|
+
│ 3. Sign transaction (sponsored=true) │
|
|
91
|
+
│ │
|
|
92
|
+
│ 4. Submit signed transaction │
|
|
93
|
+
├──────────────────────────────────────────────► │
|
|
94
|
+
│ │
|
|
95
|
+
│ │ 5. Sponsor with STX
|
|
96
|
+
│ │ & broadcast
|
|
97
|
+
│ │
|
|
98
|
+
│ 6. Return transaction ID │
|
|
99
|
+
│ ◄──────────────────────────────────────────────┤
|
|
100
|
+
│ │
|
|
101
|
+
▼ ▼
|
|
102
|
+
|
|
103
|
+
┌─────────────────────────────────────────────────────┐
|
|
104
|
+
│ Stacks Blockchain │
|
|
105
|
+
│ │
|
|
106
|
+
│ simple-paymaster-v1::bridge-gasless │
|
|
107
|
+
│ • Transfer USDCx fee from user to relayer │
|
|
108
|
+
│ • Execute core logic (burn/swap) │
|
|
109
|
+
│ • Transaction confirmed ✓ │
|
|
110
|
+
└─────────────────────────────────────────────────────┘
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Fee Calculation
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
Fee in any SIP-010 token = (Gas Cost in STX × STX/USD Rate × 1.08) / Token/USD Rate
|
|
117
|
+
|
|
118
|
+
Example with USDCx:
|
|
119
|
+
- Gas: 100,000 units = 1 STX
|
|
120
|
+
- STX/USD: $0.50
|
|
121
|
+
- USDC/USD: $1.00
|
|
122
|
+
- Markup: 8%
|
|
123
|
+
- Fee: 1 × $0.50 × 1.08 / $1.00 = 0.54 USDCx
|
|
124
|
+
|
|
125
|
+
Example with sBTC:
|
|
126
|
+
- Gas: 100,000 units = 1 STX
|
|
127
|
+
- STX/USD: $0.50
|
|
128
|
+
- BTC/USD: $45,000
|
|
129
|
+
- Markup: 8%
|
|
130
|
+
- Fee: 1 × $0.50 × 1.08 / $45,000 = 0.000012 BTC (1,200 sats)
|
|
131
|
+
|
|
132
|
+
Example with ALEX:
|
|
133
|
+
- Gas: 100,000 units = 1 STX
|
|
134
|
+
- STX/USD: $0.50
|
|
135
|
+
- ALEX/USD: $0.10
|
|
136
|
+
- Markup: 8%
|
|
137
|
+
- Fee: 1 × $0.50 × 1.08 / $0.10 = 5.4 ALEX
|
|
77
138
|
```
|
|
78
139
|
|
|
79
|
-
##
|
|
140
|
+
## API Reference
|
|
80
141
|
|
|
81
142
|
### VelumXClient
|
|
82
143
|
|
|
83
|
-
####
|
|
144
|
+
#### Configuration
|
|
84
145
|
|
|
85
146
|
```typescript
|
|
86
|
-
|
|
147
|
+
interface NetworkConfig {
|
|
148
|
+
coreApiUrl: string; // Stacks API URL
|
|
149
|
+
network: 'mainnet' | 'testnet' | 'devnet';
|
|
150
|
+
paymasterUrl?: string; // Relayer URL (optional)
|
|
151
|
+
}
|
|
87
152
|
```
|
|
88
153
|
|
|
89
|
-
**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
154
|
+
**Default Configuration**:
|
|
155
|
+
```typescript
|
|
156
|
+
{
|
|
157
|
+
coreApiUrl: 'https://api.testnet.hiro.so',
|
|
158
|
+
network: 'testnet',
|
|
159
|
+
paymasterUrl: 'https://sgal-relayer.onrender.com/api/v1'
|
|
160
|
+
}
|
|
161
|
+
```
|
|
94
162
|
|
|
95
163
|
#### Methods
|
|
96
164
|
|
|
97
165
|
##### estimateFee()
|
|
98
166
|
|
|
167
|
+
Get fee estimate in USDCx for a transaction.
|
|
168
|
+
|
|
99
169
|
```typescript
|
|
100
|
-
estimateFee(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}>
|
|
170
|
+
estimateFee(params: {
|
|
171
|
+
estimatedGas: number
|
|
172
|
+
}): Promise<FeeEstimate>
|
|
104
173
|
```
|
|
105
174
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
##### submitIntent()
|
|
175
|
+
**Parameters**:
|
|
176
|
+
- `estimatedGas`: Estimated gas units (e.g., 100000)
|
|
109
177
|
|
|
178
|
+
**Returns**:
|
|
110
179
|
```typescript
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
180
|
+
interface FeeEstimate {
|
|
181
|
+
maxFeeUSDCx: string; // Fee in micro-USDCx
|
|
182
|
+
estimatedGas: number; // Gas units
|
|
183
|
+
stxToUsd?: number; // Exchange rate
|
|
184
|
+
markup?: number; // Fee markup (0.08 = 8%)
|
|
185
|
+
}
|
|
115
186
|
```
|
|
116
187
|
|
|
117
|
-
|
|
188
|
+
**Example**:
|
|
189
|
+
```typescript
|
|
190
|
+
const estimate = await velumx.estimateFee({
|
|
191
|
+
estimatedGas: 100000
|
|
192
|
+
});
|
|
118
193
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- `maxFeeUSDCx`: Maximum fee in micro-USDCx
|
|
123
|
-
- `nonce`: Smart Wallet nonce
|
|
124
|
-
- `signature`: SIP-018 signature
|
|
194
|
+
console.log(`Fee: ${estimate.maxFeeUSDCx} micro-USDCx`);
|
|
195
|
+
// Fee: 540000 micro-USDCx (0.54 USDCx)
|
|
196
|
+
```
|
|
125
197
|
|
|
126
198
|
##### submitRawTransaction()
|
|
127
199
|
|
|
200
|
+
Submit a signed transaction for sponsorship.
|
|
201
|
+
|
|
128
202
|
```typescript
|
|
129
|
-
submitRawTransaction(
|
|
130
|
-
txid: string;
|
|
131
|
-
status: string;
|
|
132
|
-
}>
|
|
203
|
+
submitRawTransaction(txRaw: string): Promise<TransactionResult>
|
|
133
204
|
```
|
|
134
205
|
|
|
135
|
-
|
|
206
|
+
**Parameters**:
|
|
207
|
+
- `txRaw`: Hex-encoded signed transaction from wallet
|
|
136
208
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
209
|
+
**Returns**:
|
|
210
|
+
```typescript
|
|
211
|
+
interface TransactionResult {
|
|
212
|
+
txid: string; // Transaction ID
|
|
213
|
+
status: string; // Status (pending/success/failed)
|
|
214
|
+
}
|
|
215
|
+
```
|
|
140
216
|
|
|
217
|
+
**Example**:
|
|
141
218
|
```typescript
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
amount: uintCV(5000000), // 5 USDCx
|
|
146
|
-
fee: uintCV(250000), // 0.25 USDCx fee
|
|
147
|
-
recipient: bufferCV(ethAddressBytes)
|
|
148
|
-
});
|
|
219
|
+
const result = await velumx.submitRawTransaction(data.txRaw);
|
|
220
|
+
console.log(`Transaction ID: ${result.txid}`);
|
|
221
|
+
```
|
|
149
222
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
223
|
+
##### sponsorTransaction()
|
|
224
|
+
|
|
225
|
+
High-level helper to make any transaction gasless.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
sponsorTransaction(params: {
|
|
229
|
+
transaction: any;
|
|
230
|
+
network: 'mainnet' | 'testnet';
|
|
231
|
+
}): Promise<any>
|
|
157
232
|
```
|
|
158
233
|
|
|
159
|
-
|
|
234
|
+
**Parameters**:
|
|
235
|
+
- `transaction`: Unsigned Stacks transaction
|
|
236
|
+
- `network`: Target network
|
|
237
|
+
|
|
238
|
+
**Returns**: Transaction with `sponsored: true` flag
|
|
160
239
|
|
|
240
|
+
**Example**:
|
|
161
241
|
```typescript
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
tokenOut: principalCV('ST...TOKEN-B'),
|
|
166
|
-
amountIn: uintCV(1000000),
|
|
167
|
-
minOut: uintCV(950000),
|
|
168
|
-
fee: uintCV(200000)
|
|
169
|
-
});
|
|
242
|
+
import { makeContractCall } from '@stacks/transactions';
|
|
243
|
+
|
|
244
|
+
const unsignedTx = await makeContractCall({...});
|
|
170
245
|
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
maxFeeUSDCx: '200000',
|
|
175
|
-
nonce: 1,
|
|
176
|
-
signature: userSignature
|
|
246
|
+
const sponsored = await velumx.sponsorTransaction({
|
|
247
|
+
transaction: unsignedTx,
|
|
248
|
+
network: 'testnet'
|
|
177
249
|
});
|
|
250
|
+
|
|
251
|
+
// User signs and broadcasts
|
|
252
|
+
const result = await openContractCall(sponsored);
|
|
178
253
|
```
|
|
179
254
|
|
|
180
|
-
|
|
255
|
+
## Use Cases
|
|
256
|
+
|
|
257
|
+
### 1. Gasless Bridge
|
|
258
|
+
|
|
259
|
+
Bridge USDC from Ethereum to Stacks without needing STX.
|
|
181
260
|
|
|
182
261
|
```typescript
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
262
|
+
import { getVelumXClient } from '@velumx/sdk';
|
|
263
|
+
import { openContractCall } from '@stacks/connect';
|
|
264
|
+
import { Cl } from '@stacks/transactions';
|
|
265
|
+
import { parseUnits } from 'viem';
|
|
266
|
+
|
|
267
|
+
async function gaslessBridge(amount: string, recipient: string) {
|
|
268
|
+
const velumx = getVelumXClient();
|
|
269
|
+
|
|
270
|
+
// 1. Estimate fee
|
|
271
|
+
const estimate = await velumx.estimateFee({
|
|
272
|
+
estimatedGas: 100000
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// 2. Encode Ethereum address
|
|
276
|
+
const recipientBytes = encodeEthereumAddress(recipient);
|
|
277
|
+
|
|
278
|
+
// 3. Execute gasless bridge
|
|
279
|
+
const result = await openContractCall({
|
|
280
|
+
contractAddress: 'STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P',
|
|
281
|
+
contractName: 'simple-paymaster-v1',
|
|
282
|
+
functionName: 'bridge-gasless',
|
|
283
|
+
functionArgs: [
|
|
284
|
+
Cl.uint(parseUnits(amount, 6)),
|
|
285
|
+
Cl.buffer(recipientBytes),
|
|
286
|
+
Cl.uint(estimate.maxFeeUSDCx),
|
|
287
|
+
Cl.principal('STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P'),
|
|
288
|
+
Cl.principal('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usdcx')
|
|
289
|
+
],
|
|
290
|
+
sponsored: true,
|
|
291
|
+
network: 'testnet',
|
|
292
|
+
onFinish: async (data) => {
|
|
293
|
+
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
294
|
+
console.log(`Bridge transaction: ${tx.txid}`);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
189
298
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
299
|
+
// Helper function
|
|
300
|
+
function encodeEthereumAddress(address: string): Uint8Array {
|
|
301
|
+
const hex = address.startsWith('0x') ? address.slice(2) : address;
|
|
302
|
+
const paddedHex = hex.padStart(64, '0');
|
|
303
|
+
const bytes = new Uint8Array(32);
|
|
304
|
+
for (let i = 0; i < 32; i++) {
|
|
305
|
+
bytes[i] = parseInt(paddedHex.substring(i * 2, i * 2 + 2), 16);
|
|
306
|
+
}
|
|
307
|
+
return bytes;
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### 2. Gasless Swap
|
|
312
|
+
|
|
313
|
+
Swap tokens without holding STX.
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
async function gaslessSwap(
|
|
317
|
+
tokenIn: string,
|
|
318
|
+
tokenOut: string,
|
|
319
|
+
amountIn: string,
|
|
320
|
+
minOut: string
|
|
321
|
+
) {
|
|
322
|
+
const velumx = getVelumXClient();
|
|
323
|
+
|
|
324
|
+
// 1. Estimate fee
|
|
325
|
+
const estimate = await velumx.estimateFee({
|
|
326
|
+
estimatedGas: 150000
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// 2. Execute gasless swap
|
|
330
|
+
const result = await openContractCall({
|
|
331
|
+
contractAddress: 'STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P',
|
|
332
|
+
contractName: 'simple-paymaster-v1',
|
|
333
|
+
functionName: 'swap-gasless',
|
|
334
|
+
functionArgs: [
|
|
335
|
+
Cl.principal(tokenIn),
|
|
336
|
+
Cl.principal(tokenOut),
|
|
337
|
+
Cl.uint(parseUnits(amountIn, 6)),
|
|
338
|
+
Cl.uint(parseUnits(minOut, 6)),
|
|
339
|
+
Cl.uint(estimate.maxFeeUSDCx),
|
|
340
|
+
Cl.principal('STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P'),
|
|
341
|
+
Cl.principal('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usdcx')
|
|
342
|
+
],
|
|
343
|
+
sponsored: true,
|
|
344
|
+
network: 'testnet',
|
|
345
|
+
onFinish: async (data) => {
|
|
346
|
+
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
347
|
+
console.log(`Swap transaction: ${tx.txid}`);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
197
351
|
```
|
|
198
352
|
|
|
199
|
-
|
|
353
|
+
### 3. Custom Gasless Transaction
|
|
200
354
|
|
|
201
|
-
|
|
355
|
+
Make any contract call gasless.
|
|
202
356
|
|
|
203
357
|
```typescript
|
|
204
|
-
|
|
358
|
+
async function customGaslessTransaction() {
|
|
359
|
+
const velumx = getVelumXClient();
|
|
360
|
+
|
|
361
|
+
// 1. Estimate fee
|
|
362
|
+
const estimate = await velumx.estimateFee({
|
|
363
|
+
estimatedGas: 120000
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// 2. Your custom contract call
|
|
367
|
+
const result = await openContractCall({
|
|
368
|
+
contractAddress: 'YOUR_CONTRACT_ADDRESS',
|
|
369
|
+
contractName: 'your-contract',
|
|
370
|
+
functionName: 'your-function',
|
|
371
|
+
functionArgs: [
|
|
372
|
+
// Your function args
|
|
373
|
+
Cl.uint(estimate.maxFeeUSDCx), // Include fee
|
|
374
|
+
// More args...
|
|
375
|
+
],
|
|
376
|
+
sponsored: true, // Enable gasless
|
|
377
|
+
network: 'testnet',
|
|
378
|
+
onFinish: async (data) => {
|
|
379
|
+
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
380
|
+
console.log(`Transaction: ${tx.txid}`);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### 4. Using Different Fee Tokens
|
|
205
387
|
|
|
206
|
-
|
|
388
|
+
Pay fees in sBTC, ALEX, or any SIP-010 token.
|
|
207
389
|
|
|
208
|
-
|
|
209
|
-
|
|
390
|
+
```typescript
|
|
391
|
+
async function gaslessWithSBTC(amount: string) {
|
|
392
|
+
const velumx = getVelumXClient();
|
|
393
|
+
|
|
394
|
+
// 1. Estimate fee (returns USDCx equivalent)
|
|
395
|
+
const estimate = await velumx.estimateFee({
|
|
396
|
+
estimatedGas: 100000
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// 2. Convert fee to sBTC (example: 0.54 USDCx → 0.000012 BTC)
|
|
400
|
+
const feeInSBTC = convertToSBTC(estimate.maxFeeUSDCx);
|
|
401
|
+
|
|
402
|
+
// 3. Execute with sBTC as fee token
|
|
403
|
+
const result = await openContractCall({
|
|
404
|
+
contractAddress: 'STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P',
|
|
405
|
+
contractName: 'simple-paymaster-v1',
|
|
406
|
+
functionName: 'bridge-gasless',
|
|
407
|
+
functionArgs: [
|
|
408
|
+
Cl.uint(parseUnits(amount, 6)),
|
|
409
|
+
Cl.buffer(recipientBytes),
|
|
410
|
+
Cl.uint(feeInSBTC), // Fee in sBTC
|
|
411
|
+
Cl.principal('STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P'),
|
|
412
|
+
Cl.principal('SM3KNVZS30WM7F89SXKVVFY4SN9RMPZZ9FX929N0V.sbtc') // sBTC token
|
|
413
|
+
],
|
|
414
|
+
sponsored: true,
|
|
415
|
+
network: 'testnet',
|
|
416
|
+
onFinish: async (data) => {
|
|
417
|
+
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
418
|
+
console.log(`Transaction: ${tx.txid}`);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Helper: Convert USDCx fee to sBTC
|
|
424
|
+
function convertToSBTC(feeInUSDCx: string): string {
|
|
425
|
+
// Example conversion (fetch real rates from API)
|
|
426
|
+
const usdcAmount = Number(feeInUSDCx) / 1_000_000; // 0.54 USDC
|
|
427
|
+
const btcPrice = 45000; // $45,000 per BTC
|
|
428
|
+
const btcAmount = usdcAmount / btcPrice; // 0.000012 BTC
|
|
429
|
+
const satoshis = Math.ceil(btcAmount * 100_000_000); // 1,200 sats
|
|
430
|
+
return satoshis.toString();
|
|
431
|
+
}
|
|
210
432
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
433
|
+
// Using ALEX token
|
|
434
|
+
async function gaslessWithALEX(amount: string) {
|
|
435
|
+
const velumx = getVelumXClient();
|
|
436
|
+
|
|
437
|
+
const estimate = await velumx.estimateFee({
|
|
438
|
+
estimatedGas: 100000
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Convert to ALEX (example: 0.54 USDCx → 5.4 ALEX)
|
|
442
|
+
const feeInALEX = convertToALEX(estimate.maxFeeUSDCx);
|
|
443
|
+
|
|
444
|
+
const result = await openContractCall({
|
|
445
|
+
contractAddress: 'STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P',
|
|
446
|
+
contractName: 'simple-paymaster-v1',
|
|
447
|
+
functionName: 'bridge-gasless',
|
|
448
|
+
functionArgs: [
|
|
449
|
+
Cl.uint(parseUnits(amount, 6)),
|
|
450
|
+
Cl.buffer(recipientBytes),
|
|
451
|
+
Cl.uint(feeInALEX), // Fee in ALEX
|
|
452
|
+
Cl.principal('STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P'),
|
|
453
|
+
Cl.principal('ALEX_TOKEN_ADDRESS') // ALEX token
|
|
454
|
+
],
|
|
455
|
+
sponsored: true,
|
|
456
|
+
network: 'testnet',
|
|
457
|
+
onFinish: async (data) => {
|
|
458
|
+
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
459
|
+
console.log(`Transaction: ${tx.txid}`);
|
|
460
|
+
}
|
|
461
|
+
});
|
|
215
462
|
}
|
|
216
463
|
```
|
|
217
464
|
|
|
218
|
-
##
|
|
465
|
+
## Smart Contract Integration
|
|
466
|
+
|
|
467
|
+
To make your contract gasless-compatible, accept a fee parameter and transfer it to the relayer:
|
|
468
|
+
|
|
469
|
+
```clarity
|
|
470
|
+
(define-public (your-gasless-function
|
|
471
|
+
(amount uint)
|
|
472
|
+
(fee-usdcx uint)
|
|
473
|
+
(relayer principal)
|
|
474
|
+
(fee-token <sip-010-trait>))
|
|
475
|
+
(begin
|
|
476
|
+
;; 1. Transfer fee from user to relayer
|
|
477
|
+
(try! (contract-call? fee-token transfer
|
|
478
|
+
fee-usdcx tx-sender relayer none))
|
|
479
|
+
|
|
480
|
+
;; 2. Your contract logic
|
|
481
|
+
(try! (your-logic amount))
|
|
482
|
+
|
|
483
|
+
(ok true)
|
|
484
|
+
)
|
|
485
|
+
)
|
|
486
|
+
```
|
|
219
487
|
|
|
220
|
-
|
|
221
|
-
- **Markup**: 8% (configurable by relayer)
|
|
222
|
-
- **Example**: 0.005 STX gas = ~$0.0025 = 0.0025 USDCx + 8% = 0.0027 USDCx
|
|
488
|
+
## Configuration
|
|
223
489
|
|
|
224
|
-
|
|
490
|
+
### Supported Fee Tokens
|
|
225
491
|
|
|
226
|
-
|
|
227
|
-
- Relayer: `https://relayer.velumx.com/api/v1`
|
|
228
|
-
- Stacks API: `https://api.testnet.hiro.so`
|
|
229
|
-
- Free for development (no API key required)
|
|
492
|
+
VelumX accepts ANY SIP-010 token for gas fees. The paymaster contract uses the `<sip-010-trait>` parameter for universal compatibility.
|
|
230
493
|
|
|
231
|
-
|
|
232
|
-
- Relayer: `https://mainnet-relayer.velumx.com/api/v1`
|
|
233
|
-
- Stacks API: `https://api.mainnet.hiro.so`
|
|
234
|
-
- Requires API key from dashboard
|
|
494
|
+
**Popular Tokens:**
|
|
235
495
|
|
|
236
|
-
|
|
496
|
+
| Token | Contract Address | Decimals | Use Case |
|
|
497
|
+
|-------|-----------------|----------|----------|
|
|
498
|
+
| USDCx | `ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usdcx` | 6 | Stablecoin fees |
|
|
499
|
+
| sBTC | `SM3KNVZS30WM7F89SXKVVFY4SN9RMPZZ9FX929N0V.sbtc` | 8 | Bitcoin fees |
|
|
500
|
+
| ALEX | `ALEX_TOKEN_ADDRESS` | 6 | DeFi token fees |
|
|
501
|
+
| STX | Native | 6 | Native token fees |
|
|
237
502
|
|
|
238
|
-
|
|
503
|
+
**Exchange Rate Calculation:**
|
|
239
504
|
|
|
240
505
|
```typescript
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
506
|
+
// Fetch real-time exchange rates
|
|
507
|
+
async function calculateFeeInToken(
|
|
508
|
+
feeInUSDCx: string,
|
|
509
|
+
targetToken: 'sBTC' | 'ALEX' | 'STX'
|
|
510
|
+
): Promise<string> {
|
|
511
|
+
const usdcAmount = Number(feeInUSDCx) / 1_000_000;
|
|
512
|
+
|
|
513
|
+
// Fetch rates from price oracle or API
|
|
514
|
+
const rates = await fetchExchangeRates();
|
|
515
|
+
|
|
516
|
+
switch (targetToken) {
|
|
517
|
+
case 'sBTC':
|
|
518
|
+
const btcAmount = usdcAmount / rates.BTC_USD;
|
|
519
|
+
return Math.ceil(btcAmount * 100_000_000).toString(); // Convert to satoshis
|
|
520
|
+
|
|
521
|
+
case 'ALEX':
|
|
522
|
+
const alexAmount = usdcAmount / rates.ALEX_USD;
|
|
523
|
+
return Math.ceil(alexAmount * 1_000_000).toString(); // Convert to micro-ALEX
|
|
524
|
+
|
|
525
|
+
case 'STX':
|
|
526
|
+
const stxAmount = usdcAmount / rates.STX_USD;
|
|
527
|
+
return Math.ceil(stxAmount * 1_000_000).toString(); // Convert to micro-STX
|
|
528
|
+
|
|
529
|
+
default:
|
|
530
|
+
return feeInUSDCx;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
244
533
|
|
|
245
|
-
//
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
const feeInUsdcx = gasInUsd * 1.08; // 8% markup
|
|
534
|
+
// Example usage
|
|
535
|
+
const estimate = await velumx.estimateFee({ estimatedGas: 100000 });
|
|
536
|
+
const feeInSBTC = await calculateFeeInToken(estimate.maxFeeUSDCx, 'sBTC');
|
|
249
537
|
```
|
|
250
538
|
|
|
251
|
-
###
|
|
539
|
+
### Environment Variables
|
|
252
540
|
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
|
|
541
|
+
```bash
|
|
542
|
+
# Frontend (.env.local)
|
|
543
|
+
NEXT_PUBLIC_STACKS_NETWORK=testnet
|
|
544
|
+
NEXT_PUBLIC_STACKS_API_URL=https://api.testnet.hiro.so
|
|
545
|
+
NEXT_PUBLIC_VELUMX_RELAYER_URL=https://sgal-relayer.onrender.com/api/v1
|
|
546
|
+
|
|
547
|
+
# Contracts
|
|
548
|
+
NEXT_PUBLIC_STACKS_PAYMASTER_ADDRESS=STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P.simple-paymaster-v1
|
|
549
|
+
NEXT_PUBLIC_STACKS_USDCX_ADDRESS=ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usdcx
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Network Configuration
|
|
256
553
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
);
|
|
554
|
+
```typescript
|
|
555
|
+
// Testnet (default)
|
|
556
|
+
const velumx = getVelumXClient();
|
|
261
557
|
|
|
262
|
-
|
|
263
|
-
|
|
558
|
+
// Mainnet
|
|
559
|
+
const velumx = new VelumXClient({
|
|
560
|
+
coreApiUrl: 'https://api.mainnet.hiro.so',
|
|
561
|
+
network: 'mainnet',
|
|
562
|
+
paymasterUrl: 'https://mainnet-relayer.velumx.com/api/v1'
|
|
563
|
+
});
|
|
264
564
|
```
|
|
265
565
|
|
|
266
|
-
|
|
566
|
+
## Error Handling
|
|
267
567
|
|
|
268
568
|
```typescript
|
|
269
569
|
try {
|
|
270
|
-
const
|
|
570
|
+
const estimate = await velumx.estimateFee({
|
|
571
|
+
estimatedGas: 100000
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
const result = await openContractCall({
|
|
575
|
+
// ... transaction params
|
|
576
|
+
onFinish: async (data) => {
|
|
577
|
+
try {
|
|
578
|
+
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
579
|
+
console.log(`Success: ${tx.txid}`);
|
|
580
|
+
} catch (error) {
|
|
581
|
+
if (error.message.includes('insufficient balance')) {
|
|
582
|
+
console.error('User needs more USDCx for fees');
|
|
583
|
+
} else if (error.message.includes('invalid signature')) {
|
|
584
|
+
console.error('Signature verification failed');
|
|
585
|
+
} else {
|
|
586
|
+
console.error('Transaction failed:', error);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
onCancel: () => {
|
|
591
|
+
console.log('User cancelled transaction');
|
|
592
|
+
}
|
|
593
|
+
});
|
|
271
594
|
} catch (error) {
|
|
272
|
-
|
|
273
|
-
console.error('User needs more USDCx for fees');
|
|
274
|
-
} else if (error.message.includes('invalid signature')) {
|
|
275
|
-
console.error('Signature verification failed');
|
|
276
|
-
} else {
|
|
277
|
-
console.error('Transaction failed:', error);
|
|
278
|
-
}
|
|
595
|
+
console.error('Failed to estimate fee:', error);
|
|
279
596
|
}
|
|
280
597
|
```
|
|
281
598
|
|
|
282
|
-
##
|
|
599
|
+
## Testing
|
|
600
|
+
|
|
601
|
+
### Unit Tests
|
|
602
|
+
|
|
603
|
+
```bash
|
|
604
|
+
npm test
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### Integration Tests
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
npm run test:integration
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
### Example Test
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
import { getVelumXClient } from '@velumx/sdk';
|
|
617
|
+
|
|
618
|
+
describe('VelumX SDK', () => {
|
|
619
|
+
it('should estimate fee correctly', async () => {
|
|
620
|
+
const velumx = getVelumXClient();
|
|
621
|
+
|
|
622
|
+
const estimate = await velumx.estimateFee({
|
|
623
|
+
estimatedGas: 100000
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
expect(estimate.maxFeeUSDCx).toBeDefined();
|
|
627
|
+
expect(Number(estimate.maxFeeUSDCx)).toBeGreaterThan(0);
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
## Deployed Contracts
|
|
633
|
+
|
|
634
|
+
### Testnet
|
|
635
|
+
|
|
636
|
+
**Simple Paymaster**
|
|
637
|
+
```
|
|
638
|
+
Address: STKYNF473GQ1V0WWCF24TV7ZR1WYAKTC79V25E3P.simple-paymaster-v1
|
|
639
|
+
Network: Stacks Testnet
|
|
640
|
+
Explorer: https://explorer.hiro.so/txid/0x90c134205b04599405e3cccae6c86ed496ae2d81ef0392970e2c9a7acd3b2138?chain=testnet
|
|
641
|
+
```
|
|
283
642
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
-
|
|
643
|
+
**USDCx Token**
|
|
644
|
+
```
|
|
645
|
+
Address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usdcx
|
|
646
|
+
Standard: SIP-010
|
|
647
|
+
Decimals: 6
|
|
648
|
+
```
|
|
288
649
|
|
|
289
|
-
|
|
650
|
+
**Relayer**
|
|
651
|
+
```
|
|
652
|
+
URL: https://sgal-relayer.onrender.com/api/v1
|
|
653
|
+
Status: https://sgal-relayer.onrender.com/api/v1/health
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
## FAQ
|
|
657
|
+
|
|
658
|
+
### Q: Do users need STX?
|
|
659
|
+
**A:** No! Users only need any SIP-010 token (USDCx, sBTC, ALEX, etc.). The relayer pays STX fees.
|
|
660
|
+
|
|
661
|
+
### Q: What tokens can I use for fees?
|
|
662
|
+
**A:** Any SIP-010 compliant token! Popular options include USDCx, sBTC, ALEX, and STX. The paymaster contract uses the `<sip-010-trait>` parameter for universal compatibility.
|
|
663
|
+
|
|
664
|
+
### Q: How much does it cost?
|
|
665
|
+
**A:** Fees are calculated in real-time based on STX/USD rates with an 8% markup. Typically 0.001-0.01 USDCx (or equivalent in other tokens) per transaction.
|
|
666
|
+
|
|
667
|
+
### Q: Is it secure?
|
|
668
|
+
**A:** Yes! Uses Stacks' native sponsored transaction feature. No smart wallet complexity.
|
|
669
|
+
|
|
670
|
+
### Q: What wallets are supported?
|
|
671
|
+
**A:** Any Stacks wallet (Xverse, Leather, Hiro) that supports sponsored transactions.
|
|
672
|
+
|
|
673
|
+
### Q: Can I use this in production?
|
|
674
|
+
**A:** Currently on testnet. Mainnet launch pending security audit.
|
|
675
|
+
|
|
676
|
+
### Q: How do I get an API key?
|
|
677
|
+
**A:** Visit [https://velum-x-ssum.vercel.app](https://velum-x-ssum.vercel.app) to sign up and generate API keys.
|
|
678
|
+
|
|
679
|
+
## Examples
|
|
680
|
+
|
|
681
|
+
Complete examples available in the repository:
|
|
682
|
+
|
|
683
|
+
- [Bridge Example](../examples/bridge)
|
|
684
|
+
- [Swap Example](../examples/swap)
|
|
685
|
+
- [Custom Integration](../examples/custom)
|
|
686
|
+
|
|
687
|
+
## Support
|
|
290
688
|
|
|
291
689
|
- **Documentation**: [docs.velumx.com](https://docs.velumx.com)
|
|
292
|
-
- **Dashboard**: [
|
|
690
|
+
- **Dashboard**: [https://velum-x-ssum.vercel.app](https://velum-x-ssum.vercel.app)
|
|
293
691
|
- **Discord**: [discord.gg/velumx](https://discord.gg/velumx)
|
|
294
692
|
- **Email**: support@velumx.com
|
|
693
|
+
- **GitHub Issues**: [github.com/velumx/sdk/issues](https://github.com/velumx/sdk/issues)
|
|
694
|
+
|
|
695
|
+
## Contributing
|
|
696
|
+
|
|
697
|
+
Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
698
|
+
|
|
699
|
+
## License
|
|
295
700
|
|
|
296
|
-
|
|
701
|
+
MIT License - see [LICENSE](./LICENSE) for details.
|
|
297
702
|
|
|
298
|
-
|
|
703
|
+
## Changelog
|
|
299
704
|
|
|
300
|
-
|
|
705
|
+
### v2.0.0 (Current)
|
|
706
|
+
- ✅ Simplified architecture (removed smart wallets)
|
|
707
|
+
- ✅ Native Stacks sponsored transactions
|
|
708
|
+
- ✅ Simple paymaster contract
|
|
709
|
+
- ✅ Improved performance and reliability
|
|
301
710
|
|
|
302
|
-
|
|
711
|
+
### v1.0.0
|
|
712
|
+
- Initial release with smart wallet pattern
|
|
303
713
|
|
|
304
714
|
---
|
|
305
715
|
|
package/dist/VelumXClient.js
CHANGED
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.VelumXClient = void 0;
|
|
4
4
|
class VelumXClient {
|
|
5
5
|
constructor(config) {
|
|
6
|
+
if (!config.apiKey) {
|
|
7
|
+
throw new Error("VelumX Client Error: API Key is required. Please obtain your key from the VelumX Developer Dashboard.");
|
|
8
|
+
}
|
|
6
9
|
this.config = config;
|
|
7
10
|
// Default to a hosted relayer if not provided
|
|
8
11
|
this.relayerUrl = config.paymasterUrl || 'https://relayer.velumx.com/api/v1';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velumx/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "VelumX Gas Abstraction Layer SDK for dApps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
"@types/node": "^25.3.2",
|
|
33
33
|
"typescript": "^5.9.3"
|
|
34
34
|
}
|
|
35
|
-
}
|
|
35
|
+
}
|
package/src/VelumXClient.ts
CHANGED
|
@@ -5,6 +5,9 @@ export class VelumXClient {
|
|
|
5
5
|
private relayerUrl: string;
|
|
6
6
|
|
|
7
7
|
constructor(config: NetworkConfig) {
|
|
8
|
+
if (!config.apiKey) {
|
|
9
|
+
throw new Error("VelumX Client Error: API Key is required. Please obtain your key from the VelumX Developer Dashboard.");
|
|
10
|
+
}
|
|
8
11
|
this.config = config;
|
|
9
12
|
// Default to a hosted relayer if not provided
|
|
10
13
|
this.relayerUrl = config.paymasterUrl || 'https://relayer.velumx.com/api/v1';
|