Skip to content

Commit 2bbcfdc

Browse files
authored
Merge pull request #308 from Caritajoe18/main
implement role-based access control (RBAC) for contract administration
2 parents 56739e7 + 0ea8159 commit 2bbcfdc

24 files changed

Lines changed: 7060 additions & 83 deletions
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
# Gas Estimation for Soroban Operations
2+
3+
This SDK provides comprehensive gas estimation capabilities, allowing users to preview transaction costs before executing complex contract operations.
4+
5+
## Features
6+
7+
- **Accurate Simulation**: Uses Soroban RPC simulation to get precise resource measurements
8+
- **Cost Breakdown**: Detailed breakdown of base fees, resource fees, and refundable portions
9+
- **Resource Metrics**: CPU instructions, read/write bytes, and storage operations
10+
- **Safety Buffer**: Configurable fee buffer multiplier to ensure transactions succeed
11+
- **Offline Estimation**: Rough estimates for air-gapped or hardware wallet flows
12+
- **Formatted Display**: Human-readable gas reports for UI display
13+
14+
## Quick Start
15+
16+
### Basic Gas Estimation
17+
18+
```typescript
19+
import { createTokenboundSdk } from '@crowdpass/tokenbound-sdk';
20+
21+
const sdk = createTokenboundSdk({
22+
horizonUrl: 'https://horizon-testnet.stellar.org',
23+
sorobanRpcUrl: 'https://soroban-testnet.stellar.org',
24+
networkPassphrase: 'Test SDF Network ; September 2015',
25+
simulationSource: 'GABC...', // Default source for simulations
26+
});
27+
28+
// Estimate gas for a contract call
29+
const gas = await sdk.estimateGas('eventManager', {
30+
contractId: 'CDP...',
31+
method: 'create_event',
32+
args: [/* ... */]
33+
}, {
34+
source: 'GABC...', // Optional: override simulation source
35+
feeBufferMultiplier: 1.3, // 30% buffer for safety
36+
});
37+
38+
console.log(gas.summary);
39+
// "Estimated gas: 0.0001234 XLM (max: 0.0001481 XLM). Resources: 50000 instructions, 1024 bytes I/O."
40+
```
41+
42+
### Formatted Display
43+
44+
```typescript
45+
import { formatGasDisplay } from '@crowdpass/tokenbound-sdk';
46+
47+
// Print detailed gas report
48+
console.log(formatGasDisplay(gas, {
49+
currency: 'XLM',
50+
decimals: 7,
51+
showResources: true,
52+
showCosts: true,
53+
}));
54+
55+
// Output:
56+
// ═══════════════════════════════════════════
57+
// GAS ESTIMATION REPORT
58+
// ═══════════════════════════════════════════
59+
//
60+
// 💰 ESTIMATED COSTS
61+
// ───────────────────────────────────────────
62+
// Base Fee: 0.0000100 XLM
63+
// Resource Fee: 0.0001134 XLM
64+
// Refundable: 0.0000100 XLM
65+
// ─────────────────────────────────────────
66+
// TOTAL: 0.0001234 XLM
67+
// Max (buffered): 0.0001481 XLM
68+
//
69+
// ⚡ RESOURCE USAGE
70+
// ───────────────────────────────────────────
71+
// Instructions: 50,000
72+
// Read Bytes: 1,024
73+
// Write Bytes: 512
74+
// ...
75+
//
76+
// ═══════════════════════════════════════════
77+
// Estimated gas: 0.0001234 XLM (max: 0.0001481 XLM). Resources: 50000 instructions, 1024 bytes I/O.
78+
// ═══════════════════════════════════════════
79+
```
80+
81+
## API Reference
82+
83+
### `sdk.estimateGas(contract, artifact, options)`
84+
85+
Estimates gas costs by simulating a transaction against the actual contract state.
86+
87+
**Parameters:**
88+
- `contract`: Contract name ('eventManager', 'ticketNft', etc.)
89+
- `artifact`: Contract call details including contractId, method, and args
90+
- `options`: Gas estimation options
91+
- `source`: Source account for simulation
92+
- `fee`: Base fee override (in stroops)
93+
- `feeBufferMultiplier`: Safety buffer multiplier (default: 1.2)
94+
- `includeRawSimulation`: Include raw RPC response (default: false)
95+
- `correlationId`: Tracing correlation ID
96+
97+
**Returns:** `GasEstimation` object with:
98+
- `costs`: Detailed cost breakdown
99+
- `baseFee`: Base transaction fee
100+
- `resourceFee`: Fee for resource consumption
101+
- `refundableFee`: Portion refunded on success
102+
- `totalFee`: Total estimated fee
103+
- `maxFee`: Maximum fee with buffer applied
104+
- `resources`: Resource usage metrics
105+
- `instructions`: CPU instructions executed
106+
- `readBytes`: Bytes read from storage
107+
- `writeBytes`: Bytes written to storage
108+
- `entryReads`: Contract entries read
109+
- `entryWrites`: Contract entries written
110+
- `transactionSizeBytes`: Transaction XDR size
111+
- `metadataSizeBytes`: Soroban metadata size
112+
- `success`: Whether simulation succeeded
113+
- `summary`: Human-readable summary string
114+
- `rawSimulation`: Raw RPC response (if requested)
115+
116+
### `formatGasDisplay(estimation, options)`
117+
118+
Formats a gas estimation into a multi-line human-readable report.
119+
120+
```typescript
121+
import { formatGasDisplay } from '@crowdpass/tokenbound-sdk';
122+
123+
const report = formatGasDisplay(gas, {
124+
currency: 'XLM', // 'XLM' or 'stroops'
125+
decimals: 7, // Decimal places for XLM
126+
showResources: true, // Include resource breakdown
127+
showCosts: true, // Include cost breakdown
128+
});
129+
```
130+
131+
### `formatGasCost(stroops, currency, decimals)`
132+
133+
Formats a single gas cost value.
134+
135+
```typescript
136+
import { formatGasCost } from '@crowdpass/tokenbound-sdk';
137+
138+
formatGasCost(1234000, 'XLM', 7); // "0.1234000 XLM"
139+
formatGasCost(1234000, 'stroops'); // "1,234,000 stroops"
140+
```
141+
142+
### `calculateRecommendedFee(gasEstimation, priority)`
143+
144+
Calculates a recommended fee based on priority level.
145+
146+
```typescript
147+
import { calculateRecommendedFee } from '@crowdpass/tokenbound-sdk';
148+
149+
const recommendedFee = calculateRecommendedFee(gas, 'high');
150+
// Returns fee with 1.5x multiplier for high priority
151+
```
152+
153+
### `checkResourceWarnings(estimation)`
154+
155+
Checks for potentially problematic resource usage.
156+
157+
```typescript
158+
import { checkResourceWarnings } from '@crowdpass/tokenbound-sdk';
159+
160+
const { hasWarnings, warnings } = checkResourceWarnings(gas);
161+
if (hasWarnings) {
162+
console.warn('Resource concerns:', warnings);
163+
// ["High instruction count (150,000,000). May exceed network limits."]
164+
}
165+
```
166+
167+
### `compareGasEstimations(before, after)`
168+
169+
Compares two gas estimations to show the impact of changes.
170+
171+
```typescript
172+
import { compareGasEstimations } from '@crowdpass/tokenbound-sdk';
173+
174+
const comparison = compareGasEstimations(oldGas, newGas);
175+
console.log(comparison.summary);
176+
// "Gas cost increase: 0.0000500 XLM (500000 stroops)"
177+
```
178+
179+
## Offline Gas Estimation
180+
181+
For air-gapped or hardware wallet flows where network access isn't available:
182+
183+
```typescript
184+
import { OfflineTransactionBuilder } from '@crowdpass/tokenbound-sdk';
185+
186+
const builder = new OfflineTransactionBuilder('Test SDF Network ; September 2015');
187+
188+
const account = {
189+
accountId: 'GABC...',
190+
sequenceNumber: '123456789',
191+
};
192+
193+
const artifact = {
194+
contractId: 'CDP...',
195+
method: 'create_event',
196+
args: [/* ... */],
197+
};
198+
199+
// Rough estimate without network
200+
const estimate = builder.estimateGasOffline(account, artifact, 100, 1.2);
201+
202+
console.log(estimate.summary);
203+
// "Estimated gas: 0.0000150 XLM (max: 0.0000180 XLM). Tx size: 450 bytes."
204+
```
205+
206+
**Note:** Offline estimates are less accurate than online simulation as they cannot account for current contract state or resource contention.
207+
208+
## Integration with Contract Calls
209+
210+
### Estimate Before Write
211+
212+
```typescript
213+
// First, estimate the gas
214+
const gas = await sdk.estimateGas('eventManager', artifact, {
215+
source: organizerAddress,
216+
});
217+
218+
// Check if user can afford it
219+
if (gas.costs.maxFee > userBalance) {
220+
throw new Error(`Insufficient balance. Need ${formatGasCost(gas.costs.maxFee)}`);
221+
}
222+
223+
// Show confirmation with gas estimate
224+
if (confirm(`This will cost approximately ${gas.summary}. Proceed?`)) {
225+
// Execute the actual transaction
226+
const result = await sdk.write('eventManager', artifact, {
227+
source: organizerAddress,
228+
signTransaction: wallet.sign,
229+
});
230+
}
231+
```
232+
233+
### Batch Operations
234+
235+
```typescript
236+
const operations = [artifact1, artifact2, artifact3];
237+
238+
// Estimate all operations
239+
const estimates = await Promise.all(
240+
operations.map(op => sdk.estimateGas('eventManager', op, { source }))
241+
);
242+
243+
// Calculate total cost
244+
const totalMaxFee = estimates.reduce((sum, e) => sum + e.costs.maxFee, 0);
245+
246+
console.log(`Total estimated cost for ${operations.length} operations:`);
247+
console.log(` Max: ${formatGasCost(totalMaxFee)}`);
248+
```
249+
250+
## Best Practices
251+
252+
1. **Always Use Buffer**: Set `feeBufferMultiplier` to at least 1.2 (20%) to handle network congestion
253+
2. **Check Success**: Verify `gas.success` before proceeding with actual transactions
254+
3. **Handle Failures**: If simulation fails, the error is in `gas.summary`
255+
4. **Cache Estimates**: Gas estimates can be cached for similar operations within a short time window
256+
5. **Offline Fallback**: Use `estimateGasOffline` for hardware wallet flows where online simulation isn't possible
257+
258+
## Troubleshooting
259+
260+
### "Simulation failed" errors
261+
- Check that the contract ID is correct
262+
- Verify the method name and arguments match the contract spec
263+
- Ensure the source account exists on the network
264+
265+
### High gas costs
266+
- Large argument values increase transaction size
267+
- Complex operations with many storage reads/writes cost more
268+
- Consider batching multiple operations into a single transaction
269+
270+
### Fee buffer exceeded
271+
- Network congestion can cause actual fees to exceed estimates
272+
- Increase `feeBufferMultiplier` during high-traffic periods
273+
- Monitor recent ledger fees to set appropriate buffers
274+
275+
## TypeScript Types
276+
277+
```typescript
278+
interface GasEstimation {
279+
costs: {
280+
baseFee: number; // stroops
281+
resourceFee: number; // stroops
282+
refundableFee: number; // stroops
283+
totalFee: number; // stroops
284+
maxFee: number; // stroops
285+
};
286+
resources: {
287+
instructions: number;
288+
readBytes: number;
289+
writeBytes: number;
290+
entryReads: number;
291+
entryWrites: number;
292+
transactionSizeBytes: number;
293+
metadataSizeBytes: number;
294+
};
295+
success: boolean;
296+
summary: string;
297+
rawSimulation?: unknown;
298+
}
299+
300+
interface GasEstimateOptions extends InvokeOptions {
301+
includeRawSimulation?: boolean;
302+
feeBufferMultiplier?: number;
303+
}
304+
```
305+
306+
---
307+
308+
For more information on Soroban fees, see the [Stellar Documentation](https://soroban.stellar.org/docs/fundamentals/fees-and-metering).

0 commit comments

Comments
 (0)