@velumx/sdk 1.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +306 -0
- package/dist/IntentBuilder.d.ts +1 -1
- package/dist/IntentBuilder.js +1 -1
- package/dist/SimplePaymaster.d.ts +48 -0
- package/dist/SimplePaymaster.js +89 -0
- package/dist/VelumXClient.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/package.json +2 -1
- package/src/IntentBuilder.ts +1 -1
- package/src/SimplePaymaster.ts +130 -0
- package/src/VelumXClient.ts +6 -6
- package/src/index.ts +2 -1
- package/tsconfig.json +2 -1
package/README.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# VelumX SDK
|
|
2
|
+
|
|
3
|
+
The first Paymaster infrastructure on Stacks blockchain. Enable gasless transactions in your dApp - users pay gas fees in USDCx instead of STX.
|
|
4
|
+
|
|
5
|
+
## 🚀 Features
|
|
6
|
+
|
|
7
|
+
- **Gasless Transactions**: Users pay fees in USDCx, not STX
|
|
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
|
|
12
|
+
|
|
13
|
+
## 📦 Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @velumx/sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 🔧 Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. Get API Key
|
|
22
|
+
|
|
23
|
+
Register your dApp at [dashboard.velumx.com](https://dashboard.velumx.com) to get your API key.
|
|
24
|
+
|
|
25
|
+
### 2. Initialize Client
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { VelumXClient } from '@velumx/sdk';
|
|
29
|
+
|
|
30
|
+
const velumx = new VelumXClient({
|
|
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
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. Estimate Fee
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
const estimate = await velumx.estimateFee({
|
|
42
|
+
estimatedGas: 100000
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
console.log(`Fee: ${estimate.maxFeeUSDCx} micro-USDCx`);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 4. Submit Gasless Transaction
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { tupleCV, uintCV, bufferCV, serializeCV } from '@stacks/transactions';
|
|
52
|
+
|
|
53
|
+
// Create your transaction payload
|
|
54
|
+
const payload = tupleCV({
|
|
55
|
+
amount: uintCV(1000000), // 1 USDCx
|
|
56
|
+
recipient: bufferCV(recipientBytes)
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Create intent
|
|
60
|
+
const intent = {
|
|
61
|
+
target: 'ST...CONTRACT.paymaster-module-v10',
|
|
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
|
+
|
|
76
|
+
console.log(`Transaction ID: ${result.txid}`);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 📚 API Reference
|
|
80
|
+
|
|
81
|
+
### VelumXClient
|
|
82
|
+
|
|
83
|
+
#### Constructor
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
new VelumXClient(config: NetworkConfig)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**NetworkConfig:**
|
|
90
|
+
- `coreApiUrl`: Stacks API URL (mainnet/testnet)
|
|
91
|
+
- `network`: 'mainnet' | 'testnet' | 'devnet'
|
|
92
|
+
- `paymasterUrl`: VelumX relayer URL
|
|
93
|
+
- `apiKey`: Your dApp API key (optional for testnet)
|
|
94
|
+
|
|
95
|
+
#### Methods
|
|
96
|
+
|
|
97
|
+
##### estimateFee()
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
estimateFee(intent: { estimatedGas: number }): Promise<{
|
|
101
|
+
maxFeeUSDCx: string;
|
|
102
|
+
estimatedGas: number;
|
|
103
|
+
}>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Get fee estimate in USDCx for a transaction.
|
|
107
|
+
|
|
108
|
+
##### submitIntent()
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
submitIntent(signedIntent: SignedIntent): Promise<{
|
|
112
|
+
txid: string;
|
|
113
|
+
status: string;
|
|
114
|
+
}>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Submit a signed intent for gasless execution.
|
|
118
|
+
|
|
119
|
+
**SignedIntent:**
|
|
120
|
+
- `target`: Contract principal to call
|
|
121
|
+
- `payload`: Hex-encoded transaction payload
|
|
122
|
+
- `maxFeeUSDCx`: Maximum fee in micro-USDCx
|
|
123
|
+
- `nonce`: Smart Wallet nonce
|
|
124
|
+
- `signature`: SIP-018 signature
|
|
125
|
+
|
|
126
|
+
##### submitRawTransaction()
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
submitRawTransaction(txHex: string): Promise<{
|
|
130
|
+
txid: string;
|
|
131
|
+
status: string;
|
|
132
|
+
}>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Submit a raw Stacks transaction for sponsorship.
|
|
136
|
+
|
|
137
|
+
## 🎯 Use Cases
|
|
138
|
+
|
|
139
|
+
### Bridge Transactions
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// User bridges USDC from Ethereum to Stacks
|
|
143
|
+
// Pays gas fee in USDCx instead of STX
|
|
144
|
+
const payload = tupleCV({
|
|
145
|
+
amount: uintCV(5000000), // 5 USDCx
|
|
146
|
+
fee: uintCV(250000), // 0.25 USDCx fee
|
|
147
|
+
recipient: bufferCV(ethAddressBytes)
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const result = await velumx.submitIntent({
|
|
151
|
+
target: 'ST...ADMIN.paymaster-module-v10',
|
|
152
|
+
payload: serializeCV(payload),
|
|
153
|
+
maxFeeUSDCx: '250000',
|
|
154
|
+
nonce: 0,
|
|
155
|
+
signature: userSignature
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Token Swaps
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// User swaps tokens without needing STX
|
|
163
|
+
const payload = tupleCV({
|
|
164
|
+
tokenIn: principalCV('ST...TOKEN-A'),
|
|
165
|
+
tokenOut: principalCV('ST...TOKEN-B'),
|
|
166
|
+
amountIn: uintCV(1000000),
|
|
167
|
+
minOut: uintCV(950000),
|
|
168
|
+
fee: uintCV(200000)
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const result = await velumx.submitIntent({
|
|
172
|
+
target: 'ST...ADMIN.swap-contract',
|
|
173
|
+
payload: serializeCV(payload),
|
|
174
|
+
maxFeeUSDCx: '200000',
|
|
175
|
+
nonce: 1,
|
|
176
|
+
signature: userSignature
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### NFT Minting
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// User mints NFT paying gas in USDCx
|
|
184
|
+
const payload = tupleCV({
|
|
185
|
+
recipient: principalCV(userAddress),
|
|
186
|
+
tokenId: uintCV(42),
|
|
187
|
+
fee: uintCV(300000)
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const result = await velumx.submitIntent({
|
|
191
|
+
target: 'ST...ADMIN.nft-contract',
|
|
192
|
+
payload: serializeCV(payload),
|
|
193
|
+
maxFeeUSDCx: '300000',
|
|
194
|
+
nonce: 2,
|
|
195
|
+
signature: userSignature
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## 🔐 Smart Wallet Setup
|
|
200
|
+
|
|
201
|
+
Users need a Smart Wallet to use gasless transactions. The SDK handles this automatically:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { getSmartWalletManager } from '@velumx/sdk';
|
|
205
|
+
|
|
206
|
+
const manager = getSmartWalletManager();
|
|
207
|
+
|
|
208
|
+
// Check if user has Smart Wallet
|
|
209
|
+
const hasWallet = await manager.hasSmartWallet(userAddress);
|
|
210
|
+
|
|
211
|
+
if (!hasWallet) {
|
|
212
|
+
// Auto-register (one-time setup)
|
|
213
|
+
const result = await manager.ensureSmartWallet(userAddress);
|
|
214
|
+
console.log(`Smart Wallet: ${result}`);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## 💰 Fee Structure
|
|
219
|
+
|
|
220
|
+
- **Base Fee**: Actual STX gas cost converted to USDCx
|
|
221
|
+
- **Markup**: 8% (configurable by relayer)
|
|
222
|
+
- **Example**: 0.005 STX gas = ~$0.0025 = 0.0025 USDCx + 8% = 0.0027 USDCx
|
|
223
|
+
|
|
224
|
+
## 🌐 Network Support
|
|
225
|
+
|
|
226
|
+
### Testnet
|
|
227
|
+
- Relayer: `https://relayer.velumx.com/api/v1`
|
|
228
|
+
- Stacks API: `https://api.testnet.hiro.so`
|
|
229
|
+
- Free for development (no API key required)
|
|
230
|
+
|
|
231
|
+
### Mainnet
|
|
232
|
+
- Relayer: `https://mainnet-relayer.velumx.com/api/v1`
|
|
233
|
+
- Stacks API: `https://api.mainnet.hiro.so`
|
|
234
|
+
- Requires API key from dashboard
|
|
235
|
+
|
|
236
|
+
## 🛠️ Advanced Usage
|
|
237
|
+
|
|
238
|
+
### Custom Fee Calculation
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// Get exchange rates
|
|
242
|
+
const rates = await velumx.getExchangeRates();
|
|
243
|
+
console.log(`STX/USD: ${rates.stxToUsd}`);
|
|
244
|
+
|
|
245
|
+
// Calculate custom fee
|
|
246
|
+
const gasInStx = 0.005;
|
|
247
|
+
const gasInUsd = gasInStx * rates.stxToUsd;
|
|
248
|
+
const feeInUsdcx = gasInUsd * 1.08; // 8% markup
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Transaction Monitoring
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
// Submit transaction
|
|
255
|
+
const result = await velumx.submitIntent(signedIntent);
|
|
256
|
+
|
|
257
|
+
// Monitor status
|
|
258
|
+
const status = await fetch(
|
|
259
|
+
`https://api.testnet.hiro.so/extended/v1/tx/${result.txid}`
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const data = await status.json();
|
|
263
|
+
console.log(`Status: ${data.tx_status}`);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Error Handling
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
try {
|
|
270
|
+
const result = await velumx.submitIntent(signedIntent);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (error.message.includes('insufficient balance')) {
|
|
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
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## 📖 Examples
|
|
283
|
+
|
|
284
|
+
Check out complete examples:
|
|
285
|
+
- [Bridge dApp](../examples/bridge)
|
|
286
|
+
- [DEX Integration](../examples/swap)
|
|
287
|
+
- [NFT Marketplace](../examples/nft)
|
|
288
|
+
|
|
289
|
+
## 🤝 Support
|
|
290
|
+
|
|
291
|
+
- **Documentation**: [docs.velumx.com](https://docs.velumx.com)
|
|
292
|
+
- **Dashboard**: [dashboard.velumx.com](https://dashboard.velumx.com)
|
|
293
|
+
- **Discord**: [discord.gg/velumx](https://discord.gg/velumx)
|
|
294
|
+
- **Email**: support@velumx.com
|
|
295
|
+
|
|
296
|
+
## 📄 License
|
|
297
|
+
|
|
298
|
+
MIT License - see [LICENSE](./LICENSE) for details
|
|
299
|
+
|
|
300
|
+
## 🎉 Contributing
|
|
301
|
+
|
|
302
|
+
We welcome contributions! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
Built with ❤️ by the VelumX team
|
package/dist/IntentBuilder.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare class IntentBuilder {
|
|
|
10
10
|
private getDomain;
|
|
11
11
|
/**
|
|
12
12
|
* Formats the intent into a Clarity Tuple for signing
|
|
13
|
-
* Structure matches the Smart Wallet
|
|
13
|
+
* Structure matches the Smart Wallet v10 expectation
|
|
14
14
|
*/
|
|
15
15
|
private formatIntentMessage;
|
|
16
16
|
/**
|
package/dist/IntentBuilder.js
CHANGED
|
@@ -20,7 +20,7 @@ class IntentBuilder {
|
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Formats the intent into a Clarity Tuple for signing
|
|
23
|
-
* Structure matches the Smart Wallet
|
|
23
|
+
* Structure matches the Smart Wallet v10 expectation
|
|
24
24
|
*/
|
|
25
25
|
formatIntentMessage(intent) {
|
|
26
26
|
return (0, transactions_1.tupleCV)({
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Paymaster SDK
|
|
3
|
+
* Simplified gasless transactions using Stacks-native sponsored transactions
|
|
4
|
+
*/
|
|
5
|
+
export interface SimplePaymasterConfig {
|
|
6
|
+
network: 'mainnet' | 'testnet';
|
|
7
|
+
paymasterContract: string;
|
|
8
|
+
relayerAddress: string;
|
|
9
|
+
usdcxContract: string;
|
|
10
|
+
}
|
|
11
|
+
export interface BridgeParams {
|
|
12
|
+
amount: string;
|
|
13
|
+
recipient: string;
|
|
14
|
+
feeUsdcx: string;
|
|
15
|
+
onFinish?: (data: any) => void;
|
|
16
|
+
onCancel?: () => void;
|
|
17
|
+
}
|
|
18
|
+
export interface SwapParams {
|
|
19
|
+
tokenIn: string;
|
|
20
|
+
tokenOut: string;
|
|
21
|
+
amountIn: string;
|
|
22
|
+
minOut: string;
|
|
23
|
+
feeUsdcx: string;
|
|
24
|
+
onFinish?: (data: any) => void;
|
|
25
|
+
onCancel?: () => void;
|
|
26
|
+
}
|
|
27
|
+
export declare class SimplePaymaster {
|
|
28
|
+
private config;
|
|
29
|
+
constructor(config: SimplePaymasterConfig);
|
|
30
|
+
/**
|
|
31
|
+
* Execute gasless bridge withdrawal (Stacks → Ethereum)
|
|
32
|
+
*/
|
|
33
|
+
bridgeGasless(params: BridgeParams): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Execute gasless swap
|
|
36
|
+
*/
|
|
37
|
+
swapGasless(params: SwapParams): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Estimate fee for gasless transaction
|
|
40
|
+
*/
|
|
41
|
+
estimateFee(estimatedGas?: number): Promise<{
|
|
42
|
+
feeUsdcx: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Encode Ethereum address to bytes32 for Stacks contract
|
|
46
|
+
*/
|
|
47
|
+
private encodeEthereumAddress;
|
|
48
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Simple Paymaster SDK
|
|
4
|
+
* Simplified gasless transactions using Stacks-native sponsored transactions
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SimplePaymaster = void 0;
|
|
8
|
+
const connect_1 = require("@stacks/connect");
|
|
9
|
+
const transactions_1 = require("@stacks/transactions");
|
|
10
|
+
class SimplePaymaster {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Execute gasless bridge withdrawal (Stacks → Ethereum)
|
|
16
|
+
*/
|
|
17
|
+
async bridgeGasless(params) {
|
|
18
|
+
const [contractAddress, contractName] = this.config.paymasterContract.split('.');
|
|
19
|
+
// Encode Ethereum address to bytes32
|
|
20
|
+
const recipientBytes = this.encodeEthereumAddress(params.recipient);
|
|
21
|
+
const functionArgs = [
|
|
22
|
+
(0, transactions_1.uintCV)(params.amount),
|
|
23
|
+
(0, transactions_1.bufferCV)(recipientBytes),
|
|
24
|
+
(0, transactions_1.uintCV)(params.feeUsdcx),
|
|
25
|
+
(0, transactions_1.principalCV)(this.config.relayerAddress),
|
|
26
|
+
(0, transactions_1.contractPrincipalCV)(this.config.usdcxContract.split('.')[0], this.config.usdcxContract.split('.')[1])
|
|
27
|
+
];
|
|
28
|
+
await (0, connect_1.openContractCall)({
|
|
29
|
+
contractAddress,
|
|
30
|
+
contractName,
|
|
31
|
+
functionName: 'bridge-gasless',
|
|
32
|
+
functionArgs,
|
|
33
|
+
network: this.config.network,
|
|
34
|
+
sponsored: true,
|
|
35
|
+
postConditionMode: transactions_1.PostConditionMode.Allow,
|
|
36
|
+
onFinish: params.onFinish || (() => { }),
|
|
37
|
+
onCancel: params.onCancel || (() => { }),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Execute gasless swap
|
|
42
|
+
*/
|
|
43
|
+
async swapGasless(params) {
|
|
44
|
+
const [contractAddress, contractName] = this.config.paymasterContract.split('.');
|
|
45
|
+
const functionArgs = [
|
|
46
|
+
(0, transactions_1.principalCV)(params.tokenIn),
|
|
47
|
+
(0, transactions_1.principalCV)(params.tokenOut),
|
|
48
|
+
(0, transactions_1.uintCV)(params.amountIn),
|
|
49
|
+
(0, transactions_1.uintCV)(params.minOut),
|
|
50
|
+
(0, transactions_1.uintCV)(params.feeUsdcx),
|
|
51
|
+
(0, transactions_1.principalCV)(this.config.relayerAddress),
|
|
52
|
+
(0, transactions_1.contractPrincipalCV)(this.config.usdcxContract.split('.')[0], this.config.usdcxContract.split('.')[1])
|
|
53
|
+
];
|
|
54
|
+
await (0, connect_1.openContractCall)({
|
|
55
|
+
contractAddress,
|
|
56
|
+
contractName,
|
|
57
|
+
functionName: 'swap-gasless',
|
|
58
|
+
functionArgs,
|
|
59
|
+
network: this.config.network,
|
|
60
|
+
sponsored: true,
|
|
61
|
+
postConditionMode: transactions_1.PostConditionMode.Allow,
|
|
62
|
+
onFinish: params.onFinish || (() => { }),
|
|
63
|
+
onCancel: params.onCancel || (() => { }),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Estimate fee for gasless transaction
|
|
68
|
+
*/
|
|
69
|
+
async estimateFee(estimatedGas = 100000) {
|
|
70
|
+
// Simple fee calculation: ~0.25 USDCx per transaction
|
|
71
|
+
// In production, this would call the relayer API
|
|
72
|
+
return {
|
|
73
|
+
feeUsdcx: '250000' // 0.25 USDCx
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Encode Ethereum address to bytes32 for Stacks contract
|
|
78
|
+
*/
|
|
79
|
+
encodeEthereumAddress(address) {
|
|
80
|
+
const hex = address.startsWith('0x') ? address.slice(2) : address;
|
|
81
|
+
const paddedHex = hex.padStart(64, '0');
|
|
82
|
+
const bytes = new Uint8Array(32);
|
|
83
|
+
for (let i = 0; i < 32; i++) {
|
|
84
|
+
bytes[i] = parseInt(paddedHex.substring(i * 2, i * 2 + 2), 16);
|
|
85
|
+
}
|
|
86
|
+
return bytes;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.SimplePaymaster = SimplePaymaster;
|
package/dist/VelumXClient.js
CHANGED
|
@@ -22,7 +22,7 @@ class VelumXClient {
|
|
|
22
22
|
body: JSON.stringify({ intent })
|
|
23
23
|
});
|
|
24
24
|
if (!response.ok) {
|
|
25
|
-
const errData = await response.json().catch(() => ({}));
|
|
25
|
+
const errData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
26
26
|
throw new Error(`Fee estimation failed: ${errData.error || errData.message || response.statusText}`);
|
|
27
27
|
}
|
|
28
28
|
return await response.json();
|
|
@@ -47,7 +47,7 @@ class VelumXClient {
|
|
|
47
47
|
body: JSON.stringify({ intent: signedIntent })
|
|
48
48
|
});
|
|
49
49
|
if (!response.ok) {
|
|
50
|
-
const errData = await response.json().catch(() => ({}));
|
|
50
|
+
const errData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
51
51
|
throw new Error(`Intent sponsorship failed: ${errData.error || errData.message || response.statusText}`);
|
|
52
52
|
}
|
|
53
53
|
return await response.json();
|
|
@@ -72,7 +72,7 @@ class VelumXClient {
|
|
|
72
72
|
body: JSON.stringify({ txHex })
|
|
73
73
|
});
|
|
74
74
|
if (!response.ok) {
|
|
75
|
-
const errData = await response.json().catch(() => ({}));
|
|
75
|
+
const errData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
76
76
|
throw new Error(`Transaction broadcast failed: ${errData.error || errData.message || response.statusText}`);
|
|
77
77
|
}
|
|
78
78
|
return await response.json();
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -17,7 +17,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./types"), exports);
|
|
18
18
|
__exportStar(require("./IntentBuilder"), exports);
|
|
19
19
|
__exportStar(require("./VelumXClient"), exports);
|
|
20
|
+
__exportStar(require("./SimplePaymaster"), exports);
|
|
20
21
|
// Core entrypoint for the @velumx/sdk
|
|
21
22
|
// Example Usage:
|
|
22
23
|
// const client = new VelumXClient({ network: 'testnet' });
|
|
23
|
-
// const
|
|
24
|
+
// const paymaster = new SimplePaymaster({ network: 'testnet', ... });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velumx/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "VelumX Gas Abstraction Layer SDK for dApps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@stacks/common": "^7.3.1",
|
|
27
|
+
"@stacks/connect": "^7.10.0",
|
|
27
28
|
"@stacks/network": "^7.3.1",
|
|
28
29
|
"@stacks/transactions": "^7.3.1"
|
|
29
30
|
},
|
package/src/IntentBuilder.ts
CHANGED
|
@@ -33,7 +33,7 @@ export class IntentBuilder {
|
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Formats the intent into a Clarity Tuple for signing
|
|
36
|
-
* Structure matches the Smart Wallet
|
|
36
|
+
* Structure matches the Smart Wallet v10 expectation
|
|
37
37
|
*/
|
|
38
38
|
private formatIntentMessage(intent: WalletIntent) {
|
|
39
39
|
return tupleCV({
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Paymaster SDK
|
|
3
|
+
* Simplified gasless transactions using Stacks-native sponsored transactions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { openContractCall } from '@stacks/connect';
|
|
7
|
+
import {
|
|
8
|
+
uintCV,
|
|
9
|
+
bufferCV,
|
|
10
|
+
principalCV,
|
|
11
|
+
contractPrincipalCV,
|
|
12
|
+
ClarityValue,
|
|
13
|
+
PostConditionMode
|
|
14
|
+
} from '@stacks/transactions';
|
|
15
|
+
|
|
16
|
+
export interface SimplePaymasterConfig {
|
|
17
|
+
network: 'mainnet' | 'testnet';
|
|
18
|
+
paymasterContract: string; // e.g., 'DEPLOYER.simple-paymaster-v1'
|
|
19
|
+
relayerAddress: string; // e.g., 'STKY...25E3P'
|
|
20
|
+
usdcxContract: string; // e.g., 'ST1P...PGZGM.usdcx'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface BridgeParams {
|
|
24
|
+
amount: string; // Amount in micro units (e.g., "10000000" for 10 USDCx)
|
|
25
|
+
recipient: string; // Ethereum address (0x...)
|
|
26
|
+
feeUsdcx: string; // Fee in micro units (e.g., "250000" for 0.25 USDCx)
|
|
27
|
+
onFinish?: (data: any) => void;
|
|
28
|
+
onCancel?: () => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SwapParams {
|
|
32
|
+
tokenIn: string; // Contract principal
|
|
33
|
+
tokenOut: string; // Contract principal
|
|
34
|
+
amountIn: string; // Amount in micro units
|
|
35
|
+
minOut: string; // Minimum output in micro units
|
|
36
|
+
feeUsdcx: string; // Fee in micro units
|
|
37
|
+
onFinish?: (data: any) => void;
|
|
38
|
+
onCancel?: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class SimplePaymaster {
|
|
42
|
+
private config: SimplePaymasterConfig;
|
|
43
|
+
|
|
44
|
+
constructor(config: SimplePaymasterConfig) {
|
|
45
|
+
this.config = config;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Execute gasless bridge withdrawal (Stacks → Ethereum)
|
|
50
|
+
*/
|
|
51
|
+
async bridgeGasless(params: BridgeParams): Promise<void> {
|
|
52
|
+
const [contractAddress, contractName] = this.config.paymasterContract.split('.');
|
|
53
|
+
|
|
54
|
+
// Encode Ethereum address to bytes32
|
|
55
|
+
const recipientBytes = this.encodeEthereumAddress(params.recipient);
|
|
56
|
+
|
|
57
|
+
const functionArgs: ClarityValue[] = [
|
|
58
|
+
uintCV(params.amount),
|
|
59
|
+
bufferCV(recipientBytes),
|
|
60
|
+
uintCV(params.feeUsdcx),
|
|
61
|
+
principalCV(this.config.relayerAddress),
|
|
62
|
+
contractPrincipalCV(this.config.usdcxContract.split('.')[0], this.config.usdcxContract.split('.')[1])
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
await openContractCall({
|
|
66
|
+
contractAddress,
|
|
67
|
+
contractName,
|
|
68
|
+
functionName: 'bridge-gasless',
|
|
69
|
+
functionArgs,
|
|
70
|
+
network: this.config.network,
|
|
71
|
+
sponsored: true,
|
|
72
|
+
postConditionMode: PostConditionMode.Allow,
|
|
73
|
+
onFinish: params.onFinish || (() => {}),
|
|
74
|
+
onCancel: params.onCancel || (() => {}),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Execute gasless swap
|
|
80
|
+
*/
|
|
81
|
+
async swapGasless(params: SwapParams): Promise<void> {
|
|
82
|
+
const [contractAddress, contractName] = this.config.paymasterContract.split('.');
|
|
83
|
+
|
|
84
|
+
const functionArgs: ClarityValue[] = [
|
|
85
|
+
principalCV(params.tokenIn),
|
|
86
|
+
principalCV(params.tokenOut),
|
|
87
|
+
uintCV(params.amountIn),
|
|
88
|
+
uintCV(params.minOut),
|
|
89
|
+
uintCV(params.feeUsdcx),
|
|
90
|
+
principalCV(this.config.relayerAddress),
|
|
91
|
+
contractPrincipalCV(this.config.usdcxContract.split('.')[0], this.config.usdcxContract.split('.')[1])
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
await openContractCall({
|
|
95
|
+
contractAddress,
|
|
96
|
+
contractName,
|
|
97
|
+
functionName: 'swap-gasless',
|
|
98
|
+
functionArgs,
|
|
99
|
+
network: this.config.network,
|
|
100
|
+
sponsored: true,
|
|
101
|
+
postConditionMode: PostConditionMode.Allow,
|
|
102
|
+
onFinish: params.onFinish || (() => {}),
|
|
103
|
+
onCancel: params.onCancel || (() => {}),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Estimate fee for gasless transaction
|
|
109
|
+
*/
|
|
110
|
+
async estimateFee(estimatedGas: number = 100000): Promise<{ feeUsdcx: string }> {
|
|
111
|
+
// Simple fee calculation: ~0.25 USDCx per transaction
|
|
112
|
+
// In production, this would call the relayer API
|
|
113
|
+
return {
|
|
114
|
+
feeUsdcx: '250000' // 0.25 USDCx
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Encode Ethereum address to bytes32 for Stacks contract
|
|
120
|
+
*/
|
|
121
|
+
private encodeEthereumAddress(address: string): Uint8Array {
|
|
122
|
+
const hex = address.startsWith('0x') ? address.slice(2) : address;
|
|
123
|
+
const paddedHex = hex.padStart(64, '0');
|
|
124
|
+
const bytes = new Uint8Array(32);
|
|
125
|
+
for (let i = 0; i < 32; i++) {
|
|
126
|
+
bytes[i] = parseInt(paddedHex.substring(i * 2, i * 2 + 2), 16);
|
|
127
|
+
}
|
|
128
|
+
return bytes;
|
|
129
|
+
}
|
|
130
|
+
}
|
package/src/VelumXClient.ts
CHANGED
|
@@ -27,11 +27,11 @@ export class VelumXClient {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
if (!response.ok) {
|
|
30
|
-
const errData = await response.json().catch(() => ({}));
|
|
30
|
+
const errData = await response.json().catch(() => ({ error: 'Unknown error' })) as any;
|
|
31
31
|
throw new Error(`Fee estimation failed: ${errData.error || errData.message || response.statusText}`);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
return await response.json();
|
|
34
|
+
return await response.json() as { maxFeeUSDCx: string, estimatedGas: number };
|
|
35
35
|
} catch (error) {
|
|
36
36
|
console.error("VelumX Client Error (estimateFee):", error);
|
|
37
37
|
throw error;
|
|
@@ -55,11 +55,11 @@ export class VelumXClient {
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
if (!response.ok) {
|
|
58
|
-
const errData = await response.json().catch(() => ({}));
|
|
58
|
+
const errData = await response.json().catch(() => ({ error: 'Unknown error' })) as any;
|
|
59
59
|
throw new Error(`Intent sponsorship failed: ${errData.error || errData.message || response.statusText}`);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
return await response.json();
|
|
62
|
+
return await response.json() as { txid: string, status: string };
|
|
63
63
|
} catch (error) {
|
|
64
64
|
console.error("VelumX Client Error (submitIntent):", error);
|
|
65
65
|
throw error;
|
|
@@ -83,11 +83,11 @@ export class VelumXClient {
|
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
if (!response.ok) {
|
|
86
|
-
const errData = await response.json().catch(() => ({}));
|
|
86
|
+
const errData = await response.json().catch(() => ({ error: 'Unknown error' })) as any;
|
|
87
87
|
throw new Error(`Transaction broadcast failed: ${errData.error || errData.message || response.statusText}`);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
return await response.json();
|
|
90
|
+
return await response.json() as { txid: string, status: string };
|
|
91
91
|
} catch (error) {
|
|
92
92
|
console.error("VelumX Client Error (submitRawTransaction):", error);
|
|
93
93
|
throw error;
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export * from './IntentBuilder';
|
|
3
3
|
export * from './VelumXClient';
|
|
4
|
+
export * from './SimplePaymaster';
|
|
4
5
|
|
|
5
6
|
// Core entrypoint for the @velumx/sdk
|
|
6
7
|
// Example Usage:
|
|
7
8
|
// const client = new VelumXClient({ network: 'testnet' });
|
|
8
|
-
// const
|
|
9
|
+
// const paymaster = new SimplePaymaster({ network: 'testnet', ... });
|