Contract IR Quick Reference
At a Glance
Status: ā
Implemented and tested
VM Backend: NoopVm (placeholder for Move 2/WASM)
Breaking Changes: None
Tests Passing: 38/38
Quick Start
1. Deploy a Contract
use rivellum_types::{PlainPayload, Address};
use rivellum_intents::Intent;
use rivellum_crypto::generate_keypair;
use rivellum_execution::SimpleExecutor;
use rivellum_ledger::InMemoryState;
let keypair = generate_keypair();
let bytecode = vec![0xCA, 0xFE, 0xBA, 0xBE];
// Create deploy intent
let intent = Intent::new_plain(
PlainPayload::DeployContract { code: bytecode.clone() },
&keypair,
1,
1000,
);
// Execute deployment
let executor = SimpleExecutor::new(100);
let mut state = InMemoryState::new();
let contract_addr = executor.execute_deploy(
bytecode,
&intent.sender,
intent.nonce,
&mut state,
)?;
println!("Deployed at: {:?}", contract_addr);
2. Call a Contract
use rivellum_types::{PlainPayload, ContractAddress};
let contract = ContractAddress([0x12; 32]);
let intent = Intent::new_plain(
PlainPayload::CallContract {
contract,
entrypoint: "transfer".to_string(),
args: vec![vec![0x01], vec![0x64]],
},
&keypair,
2,
1000,
);
executor.execute_call(
intent.sender,
contract,
"transfer",
&[vec![0x01], vec![0x64]],
1000,
&state,
&mut state,
)?;
3. Custom VM
use rivellum_execution::vm::{ContractVm, VmContext};
use rivellum_types::{ContractAddress, ContractCode, contract_address_from_sender};
struct MyVm;
impl ContractVm for MyVm {
fn deploy(&self, code: Vec<u8>, deployer: &Address, nonce: u64, state: &mut dyn StateWrite)
-> anyhow::Result<ContractAddress>
{
let addr = contract_address_from_sender(deployer, nonce);
state.put_contract(addr, ContractCode::new(code));
Ok(addr)
}
fn call(&self, ctx: &mut VmContext<'_>, entrypoint: &str, args: &[Vec<u8>])
-> anyhow::Result<()>
{
// Your VM logic here
ctx.gas_meter.consume(100)?;
Ok(())
}
}
let executor = SimpleExecutor::with_vm(100, Box::new(MyVm));
Key Types
Contract Types
ContractAddress: 32-byte deterministic addressContractCode: Code hash + bytecodecontract_address_from_sender(sender, nonce): Compute address
Payload Variants
PlainPayload::DeployContract { code: Vec<u8> }
PlainPayload::CallContract { contract, entrypoint, args }
VM Interface
trait ContractVm {
fn deploy(...) -> Result<ContractAddress>;
fn call(...) -> Result<()>;
}
State Methods
trait StateView {
fn get_contract(&self, addr) -> Option<ContractCode>;
fn get_contract_storage(&self, addr, key) -> Option<Vec<u8>>;
}
trait StateWrite {
fn put_contract(&mut self, addr, code);
fn put_contract_storage(&mut self, addr, key, value);
}
Testing
# Run all tests
cargo test --workspace --lib
# Run just VM tests
cargo test --package rivellum-execution --lib vm
# Check compilation
cargo build --workspace
Files Changed
Core Implementation:
crates/rivellum-types/src/lib.rs- Contract typescrates/rivellum-ledger/src/lib.rs- State storagecrates/rivellum-execution/src/lib.rs- Executor integrationcrates/rivellum-execution/src/vm.rs- VM interface (NEW)
Documentation:
docs/CONTRACT_IR_LAYER.md- Full guidedocs/CONTRACT_IR_IMPLEMENTATION.md- Implementation summaryexamples/deploy_contract.py- Python example
What Works Now
ā
Contract deployment via intents
ā
Contract calls via intents
ā
Deterministic address computation
ā
Contract code storage
ā
Contract storage (key-value)
ā
Gas metering (stub)
ā
Execution traces with contract ops
ā
NoopVm for testing
ā
All existing transfer logic
What's Next
- Node Integration: Update
apply_intentto callexecute_deploy/execute_call - RPC APIs: Add
/contracts/:addrendpoints - Explorer: Show deployed contracts
- Move 2: Integrate move-vm-runtime
- Gas: Replace stub with real gas accounting
Common Patterns
Check if Contract Exists
if let Some(code) = state.get_contract(&addr) {
println!("Code hash: {:?}", code.code_hash);
}
Read Contract Storage
let key = b"balance";
if let Some(value) = state.get_contract_storage(&addr, key) {
let balance = u128::from_le_bytes(value.try_into().unwrap());
}
Write Contract Storage
let key = b"balance";
let value = 100u128.to_le_bytes().to_vec();
state.put_contract_storage(&addr, key, value);
Troubleshooting
Q: Can I deploy real Move contracts now?
A: Not yet. Currently using NoopVm placeholder. Move 2 integration is next step.
Q: Will existing transfer intents break?
A: No. All existing functionality preserved. Transfers work exactly as before.
Q: How do I swap in a real VM?
A: Implement ContractVm trait, then use SimpleExecutor::with_vm(fee, Box::new(YourVm)).
Q: Where are contract addresses stored?
A: Computed deterministically via BLAKE3(sender || nonce). No address registry needed.
Q: Can contracts call other contracts?
A: Not yet. Inter-contract calls will be added when integrating real VM.
References
- Full documentation:
docs/CONTRACT_IR_LAYER.md - Implementation details:
docs/CONTRACT_IR_IMPLEMENTATION.md - Python example:
examples/deploy_contract.py - VM module:
crates/rivellum-execution/src/vm.rs