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
12 changes: 12 additions & 0 deletions contracts/agent-vault/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ use soroban_sdk::{

// Events

#[contractevent]
pub struct InitEvent {
#[topic]
pub admin: Address,
pub usdc_sac: Address,
}

#[contractevent]
pub struct DepositEvent {
#[topic]
Expand Down Expand Up @@ -305,6 +312,11 @@ impl AgentVault {
admin,
usdc_sac
);
InitEvent {
admin: admin.clone(),
usdc_sac: usdc_sac.clone(),
}
.publish(&env);
Ok(())
}

Expand Down
29 changes: 24 additions & 5 deletions contracts/agent-vault/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{AgentVault, AgentVaultClient, DataKey, VaultError};
use crate::{AgentVault, AgentVaultClient, DataKey, InitEvent, VaultError};
use soroban_sdk::testutils::storage::Persistent as _;
use soroban_sdk::testutils::{Address as _, Events, Ledger as _};
use soroban_sdk::{token, Address, Env};
use soroban_sdk::{token, Address, Env, IntoVal, Symbol};

struct TestEnv {
env: Env,
Expand Down Expand Up @@ -48,6 +48,20 @@ fn test_init() {
let test_env = setup_test();
test_env.client.init(&test_env.admin, &test_env.usdc_sac);

// 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)
);
Comment on lines +51 to +63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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'
done

Repository: 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]}")
PY

Repository: 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]}")
PY

Repository: 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 -S

Repository: 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()
PY

Repository: 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}")
PY

Repository: 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:


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.


// Verify admin and USDC SAC are stored in instance storage
test_env.env.as_contract(&test_env.contract_id, || {
let stored_admin: Address = test_env
Expand All @@ -74,10 +88,15 @@ fn test_init() {
fn test_init_twice_panics() {
let test_env = setup_test();
test_env.client.init(&test_env.admin, &test_env.usdc_sac);
let events_after_first = test_env.env.events().all().events().len();

let result = test_env
.client
.try_init(&test_env.admin, &test_env.usdc_sac);
assert!(result == Err(Ok(VaultError::AlreadyInitialized)));

let events_after_second = test_env.env.events().all().events().len();
assert_eq!(events_after_first, events_after_second);
}

// 2. Deposit Tests
Expand Down Expand Up @@ -1556,7 +1575,7 @@ fn test_pause_emits_pause_event() {
test_env.client.pause(&test_env.admin);

let events = test_env.env.events().all();
assert_eq!(events.events().len(), 1);
assert_eq!(events.events().len(), 2);
}

#[test]
Expand All @@ -1578,7 +1597,7 @@ fn test_unpause_emits_unpause_event() {
test_env.client.unpause(&test_env.admin);

let events = test_env.env.events().all();
assert_eq!(events.events().len(), 1);
assert_eq!(events.events().len(), 3);
}

#[test]
Expand Down Expand Up @@ -2052,7 +2071,7 @@ fn test_update_admin_emits_event() {
t.client.update_admin(&t.admin, &new_admin);

let events = t.env.events().all();
assert_eq!(events.events().len(), 1);
assert_eq!(events.events().len(), 2);
}

/// Chained rotation: new admin can rotate again.
Expand Down