Essential security guidelines for Augustium smart contract development.
- Overview
- Built-in Safety Features
- Common Vulnerabilities
- Secure Coding Patterns
- Testing for Security
- Audit Checklist
- Deployment Security
Augustium is designed with security as a priority, providing built-in protections against common smart contract vulnerabilities. However, developers still need to follow best practices to ensure their contracts are secure.
// Augustium automatically checks for overflows
let result = a + b; // Throws error on overflow
// Manual overflow checking also available
let result = a.checked_add(b).require("Addition overflow")?;
// Augustium prevents reentrancy by default
contract MyContract {
#[nonreentrant]
pub fn withdraw(amount: u256) {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;
// External call is safe - reentrancy is prevented
msg.sender.call{value: amount}("");
}
}
// No null pointer dereferences
let maybe_value: Option<u256> = get_value();
match maybe_value {
Some(value) => use_value(value),
None => handle_empty(),
}
// Bounds checking on arrays
let item = array[index]; // Automatically checks bounds
// Automatic gas limit checks prevent infinite loops
for i in 0..user_input {
// Gas is tracked and execution stops if limit reached
expensive_operation();
}
❌ Vulnerable (in other languages):
// This could overflow in Solidity
uint256 result = a + b;✅ Safe in Augustium:
// Augustium automatically prevents overflow
let result = a + b; // Safe - will revert on overflow
// Or use explicit safe math
use std::math::SafeMath;
let result = SafeMath::safe_add(a, b)?;
❌ Vulnerable pattern:
// Don't do this - update state after external calls
pub fn withdraw(amount: u256) {
require(balances[msg.sender] >= amount);
// External call before state update - dangerous!
msg.sender.call{value: amount}("");
balances[msg.sender] -= amount; // Too late!
}
✅ Secure pattern:
// Augustium's built-in protection + best practices
#[nonreentrant]
pub fn withdraw(amount: u256) {
require(balances[msg.sender] >= amount);
// Update state FIRST
balances[msg.sender] -= amount;
// Then make external call
msg.sender.call{value: amount}("");
}
❌ Missing access control:
pub fn admin_function() {
// Anyone can call this!
self_destruct();
}
✅ Proper access control:
use std::access::Ownable;
contract MyContract extends Ownable {
#[only_owner]
pub fn admin_function() {
// Only owner can call this
self_destruct();
}
// Or custom modifiers
modifier only_admin() {
require(admins[msg.sender], "Not an admin");
_;
}
#[only_admin]
pub fn sensitive_function() {
// Protected function
}
}
❌ Not checking return values:
// Ignoring return value is dangerous
token.transfer(recipient, amount);
✅ Always check returns:
// Check the return value
let success = token.transfer(recipient, amount);
require(success, "Transfer failed");
// Or use ? operator for automatic error handling
token.transfer(recipient, amount)?;
pub fn withdraw(amount: u256) {
// CHECKS: Validate inputs and conditions
require(amount > 0, "Amount must be positive");
require(balances[msg.sender] >= amount, "Insufficient balance");
require(!paused, "Contract is paused");
// EFFECTS: Update contract state
balances[msg.sender] -= amount;
total_supply -= amount;
// INTERACTIONS: External calls last
msg.sender.call{value: amount}("");
emit Withdrawal(msg.sender, amount);
}
❌ Push pattern (can fail):
pub fn distribute_rewards(recipients: Vec<address>, amounts: Vec<u256>) {
for (i, recipient) in recipients.iter().enumerate() {
// If one transfer fails, all fail
recipient.call{value: amounts[i]}("");
}
}
✅ Pull pattern (safer):
contract RewardDistributor {
pending_rewards: mapping<address, u256>;
pub fn set_rewards(recipients: Vec<address>, amounts: Vec<u256>) {
// Just record the rewards
for (i, recipient) in recipients.iter().enumerate() {
pending_rewards[*recipient] += amounts[i];
}
}
pub fn claim_rewards() {
let amount = pending_rewards[msg.sender];
require(amount > 0, "No rewards pending");
pending_rewards[msg.sender] = 0;
msg.sender.call{value: amount}("");
}
}
contract RateLimited {
last_call: mapping<address, u256>;
cooldown: u256 = 3600; // 1 hour
modifier rate_limit() {
require(
block.timestamp >= last_call[msg.sender] + cooldown,
"Rate limit exceeded"
);
last_call[msg.sender] = block.timestamp;
_;
}
#[rate_limit]
pub fn sensitive_function() {
// Can only be called once per hour per address
}
}
contract CircuitBreaker {
stopped: bool = false;
owner: address;
modifier only_owner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier stop_in_emergency() {
require(!stopped, "Contract is stopped");
_;
}
#[only_owner]
pub fn emergency_stop() {
stopped = true;
emit EmergencyStop();
}
#[only_owner]
pub fn resume() {
stopped = false;
emit Resume();
}
#[stop_in_emergency]
pub fn normal_function() {
// Normal operations
}
}
// tests/security_test.aug
use std::test::TestFramework;
test "prevents overflow" {
let contract = MyContract::new();
// This should fail safely
let result = contract.add_large_numbers(U256::MAX, 1);
assert!(result.is_err(), "Should prevent overflow");
}
test "access control works" {
let contract = MyContract::new();
// Non-owner should not be able to call admin functions
set_caller(non_owner_address);
let result = contract.admin_function();
assert!(result.is_err(), "Should reject non-owner");
}
test "reentrancy protection" {
let contract = MyContract::new();
// Simulate reentrancy attack
let attack_result = simulate_reentrancy_attack(&contract);
assert!(attack_result.is_err(), "Should prevent reentrancy");
}
# Generate random test cases
august fuzz --contract MyContract --iterations 10000
# Test specific functions
august fuzz --function withdraw --min-amount 0 --max-amount 1000000test "invariant: total supply equals sum of balances" {
let contract = MyContract::new();
// After any sequence of operations
for _ in 0..100 {
random_operation(&contract);
let total = contract.total_supply();
let sum_balances = sum_all_balances(&contract);
assert_eq!(total, sum_balances, "Invariant violated");
}
}
- All functions have proper access controls
- External calls are made after state updates
- Integer operations use safe math or overflow checks
- Return values from external calls are checked
- Contract has emergency stop mechanisms
- Rate limiting on sensitive functions
- Input validation on all public functions
- No hardcoded addresses or values
- Comprehensive test coverage (>90%)
- Documentation for all public functions
- Can this function be called by unauthorized users?
- What happens if external calls fail?
- Are there any integer overflow possibilities?
- Can this function be called in a loop to drain resources?
- What are the gas limits for this function?
- Are there any race conditions?
- What happens in edge cases (zero values, max values)?
- Can the contract state become inconsistent?
# Run built-in security scanner
august scan --security contract.aug
# Check for common vulnerabilities
august check --vulnerability-scan
# Generate security report
august report --security --output security-report.html# Always test on testnet first
august deploy --network goerli --verify
# Run security tests on deployed contract
august security-test --contract 0x123... --network goerli# Use hardware wallet for mainnet
august deploy --network mainnet --hardware-wallet
# Multi-signature deployment
august deploy --network mainnet --multisig 0xabc...# Monitor contract for unusual activity
august monitor --contract 0x123... --alerts security-alerts.json
# Set up automated alerts
august alert-setup --contract 0x123... --slack-webhook https://...// Use proxy patterns for upgradeable contracts
contract MyContractV1 {
// Initial implementation
}
contract MyContractProxy {
implementation: address;
#[only_owner]
pub fn upgrade(new_implementation: address) {
// Validate new implementation
require(is_valid_implementation(new_implementation), "Invalid impl");
implementation = new_implementation;
emit Upgraded(new_implementation);
}
}
- Augustium Security Scanner - Built-in vulnerability detection
- Formal Verification - Mathematical proof of correctness
- Fuzzing Framework - Automated test case generation
- Gas Analysis - Detect expensive operations
Consider professional audits for:
- High-value contracts (>$1M TVL)
- Complex DeFi protocols
- Governance systems
- Cross-chain bridges
- Augustium Security Forum - Discussion of best practices
- Bug Bounty Programs - Reward security researchers
- Security Working Group - Latest threat intelligence
Remember: Security is an ongoing process, not a one-time check. Stay updated on latest threats and best practices!