@velumx/sdk 2.2.0 → 2.3.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 +105 -218
- package/dist/SimplePaymaster.d.ts +24 -0
- package/dist/SimplePaymaster.js +52 -0
- package/package.json +1 -1
- package/src/SimplePaymaster.ts +76 -0
package/README.md
CHANGED
|
@@ -23,238 +23,125 @@ VelumX SDK enables gasless transactions on Stacks blockchain. Users pay transact
|
|
|
23
23
|
npm install @velumx/sdk
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
## Quick Start
|
|
26
|
+
## Quick Start (Production Pattern)
|
|
27
27
|
|
|
28
|
-
### 1. Initialize Client
|
|
28
|
+
### 1. Initialize Client via Proxy
|
|
29
|
+
For production dApps, initialize the client pointing to your backend proxy to keep your API key secure.
|
|
29
30
|
|
|
30
31
|
```typescript
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
const velumx = getVelumXClient();
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### 2. Estimate Fee
|
|
32
|
+
import { VelumXClient } from '@velumx/sdk';
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
const velumx = new VelumXClient({
|
|
35
|
+
paymasterUrl: '/api/velumx/proxy', // Your secure backend proxy
|
|
36
|
+
network: 'mainnet'
|
|
41
37
|
});
|
|
42
|
-
|
|
43
|
-
console.log(`Fee: ${estimate.maxFeeUSDCx} micro-USDCx`);
|
|
44
|
-
// Output: Fee: 540000 micro-USDCx (0.54 USDCx)
|
|
45
38
|
```
|
|
46
39
|
|
|
47
|
-
###
|
|
40
|
+
### 2. Request Sponsorship
|
|
41
|
+
Use the **`.sponsor()`** method to request gas sponsorship while reporting your custom fee for the VelumX Dashboard.
|
|
48
42
|
|
|
49
43
|
```typescript
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
}
|
|
44
|
+
// Report your dApp's specific fee and a unique user ID for tracking
|
|
45
|
+
const result = await velumx.sponsor(txRaw, {
|
|
46
|
+
feeAmount: '250000', // 0.25 USDCx (6 decimals)
|
|
47
|
+
userId: 'user_12345'
|
|
72
48
|
});
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## How It Works
|
|
76
|
-
|
|
77
|
-
### Architecture
|
|
78
|
-
|
|
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
49
|
|
|
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
|
|
50
|
+
console.log(`Transaction Sponsored: ${result.txid}`);
|
|
138
51
|
```
|
|
139
52
|
|
|
140
|
-
##
|
|
53
|
+
## Security Best Practice: The Proxy Pattern
|
|
141
54
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
#### Configuration
|
|
55
|
+
**NEVER** expose your `VELUMX_API_KEY` in the browser. Instead, create a simple backend route that injects the key and forwards the request to the VelumX Relayer.
|
|
145
56
|
|
|
57
|
+
### Example Next.js Proxy Route
|
|
146
58
|
```typescript
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
59
|
+
// app/api/velumx/proxy/[...path]/route.ts
|
|
60
|
+
export async function POST(req: Request, { params }) {
|
|
61
|
+
const { path } = params;
|
|
62
|
+
const apiKey = process.env.VELUMX_API_KEY; // Securely stored on server
|
|
63
|
+
const targetUrl = `https://relayer.velumx.com/api/v1/${path.join('/')}`;
|
|
64
|
+
|
|
65
|
+
const body = await req.json();
|
|
66
|
+
const response = await fetch(targetUrl, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: {
|
|
69
|
+
'Content-Type': 'application/json',
|
|
70
|
+
'x-api-key': apiKey
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify(body),
|
|
73
|
+
});
|
|
153
74
|
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
{
|
|
157
|
-
coreApiUrl: 'https://api.testnet.hiro.so',
|
|
158
|
-
network: 'testnet',
|
|
159
|
-
paymasterUrl: 'https://sgal-relayer.onrender.com/api/v1'
|
|
75
|
+
return Response.json(await response.json());
|
|
160
76
|
}
|
|
161
77
|
```
|
|
162
78
|
|
|
163
|
-
|
|
79
|
+
## API Reference
|
|
164
80
|
|
|
165
|
-
|
|
81
|
+
### VelumXClient
|
|
166
82
|
|
|
167
|
-
|
|
83
|
+
#### Methods
|
|
168
84
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
estimatedGas: number
|
|
172
|
-
}): Promise<FeeEstimate>
|
|
173
|
-
```
|
|
85
|
+
##### `.sponsor(txHex, options)`
|
|
86
|
+
The recommended method for Stacks-native sponsorship.
|
|
174
87
|
|
|
175
|
-
|
|
176
|
-
- `
|
|
88
|
+
- `txHex`: The raw hex string of the signed transaction.
|
|
89
|
+
- `options`:
|
|
90
|
+
- `feeAmount`: (Optional) The specific fee collected by your contract (e.g., "250000").
|
|
91
|
+
- `userId`: (Optional) A unique identifier for your user to enable multi-tenant wallet tracking.
|
|
177
92
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
}
|
|
186
|
-
```
|
|
93
|
+
##### `.estimateFee(intent)`
|
|
94
|
+
Get a real-time USDCx fee estimation for a transaction intent.
|
|
95
|
+
- `intent`: The transaction details (target, function, args).
|
|
187
96
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const estimate = await velumx.estimateFee({
|
|
191
|
-
estimatedGas: 100000
|
|
192
|
-
});
|
|
97
|
+
##### `.submitIntent(signedIntent)`
|
|
98
|
+
Submit a SIP-018 signed intent for legacy smart-wallet sponsorship.
|
|
193
99
|
|
|
194
|
-
|
|
195
|
-
// Fee: 540000 micro-USDCx (0.54 USDCx)
|
|
196
|
-
```
|
|
100
|
+
## Use Cases
|
|
197
101
|
|
|
198
|
-
|
|
102
|
+
### 1. Gasless Token Transfer (New in v2.3.0)
|
|
199
103
|
|
|
200
|
-
|
|
104
|
+
Transfer any SIP-010 token (like USDCx) between wallets without needing STX.
|
|
201
105
|
|
|
202
106
|
```typescript
|
|
203
|
-
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
**Parameters**:
|
|
207
|
-
- `txRaw`: Hex-encoded signed transaction from wallet
|
|
107
|
+
import { getVelumXClient } from '@velumx/sdk';
|
|
208
108
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
109
|
+
async function sendTokens(token: string, amount: string, recipient: string) {
|
|
110
|
+
const velumx = getVelumXClient();
|
|
111
|
+
|
|
112
|
+
// 1. Estimate fee
|
|
113
|
+
const estimate = await velumx.estimateFee({ estimatedGas: 100000 });
|
|
114
|
+
|
|
115
|
+
// 2. Perform gasless transfer
|
|
116
|
+
await velumx.transferGasless({
|
|
117
|
+
token: 'SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx',
|
|
118
|
+
amount: '10000000', // 10 USDCx
|
|
119
|
+
recipient: 'SP123...',
|
|
120
|
+
feeUsdcx: estimate.maxFeeUSDCx,
|
|
121
|
+
onFinish: (data) => console.log(`Transfer TX: ${data.txid}`)
|
|
122
|
+
});
|
|
214
123
|
}
|
|
215
124
|
```
|
|
216
125
|
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
const result = await velumx.submitRawTransaction(data.txRaw);
|
|
220
|
-
console.log(`Transaction ID: ${result.txid}`);
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
##### sponsorTransaction()
|
|
126
|
+
### 2. Universal Gasless Action (New in v2.3.0)
|
|
224
127
|
|
|
225
|
-
|
|
128
|
+
Make any contract call gasless by using the Universal Executor trait.
|
|
226
129
|
|
|
227
130
|
```typescript
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
**Example**:
|
|
241
|
-
```typescript
|
|
242
|
-
import { makeContractCall } from '@stacks/transactions';
|
|
243
|
-
|
|
244
|
-
const unsignedTx = await makeContractCall({...});
|
|
245
|
-
|
|
246
|
-
const sponsored = await velumx.sponsorTransaction({
|
|
247
|
-
transaction: unsignedTx,
|
|
248
|
-
network: 'testnet'
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// User signs and broadcasts
|
|
252
|
-
const result = await openContractCall(sponsored);
|
|
131
|
+
async function callDeFiProtocol() {
|
|
132
|
+
const velumx = getVelumXClient();
|
|
133
|
+
|
|
134
|
+
await velumx.executeGasless({
|
|
135
|
+
target: 'SP...YOUR_PROTOCOL_CONTRACT',
|
|
136
|
+
actionId: '0x...', // Hex hash of the intended action
|
|
137
|
+
param: '100', // Numeric parameter
|
|
138
|
+
feeUsdcx: '250000',
|
|
139
|
+
onFinish: (data) => console.log(`Action TX: ${data.txid}`)
|
|
140
|
+
});
|
|
141
|
+
}
|
|
253
142
|
```
|
|
254
143
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
### 1. Gasless Bridge
|
|
144
|
+
### 3. Gasless Bridge
|
|
258
145
|
|
|
259
146
|
Bridge USDC from Ethereum to Stacks without needing STX.
|
|
260
147
|
|
|
@@ -277,18 +164,18 @@ async function gaslessBridge(amount: string, recipient: string) {
|
|
|
277
164
|
|
|
278
165
|
// 3. Execute gasless bridge
|
|
279
166
|
const result = await openContractCall({
|
|
280
|
-
contractAddress: '
|
|
167
|
+
contractAddress: 'SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW',
|
|
281
168
|
contractName: 'simple-paymaster-v1',
|
|
282
169
|
functionName: 'bridge-gasless',
|
|
283
170
|
functionArgs: [
|
|
284
171
|
Cl.uint(parseUnits(amount, 6)),
|
|
285
172
|
Cl.buffer(recipientBytes),
|
|
286
173
|
Cl.uint(estimate.maxFeeUSDCx),
|
|
287
|
-
Cl.principal('
|
|
288
|
-
Cl.principal('
|
|
174
|
+
Cl.principal('SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW'),
|
|
175
|
+
Cl.principal('SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx')
|
|
289
176
|
],
|
|
290
177
|
sponsored: true,
|
|
291
|
-
network: '
|
|
178
|
+
network: 'mainnet',
|
|
292
179
|
onFinish: async (data) => {
|
|
293
180
|
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
294
181
|
console.log(`Bridge transaction: ${tx.txid}`);
|
|
@@ -328,7 +215,7 @@ async function gaslessSwap(
|
|
|
328
215
|
|
|
329
216
|
// 2. Execute gasless swap
|
|
330
217
|
const result = await openContractCall({
|
|
331
|
-
contractAddress: '
|
|
218
|
+
contractAddress: 'SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW',
|
|
332
219
|
contractName: 'simple-paymaster-v1',
|
|
333
220
|
functionName: 'swap-gasless',
|
|
334
221
|
functionArgs: [
|
|
@@ -337,11 +224,11 @@ async function gaslessSwap(
|
|
|
337
224
|
Cl.uint(parseUnits(amountIn, 6)),
|
|
338
225
|
Cl.uint(parseUnits(minOut, 6)),
|
|
339
226
|
Cl.uint(estimate.maxFeeUSDCx),
|
|
340
|
-
Cl.principal('
|
|
341
|
-
Cl.principal('
|
|
227
|
+
Cl.principal('SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW'),
|
|
228
|
+
Cl.principal('SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx')
|
|
342
229
|
],
|
|
343
230
|
sponsored: true,
|
|
344
|
-
network: '
|
|
231
|
+
network: 'mainnet',
|
|
345
232
|
onFinish: async (data) => {
|
|
346
233
|
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
347
234
|
console.log(`Swap transaction: ${tx.txid}`);
|
|
@@ -374,7 +261,7 @@ async function customGaslessTransaction() {
|
|
|
374
261
|
// More args...
|
|
375
262
|
],
|
|
376
263
|
sponsored: true, // Enable gasless
|
|
377
|
-
network: '
|
|
264
|
+
network: 'mainnet',
|
|
378
265
|
onFinish: async (data) => {
|
|
379
266
|
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
380
267
|
console.log(`Transaction: ${tx.txid}`);
|
|
@@ -401,18 +288,18 @@ async function gaslessWithSBTC(amount: string) {
|
|
|
401
288
|
|
|
402
289
|
// 3. Execute with sBTC as fee token
|
|
403
290
|
const result = await openContractCall({
|
|
404
|
-
contractAddress: '
|
|
291
|
+
contractAddress: 'SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW',
|
|
405
292
|
contractName: 'simple-paymaster-v1',
|
|
406
293
|
functionName: 'bridge-gasless',
|
|
407
294
|
functionArgs: [
|
|
408
295
|
Cl.uint(parseUnits(amount, 6)),
|
|
409
296
|
Cl.buffer(recipientBytes),
|
|
410
297
|
Cl.uint(feeInSBTC), // Fee in sBTC
|
|
411
|
-
Cl.principal('
|
|
298
|
+
Cl.principal('SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW'),
|
|
412
299
|
Cl.principal('SM3KNVZS30WM7F89SXKVVFY4SN9RMPZZ9FX929N0V.sbtc') // sBTC token
|
|
413
300
|
],
|
|
414
301
|
sponsored: true,
|
|
415
|
-
network: '
|
|
302
|
+
network: 'mainnet',
|
|
416
303
|
onFinish: async (data) => {
|
|
417
304
|
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
418
305
|
console.log(`Transaction: ${tx.txid}`);
|
|
@@ -442,18 +329,18 @@ async function gaslessWithALEX(amount: string) {
|
|
|
442
329
|
const feeInALEX = convertToALEX(estimate.maxFeeUSDCx);
|
|
443
330
|
|
|
444
331
|
const result = await openContractCall({
|
|
445
|
-
contractAddress: '
|
|
332
|
+
contractAddress: 'SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW',
|
|
446
333
|
contractName: 'simple-paymaster-v1',
|
|
447
334
|
functionName: 'bridge-gasless',
|
|
448
335
|
functionArgs: [
|
|
449
336
|
Cl.uint(parseUnits(amount, 6)),
|
|
450
337
|
Cl.buffer(recipientBytes),
|
|
451
338
|
Cl.uint(feeInALEX), // Fee in ALEX
|
|
452
|
-
Cl.principal('
|
|
339
|
+
Cl.principal('SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW'),
|
|
453
340
|
Cl.principal('ALEX_TOKEN_ADDRESS') // ALEX token
|
|
454
341
|
],
|
|
455
342
|
sponsored: true,
|
|
456
|
-
network: '
|
|
343
|
+
network: 'mainnet',
|
|
457
344
|
onFinish: async (data) => {
|
|
458
345
|
const tx = await velumx.submitRawTransaction(data.txRaw);
|
|
459
346
|
console.log(`Transaction: ${tx.txid}`);
|
|
@@ -495,7 +382,7 @@ VelumX accepts ANY SIP-010 token for gas fees. The paymaster contract uses the `
|
|
|
495
382
|
|
|
496
383
|
| Token | Contract Address | Decimals | Use Case |
|
|
497
384
|
|-------|-----------------|----------|----------|
|
|
498
|
-
| USDCx | `
|
|
385
|
+
| USDCx | `SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx` | 6 | Stablecoin fees |
|
|
499
386
|
| sBTC | `SM3KNVZS30WM7F89SXKVVFY4SN9RMPZZ9FX929N0V.sbtc` | 8 | Bitcoin fees |
|
|
500
387
|
| ALEX | `ALEX_TOKEN_ADDRESS` | 6 | DeFi token fees |
|
|
501
388
|
| STX | Native | 6 | Native token fees |
|
|
@@ -540,13 +427,13 @@ const feeInSBTC = await calculateFeeInToken(estimate.maxFeeUSDCx, 'sBTC');
|
|
|
540
427
|
|
|
541
428
|
```bash
|
|
542
429
|
# Frontend (.env.local)
|
|
543
|
-
NEXT_PUBLIC_STACKS_NETWORK=
|
|
544
|
-
NEXT_PUBLIC_STACKS_API_URL=https://api.
|
|
545
|
-
NEXT_PUBLIC_VELUMX_RELAYER_URL=https://
|
|
430
|
+
NEXT_PUBLIC_STACKS_NETWORK=mainnet
|
|
431
|
+
NEXT_PUBLIC_STACKS_API_URL=https://api.mainnet.hiro.so
|
|
432
|
+
NEXT_PUBLIC_VELUMX_RELAYER_URL=https://your-relayer-proxy.com
|
|
546
433
|
|
|
547
434
|
# Contracts
|
|
548
|
-
NEXT_PUBLIC_STACKS_PAYMASTER_ADDRESS=
|
|
549
|
-
NEXT_PUBLIC_STACKS_USDCX_ADDRESS=
|
|
435
|
+
NEXT_PUBLIC_STACKS_PAYMASTER_ADDRESS=SP...YOUR_PAYMASTER_ADDRESS
|
|
436
|
+
NEXT_PUBLIC_STACKS_USDCX_ADDRESS=SP...YOUR_USDCX_ADDRESS
|
|
550
437
|
```
|
|
551
438
|
|
|
552
439
|
### Network Configuration
|
|
@@ -635,14 +522,14 @@ describe('VelumX SDK', () => {
|
|
|
635
522
|
|
|
636
523
|
**Simple Paymaster**
|
|
637
524
|
```
|
|
638
|
-
Address:
|
|
639
|
-
Network: Stacks
|
|
640
|
-
Explorer: https://explorer.hiro.so/txid/
|
|
525
|
+
Address: SPKYNF473GQ1V0WWCF24TV7ZR1WYAKTC7AM8QGBW.simple-paymaster-v1
|
|
526
|
+
Network: Stacks Mainnet
|
|
527
|
+
Explorer: https://explorer.hiro.so/txid/[TRANSACTION_ID]?chain=mainnet
|
|
641
528
|
```
|
|
642
529
|
|
|
643
530
|
**USDCx Token**
|
|
644
531
|
```
|
|
645
|
-
Address:
|
|
532
|
+
Address: SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx
|
|
646
533
|
Standard: SIP-010
|
|
647
534
|
Decimals: 6
|
|
648
535
|
```
|
|
@@ -671,7 +558,7 @@ Status: https://sgal-relayer.onrender.com/api/v1/health
|
|
|
671
558
|
**A:** Any Stacks wallet (Xverse, Leather, Hiro) that supports sponsored transactions.
|
|
672
559
|
|
|
673
560
|
### Q: Can I use this in production?
|
|
674
|
-
**A:**
|
|
561
|
+
**A:** Yes! VelumX is fully functional on Stacks Mainnet.
|
|
675
562
|
|
|
676
563
|
### Q: How do I get an API key?
|
|
677
564
|
**A:** Visit [https://velum-x-ssum.vercel.app](https://velum-x-ssum.vercel.app) to sign up and generate API keys.
|
|
@@ -24,6 +24,22 @@ export interface SwapParams {
|
|
|
24
24
|
onFinish?: (data: any) => void;
|
|
25
25
|
onCancel?: () => void;
|
|
26
26
|
}
|
|
27
|
+
export interface TransferParams {
|
|
28
|
+
token: string;
|
|
29
|
+
amount: string;
|
|
30
|
+
recipient: string;
|
|
31
|
+
feeUsdcx: string;
|
|
32
|
+
onFinish?: (data: any) => void;
|
|
33
|
+
onCancel?: () => void;
|
|
34
|
+
}
|
|
35
|
+
export interface ExecuteParams {
|
|
36
|
+
target: string;
|
|
37
|
+
actionId: string;
|
|
38
|
+
param: string;
|
|
39
|
+
feeUsdcx: string;
|
|
40
|
+
onFinish?: (data: any) => void;
|
|
41
|
+
onCancel?: () => void;
|
|
42
|
+
}
|
|
27
43
|
export declare class SimplePaymaster {
|
|
28
44
|
private config;
|
|
29
45
|
constructor(config: SimplePaymasterConfig);
|
|
@@ -35,6 +51,14 @@ export declare class SimplePaymaster {
|
|
|
35
51
|
* Execute gasless swap
|
|
36
52
|
*/
|
|
37
53
|
swapGasless(params: SwapParams): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Execute gasless token transfer
|
|
56
|
+
*/
|
|
57
|
+
transferGasless(params: TransferParams): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Execute universal gasless action
|
|
60
|
+
*/
|
|
61
|
+
executeGasless(params: ExecuteParams): Promise<void>;
|
|
38
62
|
/**
|
|
39
63
|
* Estimate fee for gasless transaction
|
|
40
64
|
*/
|
package/dist/SimplePaymaster.js
CHANGED
|
@@ -63,6 +63,58 @@ class SimplePaymaster {
|
|
|
63
63
|
onCancel: params.onCancel || (() => { }),
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Execute gasless token transfer
|
|
68
|
+
*/
|
|
69
|
+
async transferGasless(params) {
|
|
70
|
+
const [contractAddress, contractName] = this.config.paymasterContract.split('.');
|
|
71
|
+
const [tokenAddress, tokenName] = params.token.split('.');
|
|
72
|
+
const functionArgs = [
|
|
73
|
+
(0, transactions_1.contractPrincipalCV)(tokenAddress, tokenName),
|
|
74
|
+
(0, transactions_1.uintCV)(params.amount),
|
|
75
|
+
(0, transactions_1.principalCV)(params.recipient),
|
|
76
|
+
(0, transactions_1.uintCV)(params.feeUsdcx),
|
|
77
|
+
(0, transactions_1.principalCV)(this.config.relayerAddress),
|
|
78
|
+
(0, transactions_1.contractPrincipalCV)(this.config.usdcxContract.split('.')[0], this.config.usdcxContract.split('.')[1])
|
|
79
|
+
];
|
|
80
|
+
await (0, connect_1.openContractCall)({
|
|
81
|
+
contractAddress,
|
|
82
|
+
contractName,
|
|
83
|
+
functionName: 'transfer-gasless',
|
|
84
|
+
functionArgs,
|
|
85
|
+
network: this.config.network,
|
|
86
|
+
sponsored: true,
|
|
87
|
+
postConditionMode: transactions_1.PostConditionMode.Allow,
|
|
88
|
+
onFinish: params.onFinish || (() => { }),
|
|
89
|
+
onCancel: params.onCancel || (() => { }),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Execute universal gasless action
|
|
94
|
+
*/
|
|
95
|
+
async executeGasless(params) {
|
|
96
|
+
const [contractAddress, contractName] = this.config.paymasterContract.split('.');
|
|
97
|
+
const [targetAddress, targetName] = params.target.split('.');
|
|
98
|
+
const functionArgs = [
|
|
99
|
+
(0, transactions_1.contractPrincipalCV)(targetAddress, targetName),
|
|
100
|
+
(0, transactions_1.bufferCV)(Buffer.from(params.actionId.replace(/^0x/, ''), 'hex')),
|
|
101
|
+
(0, transactions_1.uintCV)(params.param),
|
|
102
|
+
(0, transactions_1.uintCV)(params.feeUsdcx),
|
|
103
|
+
(0, transactions_1.principalCV)(this.config.relayerAddress),
|
|
104
|
+
(0, transactions_1.contractPrincipalCV)(this.config.usdcxContract.split('.')[0], this.config.usdcxContract.split('.')[1])
|
|
105
|
+
];
|
|
106
|
+
await (0, connect_1.openContractCall)({
|
|
107
|
+
contractAddress,
|
|
108
|
+
contractName,
|
|
109
|
+
functionName: 'execute-gasless',
|
|
110
|
+
functionArgs,
|
|
111
|
+
network: this.config.network,
|
|
112
|
+
sponsored: true,
|
|
113
|
+
postConditionMode: transactions_1.PostConditionMode.Allow,
|
|
114
|
+
onFinish: params.onFinish || (() => { }),
|
|
115
|
+
onCancel: params.onCancel || (() => { }),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
66
118
|
/**
|
|
67
119
|
* Estimate fee for gasless transaction
|
|
68
120
|
*/
|
package/package.json
CHANGED
package/src/SimplePaymaster.ts
CHANGED
|
@@ -38,6 +38,24 @@ export interface SwapParams {
|
|
|
38
38
|
onCancel?: () => void;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export interface TransferParams {
|
|
42
|
+
token: string; // Contract principal of token to transfer (e.g. 'SP...usdcx')
|
|
43
|
+
amount: string; // Amount in micro units
|
|
44
|
+
recipient: string; // Stacks address
|
|
45
|
+
feeUsdcx: string; // Fee in micro units
|
|
46
|
+
onFinish?: (data: any) => void;
|
|
47
|
+
onCancel?: () => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface ExecuteParams {
|
|
51
|
+
target: string; // Contract principal implementing executor-trait-v1
|
|
52
|
+
actionId: string; // 32-byte hex string (e.g., hash of action)
|
|
53
|
+
param: string; // Numeric parameter for the action
|
|
54
|
+
feeUsdcx: string; // Fee in micro units
|
|
55
|
+
onFinish?: (data: any) => void;
|
|
56
|
+
onCancel?: () => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
41
59
|
export class SimplePaymaster {
|
|
42
60
|
private config: SimplePaymasterConfig;
|
|
43
61
|
|
|
@@ -104,6 +122,64 @@ export class SimplePaymaster {
|
|
|
104
122
|
});
|
|
105
123
|
}
|
|
106
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Execute gasless token transfer
|
|
127
|
+
*/
|
|
128
|
+
async transferGasless(params: TransferParams): Promise<void> {
|
|
129
|
+
const [contractAddress, contractName] = this.config.paymasterContract.split('.');
|
|
130
|
+
const [tokenAddress, tokenName] = params.token.split('.');
|
|
131
|
+
|
|
132
|
+
const functionArgs: ClarityValue[] = [
|
|
133
|
+
contractPrincipalCV(tokenAddress, tokenName),
|
|
134
|
+
uintCV(params.amount),
|
|
135
|
+
principalCV(params.recipient),
|
|
136
|
+
uintCV(params.feeUsdcx),
|
|
137
|
+
principalCV(this.config.relayerAddress),
|
|
138
|
+
contractPrincipalCV(this.config.usdcxContract.split('.')[0], this.config.usdcxContract.split('.')[1])
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
await openContractCall({
|
|
142
|
+
contractAddress,
|
|
143
|
+
contractName,
|
|
144
|
+
functionName: 'transfer-gasless',
|
|
145
|
+
functionArgs,
|
|
146
|
+
network: this.config.network,
|
|
147
|
+
sponsored: true,
|
|
148
|
+
postConditionMode: PostConditionMode.Allow,
|
|
149
|
+
onFinish: params.onFinish || (() => {}),
|
|
150
|
+
onCancel: params.onCancel || (() => {}),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Execute universal gasless action
|
|
156
|
+
*/
|
|
157
|
+
async executeGasless(params: ExecuteParams): Promise<void> {
|
|
158
|
+
const [contractAddress, contractName] = this.config.paymasterContract.split('.');
|
|
159
|
+
const [targetAddress, targetName] = params.target.split('.');
|
|
160
|
+
|
|
161
|
+
const functionArgs: ClarityValue[] = [
|
|
162
|
+
contractPrincipalCV(targetAddress, targetName),
|
|
163
|
+
bufferCV(Buffer.from(params.actionId.replace(/^0x/, ''), 'hex')),
|
|
164
|
+
uintCV(params.param),
|
|
165
|
+
uintCV(params.feeUsdcx),
|
|
166
|
+
principalCV(this.config.relayerAddress),
|
|
167
|
+
contractPrincipalCV(this.config.usdcxContract.split('.')[0], this.config.usdcxContract.split('.')[1])
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
await openContractCall({
|
|
171
|
+
contractAddress,
|
|
172
|
+
contractName,
|
|
173
|
+
functionName: 'execute-gasless',
|
|
174
|
+
functionArgs,
|
|
175
|
+
network: this.config.network,
|
|
176
|
+
sponsored: true,
|
|
177
|
+
postConditionMode: PostConditionMode.Allow,
|
|
178
|
+
onFinish: params.onFinish || (() => {}),
|
|
179
|
+
onCancel: params.onCancel || (() => {}),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
107
183
|
/**
|
|
108
184
|
* Estimate fee for gasless transaction
|
|
109
185
|
*/
|