On-chain GitHub PR verification for the Stellar ecosystem. Guardians — trusted off-chain validators — cast votes on registered tasks (pull requests). Once a configurable threshold is reached the task is marked done, creating a tamper-proof audit trail on Soroban.
┌─────────────────────────────────────────────────────┐
│ VeroContract │
│ │
│ add_guardian(admin, guardian) │
│ register_task(admin, task_id) │
│ vote(guardian, task_id) ──► threshold check │
│ get_task(task_id) ──► Task { id, votes, is_done } │
└──────────────┬──────────────────────────────────────┘
│ instance storage
┌───────┴────────┐
│ DataKey │
│ Guardian(addr)│
│ Task(u64) │
│ Voted(u64,addr│
└────────────────┘
Flow
- An admin registers a GitHub PR as a
Taskwith a unique numeric ID. - The admin whitelists trusted validator addresses as guardians.
- Each guardian calls
vote. The contract rejects duplicates and non-guardians. - When
votes >= 3the task'sis_doneflag flips totrue.
| Module | Responsibility |
|---|---|
types |
Task, DataKey, ContractError |
guardian |
Guardian registry with TTL-extended instance storage |
task |
Task registration and retrieval |
lib |
Public contract surface, vote orchestration |
events |
(reserved) on-chain event emission |
rustup target add wasm32-unknown-unknown
cargo install --locked soroban-clicargo build --target wasm32-unknown-unknown --releasecargo test// admin key must sign
client.register_task(&admin, &pr_number);client.add_guardian(&admin, &validator_address);// guardian key must sign; returns Err on duplicate or non-guardian
client.vote(&guardian, &pr_number)?;let task: Task = client.get_task(&pr_number).unwrap();
assert!(task.is_done); // true once 3 votes are in#[test]
fn test_three_votes_flips_is_done() {
let env = Env::default();
env.mock_all_auths();
let id = env.register_contract(None, VeroContract);
let client = VeroContractClient::new(&env, &id);
let admin = Address::generate(&env);
let (g1, g2, g3) = (
Address::generate(&env),
Address::generate(&env),
Address::generate(&env),
);
client.add_guardian(&admin, &g1);
client.add_guardian(&admin, &g2);
client.add_guardian(&admin, &g3);
client.register_task(&admin, &42u64).unwrap();
client.vote(&g1, &42u64).unwrap();
client.vote(&g2, &42u64).unwrap();
client.vote(&g3, &42u64).unwrap();
assert!(client.get_task(&42u64).unwrap().is_done);
}All state lives in instance storage — scoped to the contract instance and extended with a 100 000-ledger TTL window on every guardian write. Keys are typed via the DataKey enum so there are no raw string collisions.
pub enum DataKey {
Guardian(Address), // bool — is this address a guardian?
Task(u64), // Task struct
Voted(u64, Address), // bool — has this guardian voted on this task?
Admin, // reserved
}| Code | Meaning |
|---|---|
NotAuthorized (1) |
Caller is not a registered guardian or admin |
DuplicateVote (2) |
Guardian already voted on this task |
Apache-2.0