self_agent_sdk/
registration.rs1use alloy::primitives::{keccak256, Address, U256};
14use alloy::signers::local::PrivateKeySigner;
15use alloy::signers::Signer;
16use alloy::sol_types::SolValue;
17use serde::{Deserialize, Serialize};
18
19#[derive(Debug, Clone, Default, Serialize, Deserialize)]
30pub struct RegistrationDisclosures {
31 pub minimum_age: u8,
32 pub ofac: bool,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct SignatureParts {
38 pub r: String,
40 pub s: String,
42 pub v: u64,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct SignedRegistrationChallenge {
49 pub message_hash: String,
51 pub parts: SignatureParts,
53 pub agent_address: String,
55}
56
57pub fn get_registration_config_index(disclosures: &RegistrationDisclosures) -> u8 {
59 match (disclosures.minimum_age, disclosures.ofac) {
60 (18, true) => 4,
61 (21, true) => 5,
62 (18, false) => 1,
63 (21, false) => 2,
64 (0, true) => 3,
65 _ => 0,
66 }
67}
68
69pub fn compute_registration_challenge_hash(
76 human_identifier: Address,
77 chain_id: u64,
78 registry_address: Address,
79 nonce: u64,
80) -> [u8; 32] {
81 let packed = (
82 "self-agent-id:register:".to_string(),
83 human_identifier,
84 U256::from(chain_id),
85 registry_address,
86 U256::from(nonce),
87 )
88 .abi_encode_packed();
89 keccak256(packed).into()
90}
91
92pub async fn sign_registration_challenge(
100 private_key: &str,
101 human_identifier: Address,
102 chain_id: u64,
103 registry_address: Address,
104 nonce: u64,
105) -> Result<SignedRegistrationChallenge, crate::Error> {
106 let signer: PrivateKeySigner = private_key
107 .parse::<PrivateKeySigner>()
108 .map_err(|_| crate::Error::InvalidPrivateKey)?;
109 let hash = compute_registration_challenge_hash(human_identifier, chain_id, registry_address, nonce);
110 let sig = signer
111 .sign_message(&hash)
112 .await
113 .map_err(|e| crate::Error::SigningError(e.to_string()))?;
114 let bytes = sig.as_bytes();
115 if bytes.len() != 65 {
116 return Err(crate::Error::InvalidSignature);
117 }
118 let mut v = bytes[64] as u64;
119 if v == 0 || v == 1 {
120 v += 27;
121 }
122
123 Ok(SignedRegistrationChallenge {
124 message_hash: format!("0x{}", hex::encode(hash)),
125 parts: SignatureParts {
126 r: format!("0x{}", hex::encode(&bytes[0..32])),
127 s: format!("0x{}", hex::encode(&bytes[32..64])),
128 v,
129 },
130 agent_address: format!("{:#x}", signer.address()),
131 })
132}
133
134pub fn build_simple_register_user_data_ascii(disclosures: &RegistrationDisclosures) -> String {
138 format!("R{}", get_registration_config_index(disclosures))
139}
140
141pub fn build_simple_deregister_user_data_ascii(disclosures: &RegistrationDisclosures) -> String {
145 format!("D{}", get_registration_config_index(disclosures))
146}
147
148pub fn build_advanced_register_user_data_ascii(
152 agent_address: &str,
153 sig: &SignatureParts,
154 disclosures: &RegistrationDisclosures,
155) -> String {
156 let cfg = get_registration_config_index(disclosures);
157 let addr_hex = agent_address.trim_start_matches("0x").to_lowercase();
158 let r_hex = sig.r.trim_start_matches("0x").to_lowercase();
159 let s_hex = sig.s.trim_start_matches("0x").to_lowercase();
160 let v_hex = format!("{:02x}", sig.v);
161 format!("K{cfg}{addr_hex}{r_hex}{s_hex}{v_hex}")
162}
163
164pub fn build_advanced_deregister_user_data_ascii(
168 agent_address: &str,
169 disclosures: &RegistrationDisclosures,
170) -> String {
171 let cfg = get_registration_config_index(disclosures);
172 let addr_hex = agent_address.trim_start_matches("0x").to_lowercase();
173 format!("X{cfg}{addr_hex}")
174}
175
176pub fn build_wallet_free_register_user_data_ascii(
180 agent_address: &str,
181 guardian_address: &str,
182 sig: &SignatureParts,
183 disclosures: &RegistrationDisclosures,
184) -> String {
185 let cfg = get_registration_config_index(disclosures);
186 let addr_hex = agent_address.trim_start_matches("0x").to_lowercase();
187 let guardian_hex = guardian_address.trim_start_matches("0x").to_lowercase();
188 let r_hex = sig.r.trim_start_matches("0x").to_lowercase();
189 let s_hex = sig.s.trim_start_matches("0x").to_lowercase();
190 let v_hex = format!("{:02x}", sig.v);
191 format!("W{cfg}{addr_hex}{guardian_hex}{r_hex}{s_hex}{v_hex}")
192}