use {
crate::{
move_bindings::{sui_framework::coin::Coin as MoveCoin, talus::us::US},
sui,
},
serde::{Deserialize, Serialize},
talus_sui_move::{MoveStruct, MoveType},
};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ObjectIdentity {
pub object_id: sui::types::Address,
}
impl ObjectIdentity {
pub const fn new(object_id: sui::types::Address) -> Self {
Self { object_id }
}
pub const fn object_id(&self) -> sui::types::Address {
self.object_id
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SharedRoot {
pub object_id: sui::types::Address,
pub initial_shared_version: u64,
}
impl SharedRoot {
pub const fn new(object_id: sui::types::Address, initial_shared_version: u64) -> Self {
Self {
object_id,
initial_shared_version,
}
}
pub const fn object_id(&self) -> sui::types::Address {
self.object_id
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct UsTokenConfig {
pub package_id: sui::types::Address,
pub protected_treasury: sui::types::Address,
pub metadata: sui::types::Address,
}
impl UsTokenConfig {
pub const fn new(
package_id: sui::types::Address,
protected_treasury: sui::types::Address,
metadata: sui::types::Address,
) -> Self {
Self {
package_id,
protected_treasury,
metadata,
}
}
pub fn type_tag(&self) -> sui::types::TypeTag {
crate::move_bindings::talus::with_packages(
self.package_id,
self.package_id,
US::type_tag_static,
)
}
pub fn coin_type_tag(&self) -> sui::types::StructTag {
crate::move_bindings::sui_framework::with_packages(
sui::types::Address::from_static("0x2"),
sui::types::Address::from_static("0x2"),
|| {
crate::move_bindings::talus::with_packages(
self.package_id,
self.package_id,
MoveCoin::<US>::struct_tag_static,
)
},
)
}
pub fn qualified_type(&self) -> String {
let tag = crate::move_bindings::talus::with_packages(
self.package_id,
self.package_id,
US::struct_tag_static,
);
format!("{}::{}::{}", tag.address(), tag.module(), tag.name())
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct NexusObjects {
pub chain_id: String,
pub network_id: sui::types::Address,
pub tool_registry: SharedRoot,
pub network_auth: SharedRoot,
pub agent_registry: SharedRoot,
pub leader_registry: SharedRoot,
pub priority_fee_vault: SharedRoot,
pub runtime_authority: SharedRoot,
pub leader_admin_cap: ObjectIdentity,
pub tool_registry_admin_cap: ObjectIdentity,
pub slashing_cap: ObjectIdentity,
pub priority_fee_vault_owner_cap: ObjectIdentity,
pub initial_leader_cap: ObjectIdentity,
pub runtime_authority_cap: ObjectIdentity,
pub us_token: UsTokenConfig,
}
#[cfg(test)]
mod tests {
use super::*;
const ENVIRONMENT: &str = r#"
chain_id = "test-chain"
network_id = "0x01"
[tool_registry]
object_id = "0x02"
initial_shared_version = 2
[network_auth]
object_id = "0x03"
initial_shared_version = 3
[agent_registry]
object_id = "0x04"
initial_shared_version = 3
[leader_registry]
object_id = "0x05"
initial_shared_version = 3
[priority_fee_vault]
object_id = "0x06"
initial_shared_version = 3
[runtime_authority]
object_id = "0x0f"
initial_shared_version = 3
[leader_admin_cap]
object_id = "0x07"
[tool_registry_admin_cap]
object_id = "0x08"
[slashing_cap]
object_id = "0x09"
[priority_fee_vault_owner_cap]
object_id = "0x0a"
[initial_leader_cap]
object_id = "0x0b"
[runtime_authority_cap]
object_id = "0x10"
[us_token]
package_id = "0x0c"
protected_treasury = "0x0d"
metadata = "0x0e"
"#;
#[test]
fn stable_environment_round_trips_through_toml() {
let objects: NexusObjects = toml::from_str(ENVIRONMENT).unwrap();
let encoded = toml::to_string(&objects).unwrap();
let decoded: NexusObjects = toml::from_str(&encoded).unwrap();
assert_eq!(decoded, objects);
assert_eq!(objects.tool_registry.initial_shared_version, 2);
assert_eq!(objects.runtime_authority.object_id, address("0x0f"));
assert_eq!(objects.us_token.metadata, address("0x0e"));
}
#[test]
fn stable_environment_requires_every_root() {
let without_root = ENVIRONMENT.replace(
"[leader_registry]\nobject_id = \"0x05\"\ninitial_shared_version = 3\n\n",
"",
);
let error = toml::from_str::<NexusObjects>(&without_root)
.unwrap_err()
.to_string();
assert!(error.contains("missing field `leader_registry`"));
}
#[test]
fn shared_root_rejects_current_object_truth() {
let with_digest = ENVIRONMENT.replace(
"[tool_registry]\nobject_id = \"0x02\"\ninitial_shared_version = 2",
"[tool_registry]\nobject_id = \"0x02\"\ninitial_shared_version = 2\ndigest = \"mutable\"",
);
let error = toml::from_str::<NexusObjects>(&with_digest)
.unwrap_err()
.to_string();
assert!(error.contains("unknown field `digest`"));
}
#[test]
fn stable_environment_rejects_package_authority() {
let with_packages = format!("{ENVIRONMENT}\n[packages]\nregistry = \"0x0f\"\n");
let error = toml::from_str::<NexusObjects>(&with_packages)
.unwrap_err()
.to_string();
assert!(error.contains("unknown field `packages`"));
}
#[test]
fn token_type_helpers_use_the_configured_package() {
let token = UsTokenConfig::new(address("0xc1"), address("0xc2"), address("0xc3"));
let type_tag = token.type_tag();
let sui::types::TypeTag::Struct(type_tag) = type_tag else {
panic!("expected US struct tag");
};
assert_eq!(*type_tag.address(), address("0xc1"));
assert_eq!(
token.coin_type_tag().type_params().first(),
Some(&sui::types::TypeTag::Struct(type_tag))
);
assert!(token
.qualified_type()
.starts_with(&address("0xc1").to_string()));
}
fn address(value: &'static str) -> sui::types::Address {
sui::types::Address::from_static(value)
}
}