Execution Sandbox
Simulates and policy-checks Solana transactions before they are broadcast to the network
4 min read
Execution Sandbox
ExecutionSandbox simulates a Solana transaction using an RPC endpoint, scores its risk, checks it against your policy, and returns a verdict before any funds move.
Import
import { ExecutionSandbox } from '@sentinel-sdk/core';
How It Works
base64 tx
│
▼
Simulator ──── RPC simulateTransaction ──► balance changes, program invocations
│
▼
RiskScorer ──► riskScore (0–100), riskLevel, riskFlags
│
▼
PolicyEnforcer ──► policyViolations, approved
All three steps run on every call to evaluate(). The result is conservative: if any step fails internally, the sandbox returns approved: false with riskScore: 100.
Instantiation
const sandbox = new ExecutionSandbox({
rpcEndpoint: 'https://api.mainnet-beta.solana.com',
policy: {
spendingLimits: {
maxPerTx: 10,
maxDaily: 50,
maxWeekly: 200,
},
},
});
API
evaluate(tx)
async evaluate(tx: string): Promise<SimulationResult>
Evaluate a base64-encoded serialized transaction.
const result = await sandbox.evaluate(base64Tx);
console.log(result.approved); // false
console.log(result.riskScore); // 85
console.log(result.riskLevel); // 'high'
console.log(result.policyViolations); // [{ rule: 'MAX_PER_TX', message: '...' }]
console.log(result.balanceChanges); // [{ mint: 'So111...', amount: -15, decimals: 9 }]
console.log(result.programInvocations); // ['11111111111111111111111111111111', ...]
SimulationResult Fields
| Field | Type | Description |
|---|---|---|
approved | boolean | false if risk score exceeds threshold or any policy is violated |
riskScore | number | 0–100, composite risk score |
riskLevel | RiskLevel | 'low' | 'medium' | 'high' | 'critical' |
riskFlags | RiskFlag[] | Individual factors contributing to the risk score |
policyViolations | PolicyViolation[] | Policy rules that were violated |
balanceChanges | TokenChange[] | Detected token balance changes (SOL + SPL tokens) |
programInvocations | string[] | Program IDs invoked by the transaction |
latency_ms | number | Total simulation time in milliseconds |
Policy Configuration
executionSandbox: {
rpcEndpoint: 'https://api.mainnet-beta.solana.com',
policy: {
spendingLimits: {
maxPerTx: 10, // SOL per transaction
maxDaily: 100, // SOL per day (rolling window)
maxWeekly: 500, // SOL per week (rolling window)
},
// Only allow interactions with these programs
programAllowlist: [
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', // SPL Token
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJe1bAe', // Associated Token
],
// Block transfers to these addresses
recipientBlocklist: [
'SuspiciousAddress123...',
],
// Only allow during business hours (UTC)
timeBounds: {
activeHours: { start: '09:00', end: '17:00' },
activeDays: [1, 2, 3, 4, 5], // Monday–Friday
timezone: 'UTC',
},
// Minimum time between transactions
cooldown: {
minMs: 5000, // 5 seconds
},
// Max transactions per hour
rateLimit: {
maxTxPerHour: 20,
},
// Block if risk score exceeds this (default: 70)
riskThreshold: 70,
},
}
Spending limits are in SOL
maxPerTx, maxDaily, and maxWeekly are denominated in SOL, not lamports. The sandbox converts balance changes from lamports automatically.
Risk Scoring
RiskScorer aggregates signals from the simulation into a 0–100 score:
| Risk Level | Score Range |
|---|---|
low | 0–29 |
medium | 30–59 |
high | 60–84 |
critical | 85–100 |
Risk factors include: large balance changes, unlisted program invocations, multiple programs invoked, high token velocity, and policy-adjacent behavior.
The default riskThreshold is 70. Transactions scoring above this threshold are rejected even if no explicit policy rule was violated.
Spending Tracker
The sandbox maintains a rolling SpendingTracker that accumulates SOL spent over the day and week. Each evaluate() call:
- Checks whether the proposed transaction would exceed daily/weekly limits
- Records the spend if the transaction is approved
The tracker state is in-memory and resets when the process restarts. For persistent limits across restarts, manage the SpendingTracker externally and provide it to PolicyEnforcer.
Program Allowlist
When programAllowlist is set, any transaction invoking a program not on the list is blocked:
policy: {
programAllowlist: ['TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'],
spendingLimits: { maxPerTx: 10, maxDaily: 50, maxWeekly: 200 },
}
// Transaction invokes System Program + SPL Token
// System Program is NOT on the allowlist → blocked
result.policyViolations[0].rule // 'PROGRAM_NOT_ALLOWED'
Using Components Directly
You can use the sub-components independently:
import { Simulator, RiskScorer, PolicyEnforcer, SpendingTracker } from '@sentinel-sdk/core';
const simulator = new Simulator({ rpcEndpoint: 'https://api.mainnet-beta.solana.com' });
const scorer = new RiskScorer();
const tracker = new SpendingTracker();
const enforcer = new PolicyEnforcer(policy, tracker);
const raw = await simulator.simulate(base64Tx);
const { riskScore, riskLevel, riskFlags } = scorer.score(raw);
const { approved, violations } = enforcer.check({ raw, riskScore, riskLevel, riskFlags });