diff --git a/src/chain/extrinsics.rs b/src/chain/extrinsics.rs index 7f808e8..c9be2f9 100644 --- a/src/chain/extrinsics.rs +++ b/src/chain/extrinsics.rs @@ -2692,9 +2692,9 @@ impl Client { self.sign_submit(&tx, pair).await } - // ──────── Registry ──────── + // ──────── Identity ──────── - /// Set on-chain identity (Registry pallet). + /// Set on-chain identity (`SubtensorModule::set_identity`, keyed by the signer's coldkey). pub async fn set_registry_identity( &self, pair: &sr25519::Pair, @@ -2705,52 +2705,28 @@ impl Client { image: &str, ) -> Result { use subxt::dynamic::Value; - let data_value = |s: &str| -> Value { - if s.is_empty() { - Value::unnamed_variant("None", []) - } else { - let bytes = s.as_bytes(); - let len = bytes.len().min(128); - Value::unnamed_variant(format!("Raw{}", len), [Value::from_bytes(&bytes[..len])]) - } - }; - let additional_entries: Vec = [("github", github), ("description", description)] - .into_iter() - .filter(|(_, value)| !value.trim().is_empty()) - .map(|(key, value)| Value::unnamed_composite([data_value(key), data_value(value)])) - .collect(); - let info = Value::named_composite([ - ("additional", Value::unnamed_composite(additional_entries)), - ("display", data_value(name)), - ("legal", Value::unnamed_variant("None", [])), - ("web", data_value(url)), - ("riot", Value::unnamed_variant("None", [])), - ("email", Value::unnamed_variant("None", [])), - ("pgp_fingerprint", Value::unnamed_variant("None", [])), - ("image", data_value(image)), - ("twitter", Value::unnamed_variant("None", [])), - ]); - let identified = AccountId::from(pair.public().0); + let bytes = |s: &str| Value::from_bytes(s.as_bytes()); + // Fields are plain byte vectors: name, url, github_repo, image, discord, description, additional. self.submit_raw_call( pair, - "Registry", + "SubtensorModule", "set_identity", - vec![Value::from_bytes(identified.0), info], + vec![ + bytes(name), + bytes(url), + bytes(github), + bytes(image), + bytes(""), + bytes(description), + bytes(""), + ], ) .await } - /// Clear on-chain identity (Registry pallet). + /// Clear on-chain identity by writing an empty identity pub async fn clear_registry_identity(&self, pair: &sr25519::Pair) -> Result { - use subxt::dynamic::Value; - let identified = AccountId::from(pair.public().0); - self.submit_raw_call( - pair, - "Registry", - "clear_identity", - vec![Value::from_bytes(identified.0)], - ) - .await + self.set_registry_identity(pair, "", "", "", "", "").await } // ──────── Utility: batch variants ──────── diff --git a/src/chain/mod.rs b/src/chain/mod.rs index ac0970d..168c4b5 100644 --- a/src/chain/mod.rs +++ b/src/chain/mod.rs @@ -1307,8 +1307,6 @@ fn format_dispatch_error(e: subxt::Error) -> anyhow::Error { || msg.contains("NotEnoughBalanceToPaySwapColdKey") { "Insufficient free balance to pay the hotkey or coldkey swap fee. Fund the signing account and retry." - } else if msg.contains("Registry::NotRegistered") { - "No on-chain identity is registered for this account (pallet Registry). Use `agcli network identity set` or pass the correct SS58." } else if msg.contains("HotKeyNotRegisteredInNetwork") { "This hotkey is not registered on any subnet yet. Pick a target netuid and register with `agcli subnet register-neuron --netuid `." } else if msg.contains("HotKeyNotRegisteredInSubNet") || msg.contains("NotRegistered") { @@ -1490,17 +1488,6 @@ fn format_dispatch_error(e: subxt::Error) -> anyhow::Error { "Pool reserves are too low for this swap or liquidity action — reduce size or wait for liquidity." } else if msg.contains("UserLiquidityDisabled") { "Subnet owner disabled user add/remove liquidity on this subnet." - } else if msg.contains("TooManyFieldsInIdentityInfo") { - "On-chain identity has too many additional fields — trim optional fields to the runtime maximum." - } else if msg.contains("Registry::CannotRegister") { - "Registry pallet: identity registration failed requirements (deposit, permissions, or eligibility)." - } else if msg.contains("CannotRegister") { - "Registry identity registration failed requirements (deposit, permissions, or eligibility)." - } else if msg.contains("NotRegistered") - && !msg.contains("HotKey") - && !msg.contains("ColdkeySwapAnnouncement") - { - "No on-chain identity registered for this account — register first or use the correct SS58." } else if msg.contains("DepositTooLow") || msg.contains("CapTooLow") || msg.contains("MinimumContributionTooLow") @@ -1924,13 +1911,13 @@ mod tests { } #[test] - fn format_dispatch_error_registry_not_registered_identity_hint() { - let err = subxt::Error::Other("Pallet error: Registry::NotRegistered".to_string()); + fn format_dispatch_error_invalid_identity_hint() { + let err = subxt::Error::Other("Pallet error: SubtensorModule::InvalidIdentity".to_string()); let result = format_dispatch_error(err); let msg = format!("{:#}", result); assert!( msg.contains("Hint:") && msg.to_lowercase().contains("identity"), - "expected Registry identity hint, got: {}", + "expected identity validation hint, got: {}", msg ); assert!( diff --git a/src/chain/queries.rs b/src/chain/queries.rs index 6933199..ef2a6f0 100644 --- a/src/chain/queries.rs +++ b/src/chain/queries.rs @@ -377,13 +377,13 @@ impl Client { // ──────── Identity Queries ──────── - /// Get on-chain identity for an account (from Registry pallet). + /// Get on-chain identity for an account (SubtensorModule `IdentitiesV2`, keyed by coldkey). pub async fn get_identity(&self, ss58: &str) -> Result> { let account_id = Self::ss58_to_account_id(ss58)?; let inner = &self.inner; let short = crate::utils::short_ss58(ss58); let result = retry_on_transient("get_identity", RPC_RETRIES, || async { - let addr = api::storage().registry().identity_of(&account_id); + let addr = api::storage().subtensor_module().identities_v2(&account_id); let r = inner .storage() .at_latest() @@ -394,7 +394,7 @@ impl Client { Ok(r) }) .await?; - Ok(result.map(|reg| chain_identity_from_registration(reg.info))) + Ok(result.map(chain_identity_from_v2)) } /// Get subnet identity (from SubtensorModule SubnetIdentitiesV3). @@ -1015,7 +1015,7 @@ impl Client { block_hash: subxt::utils::H256, ) -> Result> { let account_id = Self::ss58_to_account_id(ss58)?; - let addr = api::storage().registry().identity_of(&account_id); + let addr = api::storage().subtensor_module().identities_v2(&account_id); let result = self .inner .storage() @@ -1023,7 +1023,7 @@ impl Client { .fetch(&addr) .await .map_err(|e| Self::annotate_at_block_error(e.into(), None))?; - Ok(result.map(|reg| chain_identity_from_registration(reg.info))) + Ok(result.map(chain_identity_from_v2)) } /// Get all subnets at a specific block hash (via runtime API at block). @@ -1737,24 +1737,19 @@ impl Client { } } -/// Convert a Registry pallet `IdentityInfo` into our `ChainIdentity` struct. -fn chain_identity_from_registration( - info: api::runtime_types::pallet_registry::types::IdentityInfo, +/// Convert SubtensorModule `ChainIdentityV2` (plain UTF-8 byte fields) into our `ChainIdentity`. +fn chain_identity_from_v2( + info: api::runtime_types::pallet_subtensor::pallet::ChainIdentityV2, ) -> ChainIdentity { + let s = |b: &[u8]| String::from_utf8_lossy(b).into_owned(); ChainIdentity { - name: decode_identity_data(&info.display), - url: decode_identity_data(&info.web), - github: String::new(), // Registry pallet doesn't have github field - image: decode_identity_data(&info.image), - discord: decode_identity_data(&info.riot), - description: String::new(), - additional: info - .additional - .0 - .iter() - .map(|(k, v)| format!("{}={}", decode_identity_data(k), decode_identity_data(v))) - .collect::>() - .join(", "), + name: s(&info.name), + url: s(&info.url), + github: s(&info.github_repo), + image: s(&info.image), + discord: s(&info.discord), + description: s(&info.description), + additional: s(&info.additional), } } @@ -2362,24 +2357,6 @@ fn json_to_ss58_account(value: &serde_json::Value) -> Option { } } -fn decode_identity_data(data: &api::runtime_types::pallet_registry::types::Data) -> String { - use api::runtime_types::pallet_registry::types::Data; - macro_rules! raw_to_string { - ($($variant:ident),+) => { - match data { - Data::None => String::new(), - $(Data::$variant(b) => String::from_utf8_lossy(b).into_owned(),)+ - _ => format!("", data), - } - } - } - raw_to_string!( - Raw0, Raw1, Raw2, Raw3, Raw4, Raw5, Raw6, Raw7, Raw8, Raw9, Raw10, Raw11, Raw12, Raw13, - Raw14, Raw15, Raw16, Raw17, Raw18, Raw19, Raw20, Raw21, Raw22, Raw23, Raw24, Raw25, Raw26, - Raw27, Raw28, Raw29, Raw30, Raw31, Raw32 - ) -} - /// Crowdloan contributors map name for this runtime (`Contributions` vs legacy `Contributors`). fn crowdloan_contributors_storage_item(metadata: &subxt::Metadata) -> Result<&'static str> { const PRIMARY: &str = "Contributions"; diff --git a/src/error.rs b/src/error.rs index 632028a..cf50c9f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,7 +33,6 @@ const SURFACED_DISPATCH_PALLETS: &[&str] = &[ "swap", "drand", "utility", - "registry", "shield", ]; @@ -312,11 +311,8 @@ pub fn classify(err: &anyhow::Error) -> i32 { || msg.contains("swap::insufficientbalance") || msg.contains("swap::subtokendisabled") || msg.contains("swap::insufficientliquidity") - // Registry identity pallet - || msg.contains("registry::notregistered") - || msg.contains("registry::cannotregister") - || msg.contains("cannotregister") - || msg.contains("toomanyfieldsinidentityinfo") + // Identity (SubtensorModule::set_identity) + || msg.contains("invalididentity") // Crowdloan pallet || msg.contains("deposittoolow") || msg.contains("captoolow") @@ -436,8 +432,8 @@ pub fn hint(code: i32, msg: &str) -> Option<&'static str> { Some("Tip: Subtensor liquidity constraint (stake/swap path) — try a smaller amount or looser slippage; not always the same as DEX pool depth") } else if lower.contains("swap::subtokendisabled") { Some("Tip: This subnet’s subtoken mode is not enabled for the on-chain AMM — check subnet token/subtoken config before swaps") - } else if lower.contains("registry::notregistered") { - Some("Tip: Set on-chain identity first (`agcli network identity set`) or use an SS58 that already has one") + } else if lower.contains("invalididentity") { + Some("Tip: Identity field too long or malformed — shorten name/url/description and retry `agcli network identity set`") } else if lower.contains("insufficient") { Some("Tip: Check your balance with `agcli balance`. Transaction fees require a small reserve") } else if lower.contains("delegatetxratelimitexceeded") { @@ -1378,8 +1374,8 @@ mod tests { } #[test] - fn hint_chain_registry_not_registered() { - let h = hint(exit_code::CHAIN, "Registry::NotRegistered"); + fn hint_chain_invalid_identity() { + let h = hint(exit_code::CHAIN, "SubtensorModule::InvalidIdentity"); assert!(h.is_some_and(|s| s.contains("identity"))); } diff --git a/src/types/chain_data.rs b/src/types/chain_data.rs index 0076904..a00a42a 100644 --- a/src/types/chain_data.rs +++ b/src/types/chain_data.rs @@ -137,7 +137,7 @@ pub struct SubnetHyperparameters { pub weights_version: u64, pub weights_rate_limit: u64, pub adjustment_interval: u16, - pub activity_cutoff: u16, + pub activity_cutoff: u64, pub registration_allowed: bool, pub target_regs_per_interval: u16, pub min_burn: Balance, diff --git a/tests/extrinsic_encoding.rs b/tests/extrinsic_encoding.rs index 5c46751..aafe198 100644 --- a/tests/extrinsic_encoding.rs +++ b/tests/extrinsic_encoding.rs @@ -50,36 +50,34 @@ fn proxy_announce_differs_from_raw_account_bytes() { } #[test] -fn registry_set_identity_includes_two_top_level_fields() { - let identified = [9u8; 32]; - let info = Value::named_composite([ - ( - "display", - Value::unnamed_variant("Raw4", [Value::from_bytes(b"test")]), - ), - ("legal", Value::unnamed_variant("None", [])), - ("web", Value::unnamed_variant("None", [])), - ("riot", Value::unnamed_variant("None", [])), - ("email", Value::unnamed_variant("None", [])), - ("pgp_fingerprint", Value::unnamed_variant("None", [])), - ("image", Value::unnamed_variant("None", [])), - ("twitter", Value::unnamed_variant("None", [])), - ("additional", Value::unnamed_composite([])), - ]); - let with_info = encode_dynamic( - "Registry", - "set_identity", - vec![Value::from_bytes(identified), info], - ); +fn set_identity_encodes_seven_flat_byte_fields() { + // SubtensorModule::set_identity takes seven plain Vec fields keyed by the + // signer's coldkey — name, url, github_repo, image, discord, description, additional. + let bytes = |s: &[u8]| Value::from_bytes(s); + let full = vec![ + bytes(b"name"), + bytes(b"url"), + bytes(b"github"), + bytes(b"image"), + bytes(b""), + bytes(b"desc"), + bytes(b""), + ]; + let with_info = encode_dynamic("SubtensorModule", "set_identity", full); + assert!(!with_info.is_empty()); assert!( encode_dynamic_panics( - "Registry", + "SubtensorModule", "set_identity", - vec![Value::from_bytes(identified)], + vec![bytes(b"name"), bytes(b"url")], ), - "set_identity must include identity info as second arg" + "set_identity must encode all seven identity fields" + ); + // The standalone Registry pallet was removed from the runtime. + assert!( + encode_dynamic_panics("Registry", "set_identity", Vec::::new()), + "Registry pallet should no longer exist in metadata" ); - assert!(!with_info.is_empty()); } #[test]