Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions contracts/view-facade/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,28 @@ Emitted once when `init` succeeds.

**Topic:** `("facade", "init")`

### `Register`

Emitted when a contract is added to the registry via `register`.

| Field | Type | Description |
|---|---|---|
| `address` | `Address` | On-chain address of the registered contract |
| `kind` | `ContractKind` | Role of the contract |
| `version` | `u32` | Version number at registration time |

**Topic:** `("facade", "register")`

### `Deregister`

Emitted when a contract is removed from the registry via `deregister`.

| Field | Type | Description |
|---|---|---|
| `address` | `Address` | On-chain address of the removed contract |

**Topic:** `("facade", "deregstr")`

## Error Codes

| Code | Value | Meaning |
Expand All @@ -117,10 +139,11 @@ cargo test
Expected output:

```
running 14 tests
running 17 tests
test test::test_contract_count_initially_zero ... ok
test test::test_deregister_before_init_rejected ... ok
test test::test_deregister_contract ... ok
test test::test_deregister_emits_event ... ok
test test::test_deregister_nonexistent_is_noop ... ok
test test::test_double_init_rejected ... ok
test test::test_get_admin_before_init_returns_none ... ok
Expand All @@ -132,8 +155,10 @@ test test::test_list_and_count_contracts ... ok
test test::test_register_all_contract_kinds ... ok
test test::test_register_and_lookup_contract ... ok
test test::test_register_before_init_rejected ... ok
test test::test_register_deregister_stability ... ok
test test::test_register_emits_event ... ok

test result: ok. 14 passed; 0 failed; 0 ignored
test result: ok. 17 passed; 0 failed; 0 ignored
```

## Building
Expand Down
16 changes: 14 additions & 2 deletions contracts/view-facade/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,19 @@ impl ViewFacade {
.unwrap_or(Vec::new(&env));

registry.push_back(RegisteredContract {
address,
kind,
address: address.clone(),
kind: kind.clone(),
version,
});

env.storage().instance().set(&DataKey::Registry, &registry);

// Emit register event for off-chain indexers
env.events().publish(
(symbol_short!("facade"), symbol_short!("register")),
(address, kind, version),
);

Ok(())
}

Expand Down Expand Up @@ -316,6 +322,12 @@ impl ViewFacade {

env.storage().instance().set(&DataKey::Registry, &updated);

// Emit deregister event for off-chain indexers
env.events().publish(
(symbol_short!("facade"), symbol_short!("deregstr")),
address,
);

Ok(())
}

Expand Down
103 changes: 103 additions & 0 deletions contracts/view-facade/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,106 @@ fn test_deregister_before_init_rejected() {
let result = facade.try_deregister(&addr);
assert_eq!(result, Err(Ok(FacadeError::NotInitialized)));
}

// ---------------------------------------------------------------------------
// Registry — events
// ---------------------------------------------------------------------------

/// `register` emits a `("facade", "register")` event with (address, kind, version).
#[test]
fn test_register_emits_event() {
use soroban_sdk::{symbol_short, testutils::Events as _, vec, IntoVal};

let (env, facade, admin) = setup();
let contract = Address::generate(&env);

facade.init(&admin);
facade.register(&contract, &ContractKind::BountyEscrow, &1u32);

let facade_id = facade.address.clone();
let events = env.events().all();

let found = events.iter().any(|(cid, topics, _data)| {
if cid != facade_id {
return false;
}
let expected = vec![
&env,
symbol_short!("facade").into_val(&env),
symbol_short!("register").into_val(&env),
];
topics == expected
});

assert!(found, "register event must be emitted");
}

/// `deregister` emits a `("facade", "deregstr")` event with the removed address.
#[test]
fn test_deregister_emits_event() {
use soroban_sdk::{symbol_short, testutils::Events as _, vec, IntoVal};

let (env, facade, admin) = setup();
let contract = Address::generate(&env);

facade.init(&admin);
facade.register(&contract, &ContractKind::SorobanEscrow, &2u32);
facade.deregister(&contract);

let facade_id = facade.address.clone();
let events = env.events().all();

let found = events.iter().any(|(cid, topics, _data)| {
if cid != facade_id {
return false;
}
let expected = vec![
&env,
symbol_short!("facade").into_val(&env),
symbol_short!("deregstr").into_val(&env),
];
topics == expected
});

assert!(found, "deregister event must be emitted");
}

// ---------------------------------------------------------------------------
// Registry — stability
// ---------------------------------------------------------------------------

/// Registering and deregistering multiple contracts leaves the registry in
/// a consistent state — only the expected entries remain.
#[test]
fn test_register_deregister_stability() {
let (env, facade, admin) = setup();

facade.init(&admin);

let c1 = Address::generate(&env);
let c2 = Address::generate(&env);
let c3 = Address::generate(&env);

facade.register(&c1, &ContractKind::BountyEscrow, &1);
facade.register(&c2, &ContractKind::ProgramEscrow, &2);
facade.register(&c3, &ContractKind::GrainlifyCore, &3);
assert_eq!(facade.contract_count(), 3);

// Remove the middle one
facade.deregister(&c2);
assert_eq!(facade.contract_count(), 2);

// c1 and c3 should remain in order
let list = facade.list_contracts();
assert_eq!(list.get(0).unwrap().address, c1);
assert_eq!(list.get(1).unwrap().address, c3);

// c2 should be gone
assert_eq!(facade.get_contract(&c2), None);

// Re-register c2 — should appear at end
facade.register(&c2, &ContractKind::ProgramEscrow, &4);
assert_eq!(facade.contract_count(), 3);
assert_eq!(facade.list_contracts().get(2).unwrap().address, c2);
assert_eq!(facade.list_contracts().get(2).unwrap().version, 4);
}
Loading