Emit InitEvent on contract init - #81
Conversation
📝 WalkthroughWalkthroughThe agent vault now defines and emits an ChangesAgent vault initialization event
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant Initializer
participant AgentVault
participant SorobanEvents
Initializer->>AgentVault: init(admin, usdc_sac)
AgentVault->>SorobanEvents: publish InitEvent(admin, usdc_sac)
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment Warning |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@contracts/agent-vault/src/tests.rs`:
- Around line 51-63: Update the InitEvent assertions in the event verification
test to expect the static topic init_event and validate the complete payload
containing both test_env.admin and test_env.usdc_sac. Keep the existing event
count and contract ID checks, but replace the current event.1-only assertion
with checks matching the emitted event’s topic and data shape.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b79efdfd-6c66-41cf-9882-3e4555126cc0
📒 Files selected for processing (2)
contracts/agent-vault/src/lib.rscontracts/agent-vault/src/tests.rs
| // Verify InitEvent emission | ||
| let events = test_env.env.events().all(); | ||
| assert_eq!(events.events().len(), 1); | ||
| let event = events.events().last().unwrap(); | ||
| assert_eq!(event.0, test_env.contract_id); | ||
| assert_eq!( | ||
| event.1, | ||
| ( | ||
| Symbol::new(&test_env.env, "InitEvent"), | ||
| test_env.admin.clone() | ||
| ) | ||
| .into_val(&test_env.env) | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files contracts/agent-vault/src/tests.rs contracts/agent-vault/src/*.rs
echo
echo "== event-related symbols =="
rg -n "contractevent|InitEvent|init_event|usdc_sac|InitEvent" contracts/agent-vault/src -S
echo
echo "== tests.rs excerpt =="
nl -ba contracts/agent-vault/src/tests.rs | sed -n '1,120p'
echo
echo "== candidate source excerpts =="
for f in $(git ls-files contracts/agent-vault/src/*.rs | grep -v 'tests.rs'); do
echo "--- $f ---"
nl -ba "$f" | sed -n '1,220p'
doneRepository: clevercon-protocol/clevercon
Length of output: 29168
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for path, start, end in [
("contracts/agent-vault/src/lib.rs", 20, 40),
("contracts/agent-vault/src/tests.rs", 45, 70),
]:
print(f"--- {path}:{start}-{end} ---")
text = Path(path).read_text().splitlines()
for i in range(start, min(end, len(text)) + 1):
print(f"{i:4}: {text[i-1]}")
PYRepository: clevercon-protocol/clevercon
Length of output: 1707
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
path = Path("contracts/agent-vault/src/lib.rs")
text = path.read_text().splitlines()
for i in range(286, 322):
if i <= len(text):
print(f"{i:4}: {text[i-1]}")
PYRepository: clevercon-protocol/clevercon
Length of output: 1745
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for path in ["contracts/agent-vault/Cargo.toml", "Cargo.toml"]:
p = Path(path)
if p.exists():
print(f"--- {path} ---")
for i, line in enumerate(p.read_text().splitlines(), 1):
if "soroban-sdk" in line or "edition" in line or "version" in line:
print(f"{i:4}: {line}")
PY
echo
rg -n "events\\(\\)\\.all\\(|publish\\(&env\\)|Symbol::new\\(&.*init_event|Symbol::new\\(&.*InitEvent" contracts/agent-vault/src -SRepository: clevercon-protocol/clevercon
Length of output: 1651
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
path = Path("contracts/agent-vault/src/tests.rs")
text = path.read_text().splitlines()
for start, end in [(1560, 1610), (2055, 2090), (88, 105)]:
print(f"--- {path}:{start}-{end} ---")
for i in range(start, min(end, len(text)) + 1):
print(f"{i:4}: {text[i-1]}")
print()
PYRepository: clevercon-protocol/clevercon
Length of output: 3942
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path("contracts/agent-vault/src/tests.rs").read_text().splitlines()
for i, line in enumerate(text, 1):
if "into_val(&" in line or "event.1" in line or "events.events()" in line:
print(f"{i:4}: {line}")
PYRepository: clevercon-protocol/clevercon
Length of output: 479
🌐 Web query:
Soroban SDK 25.3.0 contractevent lower_snake_case static topic struct name data payload topic fields
💡 Result:
In the Soroban SDK, the #[contractevent] attribute macro is used to define contract events [1][2]. When using this macro on a struct, the SDK handles the event's topics and data payload according to the following conventions: Default Static Topics and Naming: By default, an event defined with #[contractevent] includes a single static topic, which is the name of the struct converted to lower_snake_case [1][3][4]. This fixed topic is automatically positioned as the first item in the event's topic list, appearing before any dynamic topics defined in the struct [1][5]. Customizing Static Topics: You can override or customize these static topics using the topics parameter within the macro: - Custom Static Topic: By providing a list of strings, such as #[contractevent(topics = ["my_contract", "an_event"])], you replace the default struct-name-based topic with your custom list [1]. - Multiple/Empty Static Topics: It is possible to specify multiple static topics (up to two) or even provide an empty list (topics = []) if no static topics are desired [3][4]. Dynamic Topics and Data Payload: - Dynamic Topics: Fields within the struct marked with the #[topic] attribute are included as dynamic topics in the event's topic list [1][3]. These follow the static topics [1]. - Data Payload: Fields within the struct that are not marked with #[topic] are automatically placed in the event's data payload section [1][3]. Example Structure: #[contractevent] pub struct MyEvent { #[topic] pub my_dynamic_topic: u32, // Becomes a dynamic topic pub my_event_data: u32, // Becomes part of the data payload } In this example, the resulting topics would be: 1. "my_event" (the struct name in lower_snake_case) 2. The value of my_dynamic_topic [1]
Citations:
- 1: https://docs.rs/soroban-sdk/latest/soroban%5Fsdk/attr.contractevent.html
- 2: https://docs.rs/soroban-sdk-macros/latest/soroban_sdk_macros/attr.contractevent.html
- 3: https://docs.rs/soroban-sdk/latest/soroban%5Fsdk/%5Fmigrating/v23%5Fcontractevent/index.html
- 4: https://docs.rs/soroban-sdk/latest/src/soroban_sdk/_migrating/v23_contractevent.rs.html
- 5: Add contractevent macro and event support to contract spec stellar/rs-soroban-sdk#1473
Match the emitted event shape and assert the payload. InitEvent uses init_event as the static topic, and usdc_sac is part of the data payload; checking only event.1 misses a wrong SAC address.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contracts/agent-vault/src/tests.rs` around lines 51 - 63, Update the
InitEvent assertions in the event verification test to expect the static topic
init_event and validate the complete payload containing both test_env.admin and
test_env.usdc_sac. Keep the existing event count and contract ID checks, but
replace the current event.1-only assertion with checks matching the emitted
event’s topic and data shape.
Closes #63
What does this PR do?
InitEventcontract event to capture the admin address and USDC SAC address during contract initialization.InitEventat the end ofCleverVault::init, ensuring the event is part of the durable event stream that indexers and explorers consume.test_init) to verify thatInitEventis emitted with the correct fields.Why is this needed?
The original
initfunction only logged the admin address, which does not appear in the durable event stream. Downstream tooling (indexers, explorers, analytics) rely on events to detect contract deployment and retrieve initial state without extra RPC calls. AddingInitEventprovides a deterministic, observable way to capture the admin and asset information at initialization.Verification
cargo testpasses, confirming the new event is emitted and stored correctly.mainbranch (branch now tracksorigin/main).Additional Notes
Please review and merge.
4:54 PM
Summary by CodeRabbit
New Features
Bug Fixes
Tests