Smart Contract Best Practices
Everything that makes Ethereum contracts safe or unsafe also applies to OXN. This page lists the standard practices you should follow, then the OXN-specific additions.
Standard EVM security (applies unchanged)
- Guard against reentrancy. Use
ReentrancyGuardfrom OpenZeppelin, or the checks-effects-interactions pattern. External calls (includingcall{value: x}) can recurse back into your contract. - Validate inputs at every external boundary. Don't trust the caller.
- Use
SafeMath-equivalent behavior. Solidity 0.8+ has built-in overflow checks; do not disable them without a specific reason. - Follow checks-effects-interactions. Do all state changes before external calls.
- Prefer pull over push for payments. Let recipients withdraw rather than pushing funds to them.
- Cap loop iterations. Unbounded loops can hit gas limits and brick your contract.
- Use battle-tested libraries. OpenZeppelin Contracts is the gold standard. Solmate is a leaner alternative.
- Access control gates. Use
AccessControlor explicitrequire(msg.sender == owner). Never rely ontx.origin. - Emit events for every state change. Auditors and monitoring rely on them.
OXN-specific additions
Do not assume storage is opaque
Encrypted storage hides values, not layout. A node operator dumping raw storage sees which slots are populated, how large mappings are, and when each slot was last written. Design your storage layout with that in mind. For extreme sensitivity, consider oblivious patterns (write to every slot even when only one changes).
Understand the encrypted event constraint
Event contents are only decryptable by the caller who emitted them. This changes some patterns:
- Never rely on a global indexer decrypting your app's events.
- If you need public event visibility (for wallets, explorers, indexers), emit a plain companion event without sensitive fields.
- Never emit sensitive fields as
indexed— even encrypted, index topics leak presence via Bloom filter matching.
Design against Gas-based side channels
If a function's Gas usage depends on the value of a secret (a branch based on encrypted state), the observed Gas leaks that value:
// BAD: gas usage differs by branch
function bad(uint256 secret) external {
if (secret > 100) {
// expensive path
} else {
// cheap path
}
}
Attackers observing your transaction can distinguish the two cases from Gas alone. Prefer branchless code or constant-time patterns for security-critical logic:
// BETTER: both branches execute
function ok(uint256 secret) external {
uint256 pathA = expensiveCompute();
uint256 pathB = otherExpensiveCompute();
result = secret > 100 ? pathA : pathB;
}
Use secure randomness
Never use block.timestamp, block.number, block.hash, or PREVRANDAO for random values. Use Sapphire.randomBytes — see Randomness.
Pin evmVersion: "paris"
Compiling with a newer EVM version emits opcodes OXN cannot execute. Make this a CI check, not just a config comment.
Explicit gasLimit in every script
Pin gasLimit explicitly on every transaction and make this a code-review criterion — client-side estimation is not the recommended path.
Store secrets private, not public
public storage generates a plain unauthenticated getter that defeats encryption:
// BAD: automatic getter is a plain view function
mapping(address => uint256) public balances;
// GOOD: expose through a gated view
mapping(address => uint256) private balances;
function balanceOf(address who) external view returns (uint256) {
require(msg.sender == who, "not authorized");
return balances[who];
}
Never expose private keys
If your contract holds a signing key (see Signatures), never:
- Expose it through a getter.
- Emit it in an event.
- Pass it as a return value.
- Return it from a view function.
The runtime keeps it encrypted, but a bug that hands it back to the client is worthless anyway.
Standard toolchain
- Static analysis: Slither, Mythril. These do not understand OXN-specific patterns, but they catch generic Solidity bugs.
- Formal verification: Certora, Halmos. Same caveat.
- Fuzzing: Foundry's built-in fuzzer, Echidna. Runs against local EVM without confidentiality, but flushes out logic bugs.
- Integration tests against real OXN: catches confidentiality-related issues that fuzzers cannot.
Next steps
- Confidentiality Pitfalls — the OXN-specific attack surface in detail
- Common Vulnerabilities