stellarskills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/skills/stellar-expert.md +36 -0
- package/README.md +150 -0
- package/SKILL.md +143 -0
- package/accounts/SKILL.md +275 -0
- package/anchors/SKILL.md +116 -0
- package/assets/SKILL.md +290 -0
- package/bin/cli.js +496 -0
- package/dex/SKILL.md +170 -0
- package/fees/SKILL.md +149 -0
- package/frontend/SKILL.md +169 -0
- package/horizon/SKILL.md +411 -0
- package/local-node/SKILL.md +125 -0
- package/openzeppelin/SKILL.md +76 -0
- package/operations/SKILL.md +260 -0
- package/package.json +35 -0
- package/rpc/SKILL.md +222 -0
- package/security/SKILL.md +184 -0
- package/seps/SKILL.md +384 -0
- package/soroban/SKILL.md +343 -0
- package/storage/SKILL.md +141 -0
- package/testing/SKILL.md +218 -0
- package/tools/SKILL.md +112 -0
- package/why/SKILL.md +73 -0
- package/x402/SKILL.md +99 -0
package/fees/SKILL.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stellarskills-fees
|
|
3
|
+
description: Stellar transaction fees, base fee, surge pricing, resource fees (Soroban), and fee bumps.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# STELLARSKILLS — Fees
|
|
7
|
+
|
|
8
|
+
> Stellar transaction fees, base fee, surge pricing, resource fees (Soroban), and fee bumps.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## The Fee Philosophy
|
|
13
|
+
|
|
14
|
+
Stellar is designed to be inexpensive. Fees exist primarily as a spam deterrent, not as a major source of revenue or yield for validators.
|
|
15
|
+
|
|
16
|
+
On Stellar:
|
|
17
|
+
- **Base Fee**: Applies to classic operations (payments, trustlines, etc.)
|
|
18
|
+
- **Resource Fee**: Applies to Soroban smart contracts (CPU, memory, ledger I/O)
|
|
19
|
+
- **Refunds**: You specify a maximum fee, but you are only charged the minimum necessary to be included in the ledger. The rest is refunded.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Classic Fees (Base Fee)
|
|
24
|
+
|
|
25
|
+
Every transaction specifies a `fee` (in **stroops**, where 1 XLM = 10,000,000 stroops). This is the **maximum total fee** you are willing to pay for the transaction.
|
|
26
|
+
|
|
27
|
+
### Minimum Fee Calculation
|
|
28
|
+
The minimum fee for a classic transaction is:
|
|
29
|
+
```
|
|
30
|
+
min_fee = base_fee × number_of_operations
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **Default base fee**: 100 stroops (0.00001 XLM)
|
|
34
|
+
- Transaction with 1 operation: min fee = 100 stroops
|
|
35
|
+
- Transaction with 10 operations: min fee = 1000 stroops
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import { TransactionBuilder, BASE_FEE } from "@stellar/stellar-sdk";
|
|
39
|
+
|
|
40
|
+
// BASE_FEE constant is 100
|
|
41
|
+
const tx = new TransactionBuilder(account, {
|
|
42
|
+
fee: BASE_FEE, // 100 stroops per operation
|
|
43
|
+
// ...
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Surge Pricing (Fee Market)
|
|
48
|
+
If the network is congested (more than 1000 operations per ledger), surge pricing activates. Transactions with higher base fees are prioritized.
|
|
49
|
+
|
|
50
|
+
**Crucial detail**: You only pay the *lowest* fee required to make it into the ledger, up to your specified maximum. If you set your fee to 10,000 stroops, but the clearing price is 500 stroops, you only pay 500.
|
|
51
|
+
|
|
52
|
+
### Dynamic Fee Estimation
|
|
53
|
+
Always fetch current fee stats before building a transaction in production to avoid stalled transactions:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
const server = new Horizon.Server("https://horizon.stellar.org");
|
|
57
|
+
const feeStats = await server.feeStats();
|
|
58
|
+
|
|
59
|
+
// Use the 99th percentile for high priority, or 50th for normal
|
|
60
|
+
const priorityFee = feeStats.fee_charged.p99;
|
|
61
|
+
const normalFee = feeStats.fee_charged.p50;
|
|
62
|
+
|
|
63
|
+
const tx = new TransactionBuilder(account, {
|
|
64
|
+
fee: priorityFee.toString(),
|
|
65
|
+
// ...
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Soroban Resource Fees
|
|
72
|
+
|
|
73
|
+
Soroban smart contracts charge for the exact resources they consume:
|
|
74
|
+
1. CPU instructions
|
|
75
|
+
2. Memory (RAM)
|
|
76
|
+
3. Ledger reads/writes (I/O)
|
|
77
|
+
4. Transaction size (bytes)
|
|
78
|
+
5. Events emitted
|
|
79
|
+
|
|
80
|
+
### How to calculate
|
|
81
|
+
You never calculate this manually. You must use `simulateTransaction` on the Soroban RPC.
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// 1. Build raw tx with a placeholder fee
|
|
85
|
+
const tx = new TransactionBuilder(account, { fee: "100" })
|
|
86
|
+
.addOperation(contract.call("my_func"))
|
|
87
|
+
.build();
|
|
88
|
+
|
|
89
|
+
// 2. Simulate
|
|
90
|
+
const sim = await sorobanServer.simulateTransaction(tx);
|
|
91
|
+
|
|
92
|
+
// 3. Assemble (applies footprint and calculated resource fee)
|
|
93
|
+
const preparedTx = SorobanRpc.assembleTransaction(tx, sim);
|
|
94
|
+
|
|
95
|
+
// preparedTx now has the correct fee and resources attached
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Extending Soroban Budgets
|
|
99
|
+
If simulation fails because it hits the default resource limits, you can manually increase the budget (if the network max allows it):
|
|
100
|
+
|
|
101
|
+
*(Note: Most dApps should optimize their contracts rather than increasing limits, as limits protect network throughput).*
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Fee Bump Transactions
|
|
106
|
+
|
|
107
|
+
A fee bump transaction allows Account A to pay the fee for Account B's transaction *after* Account B has already signed it.
|
|
108
|
+
|
|
109
|
+
**Use cases:**
|
|
110
|
+
- Rescuing a stuck transaction (one submitted with too low a fee during a surge).
|
|
111
|
+
- Sponsoring fees for users (dApp pays the gas so the user doesn't need XLM).
|
|
112
|
+
|
|
113
|
+
### Creating a Fee Bump
|
|
114
|
+
```javascript
|
|
115
|
+
import { FeeBumpTransaction, TransactionBuilder, Networks } from "@stellar/stellar-sdk";
|
|
116
|
+
|
|
117
|
+
// 1. You receive an inner transaction signed by the user (it might be stuck)
|
|
118
|
+
// const innerTx = ...
|
|
119
|
+
|
|
120
|
+
// 2. Wrap it in a FeeBump
|
|
121
|
+
const feeBump = TransactionBuilder.buildFeeBumpTransaction(
|
|
122
|
+
sponsorKeypair, // Account paying the higher fee
|
|
123
|
+
"5000", // New max base fee per operation (in stroops)
|
|
124
|
+
innerTx, // The original signed transaction
|
|
125
|
+
Networks.MAINNET
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// 3. Sponsor signs
|
|
129
|
+
feeBump.sign(sponsorKeypair);
|
|
130
|
+
|
|
131
|
+
// 4. Submit
|
|
132
|
+
await server.submitTransaction(feeBump);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Important**: The inner transaction's sequence number and signatures remain valid. Only the fee payer changes.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Common Errors
|
|
140
|
+
|
|
141
|
+
| Error | Meaning | Fix |
|
|
142
|
+
|-------|---------|-----|
|
|
143
|
+
| `tx_insufficient_fee` | The network is surging and your fee is too low | Fetch `feeStats` and submit with higher fee, or use Fee Bump |
|
|
144
|
+
| `op_underfunded` | Account doesn't have enough XLM to cover fee + min balance | Add more XLM. Remember minimum balance requirements! |
|
|
145
|
+
| Soroban simulation `wasm_vm_error` | Contract exceeded resource budget (often CPU) | Optimize contract logic or reduce storage reads/writes |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
*raw.githubusercontent.com/ggoldani/stellarskills/main/fees — MIT License*
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stellarskills-frontend
|
|
3
|
+
description: Connecting web apps to Stellar. Stellar Wallets Kit, Freighter API, signing Soroban transactions, and secure SEP-10 Web3 Auth.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# STELLARSKILLS — Frontend Integration
|
|
7
|
+
|
|
8
|
+
> Connecting web apps to Stellar. Stellar Wallets Kit, Freighter API, signing Soroban transactions, and secure SEP-10 Web3 Auth.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. Connecting Wallets (The Modern Standard)
|
|
13
|
+
|
|
14
|
+
To build a professional dApp with high UX, you must support multiple wallets (Freighter, Albedo, xBull, Lobstr, and WalletConnect) simultaneously. **Do not write custom logic for each extension.**
|
|
15
|
+
|
|
16
|
+
Instead, use **Stellar Wallets Kit** (`@creit.tech/stellar-wallets-kit`), the standard "RainbowKit equivalent" for Stellar.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @creit.tech/stellar-wallets-kit
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Initializing the Kit & Showing the Modal
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import {
|
|
26
|
+
StellarWalletsKit,
|
|
27
|
+
WalletNetwork,
|
|
28
|
+
allowAllModules,
|
|
29
|
+
FREIGHTER_ID
|
|
30
|
+
} from '@creit.tech/stellar-wallets-kit';
|
|
31
|
+
|
|
32
|
+
// 1. Initialize the kit globally in your React Context or state manager
|
|
33
|
+
const kit = new StellarWalletsKit({
|
|
34
|
+
network: WalletNetwork.TESTNET,
|
|
35
|
+
selectedWalletId: FREIGHTER_ID,
|
|
36
|
+
modules: allowAllModules(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// 2. Open the beautiful native UI modal to let user choose their wallet
|
|
40
|
+
await kit.openModal({
|
|
41
|
+
onWalletSelected: async (option) => {
|
|
42
|
+
kit.setWallet(option.id);
|
|
43
|
+
const publicKey = await kit.getPublicKey();
|
|
44
|
+
console.log(`Connected with ${option.name}: ${publicKey}`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. Signing Transactions (Wallets Kit)
|
|
52
|
+
|
|
53
|
+
Once connected, you can sign XDR transactions agnostically. The kit handles the extension pop-up whether the user is on Freighter or Albedo.
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
import { TransactionBuilder, Networks, BASE_FEE, Operation } from "@stellar/stellar-sdk";
|
|
57
|
+
|
|
58
|
+
// 1. Build the transaction (Requires fetching account sequence from Horizon)
|
|
59
|
+
const tx = new TransactionBuilder(account, { fee: BASE_FEE, networkPassphrase: Networks.TESTNET })
|
|
60
|
+
.addOperation(Operation.payment({ destination: "GBB...", asset: Asset.native(), amount: "10" }))
|
|
61
|
+
.setTimeout(30)
|
|
62
|
+
.build();
|
|
63
|
+
|
|
64
|
+
// 2. Sign via Wallets Kit
|
|
65
|
+
const { signedXDR } = await kit.signTx({
|
|
66
|
+
xdr: tx.toXDR(),
|
|
67
|
+
publicKeys: [userPublicKey],
|
|
68
|
+
network: WalletNetwork.TESTNET
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 3. Reconstruct and submit to Horizon/RPC
|
|
72
|
+
const signedTx = TransactionBuilder.fromXDR(signedXDR, Networks.TESTNET);
|
|
73
|
+
await horizonServer.submitTransaction(signedTx);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. Low-Level Integration: Freighter API
|
|
79
|
+
|
|
80
|
+
If you absolutely must build a low-level, Freighter-only integration without UI wrappers, use `@stellar/freighter-api`.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm install @stellar/freighter-api
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
import { isConnected, requestAccess, signTransaction } from "@stellar/freighter-api";
|
|
88
|
+
|
|
89
|
+
// Connect
|
|
90
|
+
if (await isConnected()) {
|
|
91
|
+
const address = await requestAccess();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Sign
|
|
95
|
+
const signedTxXdr = await signTransaction(tx.toXDR(), { network: "TESTNET" });
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 4. Signing Transactions (Soroban Smart Contracts)
|
|
101
|
+
|
|
102
|
+
When calling a Soroban smart contract, you cannot simply sign the operation. You must **simulate** the transaction first to fetch the correct resource footprint and fee, assemble it, and *then* request the user's signature.
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
import { TransactionBuilder, Networks, BASE_FEE, Contract } from "@stellar/stellar-sdk";
|
|
106
|
+
import { SorobanRpc } from "@stellar/stellar-sdk";
|
|
107
|
+
|
|
108
|
+
const contract = new Contract(contractId);
|
|
109
|
+
|
|
110
|
+
// 1. Build initial tx with default limits
|
|
111
|
+
let tx = new TransactionBuilder(account, { fee: BASE_FEE, networkPassphrase: Networks.TESTNET })
|
|
112
|
+
.addOperation(contract.call("deposit", ...))
|
|
113
|
+
.setTimeout(30)
|
|
114
|
+
.build();
|
|
115
|
+
|
|
116
|
+
// 2. Simulate via RPC to calculate the exact footprint and CPU fee
|
|
117
|
+
const sim = await rpcServer.simulateTransaction(tx);
|
|
118
|
+
if (SorobanRpc.Api.isSimulationError(sim)) throw sim.error;
|
|
119
|
+
|
|
120
|
+
// 3. Assemble transaction with the correct simulated footprint
|
|
121
|
+
tx = SorobanRpc.assembleTransaction(tx, sim);
|
|
122
|
+
|
|
123
|
+
// 4. Request user signature via Wallets Kit or Freighter API
|
|
124
|
+
const { signedXDR } = await kit.signTx({ xdr: tx.toXDR(), publicKeys: [userPublicKey] });
|
|
125
|
+
|
|
126
|
+
// 5. Submit to RPC and poll
|
|
127
|
+
const signedTx = TransactionBuilder.fromXDR(signedXDR, Networks.TESTNET);
|
|
128
|
+
const sendResponse = await rpcServer.sendTransaction(signedTx);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 5. SEP-10 Web3 Auth (Security Warning)
|
|
134
|
+
|
|
135
|
+
If your app has a backend and needs to securely authenticate the user via Web3, implement the **SEP-10** standard.
|
|
136
|
+
|
|
137
|
+
### ⚠️ SECURITY RULE: Never store JWTs in LocalStorage
|
|
138
|
+
Do not store the returned `auth_token` in `localStorage` or `sessionStorage`. This makes your frontend highly vulnerable to XSS (Cross-Site Scripting) attacks. The backend must set the JWT inside an `HttpOnly`, `Secure`, and `SameSite=Strict` (or `Lax`) cookie. Ensure all `fetch` calls to your protected API include `credentials: "include"`.
|
|
139
|
+
|
|
140
|
+
### Frontend Implementation
|
|
141
|
+
```javascript
|
|
142
|
+
// 1. Fetch SEP-10 challenge from your backend
|
|
143
|
+
const challengeResponse = await fetch("/api/auth/challenge?account=" + userPublicKey);
|
|
144
|
+
const { transaction, network_passphrase } = await challengeResponse.json();
|
|
145
|
+
|
|
146
|
+
// 2. Request user to sign the challenge transaction via wallet
|
|
147
|
+
const { signedXDR } = await kit.signTx({
|
|
148
|
+
xdr: transaction,
|
|
149
|
+
publicKeys: [userPublicKey],
|
|
150
|
+
network: WalletNetwork.TESTNET
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// 3. Send signed challenge back to backend
|
|
154
|
+
// The backend verifies the signature. It MUST NOT return the token in the JSON body.
|
|
155
|
+
// Instead, the backend sets the HttpOnly cookie in the response headers.
|
|
156
|
+
await fetch("/api/auth/token", {
|
|
157
|
+
method: "POST",
|
|
158
|
+
body: JSON.stringify({ transaction: signedXDR }),
|
|
159
|
+
headers: { "Content-Type": "application/json" }
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// 4. Subsequent authenticated requests
|
|
163
|
+
// The browser will automatically attach the HttpOnly cookie
|
|
164
|
+
const userProfile = await fetch("/api/me", { credentials: "include" });
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
*raw.githubusercontent.com/ggoldani/stellarskills/main/frontend — MIT License*
|