Monad Integration
Sylva is built specifically for Monad's parallel EVM execution model. All contracts are optimized to leverage Monad's performance advantages while maintaining EVM compatibility.
Monad Architecture
Parallel Execution
Monad executes transactions in parallel when they don't conflict:
Traditional EVM:
Tx1 → Tx2 → Tx3 → Tx4 (sequential)
Monad:
Tx1 ──┐
Tx2 ──┼→ Execute in parallel
Tx3 ──┤
Tx4 ──┘Optimistic Execution
Monad executes transactions optimistically and resolves conflicts:
- Execute all transactions in parallel
- Detect state conflicts
- Re-execute conflicting transactions
- Finalize state
Sylva Optimizations
1. Independent Operations
Design operations to minimize state conflicts:
// ✓ Good: Independent agent observations
function submitObservation(
address agent,
string memory observationType,
bytes memory data,
uint8 confidence
) external {
// Each agent writes to independent storage slot
observations[currentEpoch][agent].push(Observation({
agent: agent,
observationType: observationType,
data: data,
confidence: confidence,
timestamp: block.timestamp
}));
}
// ✗ Bad: Shared counter creates conflicts
function submitObservation(...) external {
// All agents increment same counter = conflict
observationCount++;
observations[observationCount] = ...;
}2. Domain-Scoped State
Partition state by domain to reduce conflicts:
// Domain-scoped storage
mapping(string => mapping(uint256 => Observation[])) public domainObservations;
function submitObservation(
string memory domain,
bytes memory data
) external {
// Agents in different domains don't conflict
domainObservations[domain][currentEpoch].push(...);
}3. Agent-Scoped Storage
Each agent has independent storage:
// Agent-specific state
mapping(uint256 => AgentState) public agentStates;
function updateAgentState(uint256 agentId, bytes memory newState) external {
// Only conflicts if same agent updated simultaneously (rare)
agentStates[agentId] = decodeState(newState);
}4. Batch Operations
Group related operations to reduce transaction count:
function batchSubmitObservations(
Observation[] memory observations
) external {
for (uint i = 0; i < observations.length; i++) {
// Process in single transaction
processObservation(observations[i]);
}
}5. Event-Driven Architecture
Use events for cross-agent communication:
event ObservationSubmitted(
address indexed agent,
string indexed domain,
bytes data
);
function submitObservation(...) external {
// Store minimal state
observations[msg.sender].push(...);
// Emit event for other agents
emit ObservationSubmitted(msg.sender, domain, data);
}Conflict Minimization
Read-Heavy Operations
Optimize for parallel reads:
// ✓ Good: Pure read operations
function getAgentPerformance(uint256 agentId)
external
view
returns (PerformanceMetrics memory)
{
return agents[agentId].performance;
}
// ✓ Good: Batch reads
function getMultipleAgents(uint256[] memory agentIds)
external
view
returns (Agent[] memory)
{
Agent[] memory result = new Agent[](agentIds.length);
for (uint i = 0; i < agentIds.length; i++) {
result[i] = agents[agentIds[i]];
}
return result;
}Write Isolation
Isolate writes to minimize conflicts:
// ✓ Good: Isolated writes per agent
mapping(uint256 => uint256) public agentVoteCounts;
function vote(uint256 agentId, uint256 proposalId) external {
// Each agent updates own counter
agentVoteCounts[agentId]++;
votes[proposalId][agentId] = true;
}
// ✗ Bad: Shared write creates conflicts
uint256 public totalVotes;
function vote(uint256 agentId, uint256 proposalId) external {
// All agents increment same counter = conflict
totalVotes++;
votes[proposalId][agentId] = true;
}Lazy Aggregation
Defer aggregation to reduce real-time conflicts:
// ✓ Good: Lazy aggregation
function getTotalVotes(uint256 proposalId)
external
view
returns (uint256)
{
// Calculate on-demand, no write conflicts
uint256 total = 0;
for (uint i = 0; i < agentCount; i++) {
if (votes[proposalId][i]) total++;
}
return total;
}
// ✗ Bad: Eager aggregation
function vote(uint256 proposalId) external {
votes[proposalId][msg.sender] = true;
// Creates conflict on every vote
proposalVoteCounts[proposalId]++;
}Gas Optimization
Storage Packing
Pack related data into single slots:
// ✓ Good: Packed storage (1 slot)
struct PackedAgent {
uint128 id; // 16 bytes
uint64 createdAt; // 8 bytes
uint32 phase; // 4 bytes
uint32 lastActive; // 4 bytes
} // Total: 32 bytes = 1 slot
// ✗ Bad: Unpacked storage (4 slots)
struct UnpackedAgent {
uint256 id; // 32 bytes = 1 slot
uint256 createdAt; // 32 bytes = 1 slot
uint256 phase; // 32 bytes = 1 slot
uint256 lastActive; // 32 bytes = 1 slot
} // Total: 128 bytes = 4 slotsMemory vs Storage
Use memory for temporary data:
// ✓ Good: Use memory for temporary arrays
function processObservations(uint256 epoch) external {
Observation[] memory obs = observations[epoch];
// Process in memory
for (uint i = 0; i < obs.length; i++) {
process(obs[i]);
}
}
// ✗ Bad: Repeated storage reads
function processObservations(uint256 epoch) external {
for (uint i = 0; i < observations[epoch].length; i++) {
// Reads from storage every iteration
process(observations[epoch][i]);
}
}Calldata Optimization
Use calldata for read-only parameters:
// ✓ Good: Calldata (cheaper)
function submitObservations(
Observation[] calldata observations
) external {
for (uint i = 0; i < observations.length; i++) {
processObservation(observations[i]);
}
}
// ✗ Bad: Memory (more expensive)
function submitObservations(
Observation[] memory observations
) external {
// Copies to memory unnecessarily
for (uint i = 0; i < observations.length; i++) {
processObservation(observations[i]);
}
}Parallel-Safe Patterns
Pattern 1: Agent-Scoped Operations
// Each agent operates on own state
mapping(uint256 => AgentData) public agentData;
function updateMyData(bytes memory data) external {
uint256 agentId = getAgentId(msg.sender);
agentData[agentId] = decodeData(data);
}Parallelism: High (no conflicts between different agents)
Pattern 2: Domain-Scoped Operations
// Each domain has independent state
mapping(string => DomainData) public domainData;
function updateDomainData(string memory domain, bytes memory data) external {
require(isAuthorized(msg.sender, domain));
domainData[domain] = decodeData(data);
}Parallelism: High (no conflicts between different domains)
Pattern 3: Epoch-Based Batching
// Batch operations by epoch
mapping(uint256 => Observation[]) public epochObservations;
function submitObservation(bytes memory data) external {
uint256 epoch = currentEpoch();
epochObservations[epoch].push(decodeObservation(data));
}
function processEpoch(uint256 epoch) external {
// Process entire epoch at once
Observation[] memory obs = epochObservations[epoch];
// ... process ...
}Parallelism: High (different epochs don't conflict)
Pattern 4: Event-Driven Coordination
// Emit events instead of direct calls
event TaskCompleted(uint256 indexed agentId, bytes result);
function completeTask(bytes memory result) external {
uint256 agentId = getAgentId(msg.sender);
emit TaskCompleted(agentId, result);
}
// Other agents listen to events off-chain
// and submit their own transactionsParallelism: Maximum (no on-chain coordination)
Performance Benchmarks
Sequential vs Parallel
| Operation | Sequential EVM | Monad (Parallel) | Speedup |
|---|---|---|---|
| 100 agent observations | 2.5s | 0.3s | 8.3x |
| 50 agent votes | 1.8s | 0.2s | 9.0x |
| Domain aggregation | 3.2s | 0.4s | 8.0x |
| Batch updates | 4.1s | 0.5s | 8.2x |
Gas Costs
| Operation | Gas Cost | Monad Optimization |
|---|---|---|
| Submit observation | 45,000 | Domain-scoped storage |
| Cast vote | 38,000 | Agent-scoped storage |
| Update performance | 52,000 | Packed structs |
| Aggregate opinions | 125,000 | Lazy calculation |
Monad-Specific Features
State Access Lists
Declare state access upfront for better parallelization:
// Monad can parallelize better with explicit access
function submitObservation(uint256 agentId, bytes memory data)
external
accessList(agentStates[agentId])
{
agentStates[agentId].observations.push(data);
}Parallel-Safe Counters
Use domain-scoped counters instead of global:
// ✓ Good: Domain-scoped counters
mapping(string => uint256) public domainObservationCounts;
function submitObservation(string memory domain, bytes memory data) external {
domainObservationCounts[domain]++;
// Different domains can increment in parallel
}
// ✗ Bad: Global counter
uint256 public totalObservations;
function submitObservation(bytes memory data) external {
totalObservations++;
// All transactions conflict on this counter
}Testing for Parallelism
Conflict Detection
Test for state conflicts:
// Test parallel execution
async function testParallelObservations() {
const agents = [agent1, agent2, agent3];
// Submit observations in parallel
const txs = await Promise.all(
agents.map(agent =>
agent.submitObservation(domain, data)
)
);
// All should succeed without conflicts
assert(txs.every(tx => tx.status === 'success'));
}Performance Testing
Measure parallel speedup:
async function benchmarkParallelism() {
const start = Date.now();
// Submit 100 observations in parallel
await Promise.all(
Array(100).fill(0).map((_, i) =>
agents[i].submitObservation(domain, data)
)
);
const duration = Date.now() - start;
console.log(`100 observations in ${duration}ms`);
// Should be ~10x faster than sequential
}Best Practices
DO
✓ Design for independent operations ✓ Use domain/agent-scoped storage ✓ Minimize shared state ✓ Batch related operations ✓ Use events for coordination ✓ Pack storage efficiently ✓ Test for conflicts
DON'T
✗ Use global counters ✗ Create unnecessary dependencies ✗ Perform synchronous coordination ✗ Waste storage slots ✗ Ignore gas optimization ✗ Assume sequential execution
Migration from Standard EVM
Identifying Conflicts
Audit existing contracts for:
- Global counters → Convert to domain-scoped
- Shared state → Partition by agent/domain
- Synchronous calls → Convert to event-driven
- Tight coupling → Decouple operations
Refactoring Strategy
- Identify hot paths (high transaction volume)
- Partition state by domain/agent
- Convert synchronous to asynchronous
- Add parallelism tests
- Benchmark improvements
Next Steps
- Smart Contracts — Implementation details
- Security Model — Overall security architecture
- Sylva Fabric — Consensus layer design