Skip to content

feat(CLI)!: Split iota names set-target-address #6878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
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
149 changes: 116 additions & 33 deletions crates/iota/src/name_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
time::{Duration, SystemTime, UNIX_EPOCH},
};

use anyhow::bail;
use anyhow::{bail, ensure};
use chrono::{Utc, prelude::DateTime};
use clap::Parser;
use iota_graphql_rpc_client::simple_client::{GraphqlQueryVariable, SimpleClient};
Expand All @@ -32,6 +32,7 @@ use iota_types::{
digests::{ChainIdentifier, TransactionDigest},
};
use move_core_types::{
account_address::AccountAddress,
annotated_value::{MoveFieldLayout, MoveStructLayout, MoveTypeLayout},
identifier::Identifier,
language_storage::StructTag,
Expand Down Expand Up @@ -143,7 +144,8 @@ pub enum NameCommand {
SetTargetAddress {
/// The full name of the domain. Ex. my-domain.iota
domain: Domain,
/// The address to which the domain will point
/// The address to which the domain will point. Defaults to the current
/// active address.
new_address: Option<IotaAddress>,
// Whether to print detailed output.
#[arg(long)]
Expand Down Expand Up @@ -188,6 +190,16 @@ pub enum NameCommand {
#[command(flatten)]
opts: OptsWithGas,
},
/// Unset the target address for a domain
UnsetTargetAddress {
/// The full name of the domain. Ex. my-domain.iota
domain: Domain,
// Whether to print detailed output.
#[arg(long)]
verbose: bool,
#[command(flatten)]
opts: OptsWithGas,
},
/// Unset keyed user data
UnsetUserData {
/// The full name of the domain. Ex. my-domain.iota
Expand Down Expand Up @@ -325,7 +337,7 @@ impl NameCommand {
verbose,
mut opts,
} => {
anyhow::ensure!(
ensure!(
domain.num_labels() == 2,
"domain to register must consist of two labels"
);
Expand Down Expand Up @@ -511,22 +523,28 @@ impl NameCommand {
verbose,
opts,
} => {
let nft_id = get_owned_nft_by_name::<IotaNamesRegistration>(&domain, context)
.await?
.id();
let entry = get_registry_entry(&domain, &iota_client).await?;
let new_address =
get_identity_address(new_address.map(KeyIdentity::Address), context)?;
if entry
.name_record
.target_address
.is_some_and(|a| a == new_address)
{
bail!("target address is already set to the given value");
}
let nft = get_proxy_nft_by_name(&domain, context).await?;
let iota_names_config = get_iota_names_config(&iota_client).await?;

let res = IotaClientCommands::Call {
package: iota_names_config.package_address.into(),
module: "controller".to_owned(),
module: nft.module_name().to_owned(),
function: "set_target_address".to_owned(),
type_args: Default::default(),
args: vec![
IotaJsonValue::from_object_id(iota_names_config.object_id),
IotaJsonValue::from_object_id(nft_id),
IotaJsonValue::new(serde_json::to_value(
new_address.into_iter().collect::<Vec<_>>(),
)?)?,
IotaJsonValue::from_object_id(nft.id()),
IotaJsonValue::new(serde_json::to_value(vec![new_address])?)?,
IotaJsonValue::from_object_id(IOTA_CLOCK_OBJECT_ID),
],
gas_price: None,
Expand Down Expand Up @@ -589,21 +607,16 @@ impl NameCommand {
verbose,
opts,
} => {
let nft_id = get_owned_nft_by_name::<IotaNamesRegistration>(&domain, context)
.await?
.id();
let nft = get_proxy_nft_by_name(&domain, context).await?;
let iota_names_config = get_iota_names_config(&iota_client).await?;

let res = IotaClientCommands::Call {
package: IOTA_FRAMEWORK_PACKAGE_ID,
module: "transfer".to_owned(),
function: "public_transfer".to_owned(),
type_args: vec![TypeTag::from_str(&format!(
"{}::iota_names_registration::IotaNamesRegistration",
iota_names_config.package_address
))?],
type_args: vec![nft.type_(iota_names_config.package_address.into()).into()],
args: vec![
IotaJsonValue::from_object_id(nft_id),
IotaJsonValue::from_object_id(nft.id()),
IotaJsonValue::new(serde_json::to_value(address)?)?,
],
gas_price: None,
Expand Down Expand Up @@ -645,6 +658,45 @@ impl NameCommand {
})
.await?
}
Self::UnsetTargetAddress {
domain,
opts,
verbose,
} => {
let entry = get_registry_entry(&domain, &iota_client).await?;
if entry.name_record.target_address.is_none() {
bail!("target address is already unset");
}

let nft = get_proxy_nft_by_name(&domain, context).await?;
let iota_names_config = get_iota_names_config(&iota_client).await?;

let res = IotaClientCommands::Call {
package: nft.package_id(&iota_client).await?,
module: nft.module_name().to_owned(),
function: "set_target_address".to_owned(),
type_args: Default::default(),
args: vec![
IotaJsonValue::from_object_id(iota_names_config.object_id),
IotaJsonValue::from_object_id(nft.id()),
IotaJsonValue::new(serde_json::to_value(Vec::<IotaAddress>::new())?)?,
IotaJsonValue::from_object_id(IOTA_CLOCK_OBJECT_ID),
],
gas_price: None,
opts,
}
.execute(context)
.await?;

handle_transaction_result(res, verbose, async |res| {
let entry = get_registry_entry(&domain, &iota_client).await?;
Ok(NameCommandResult::SetTargetAddress {
entry,
digest: res.digest,
})
})
.await?
}
Self::UnsetUserData {
domain,
key,
Expand Down Expand Up @@ -768,7 +820,7 @@ impl AuctionCommand {
let auction = auction_house.get_auction(&domain, &iota_client).await?;
let min_price = auction.current_bid.value() + MIN_OVERBID;
let amount = amount.unwrap_or(min_price);
anyhow::ensure!(
ensure!(
amount >= min_price,
"bid amount must be at least {min_price} for this domain"
);
Expand Down Expand Up @@ -858,7 +910,7 @@ impl AuctionCommand {
.await?
.get_price(domain.label(1).unwrap())?;
let amount = amount.unwrap_or(min_price);
anyhow::ensure!(
ensure!(
amount >= min_price,
"bid amount must be at least {min_price} for this domain"
);
Expand Down Expand Up @@ -993,9 +1045,9 @@ impl SubdomainCommand {
let iota_names_config = get_iota_names_config(&iota_client).await?;

let parent = get_proxy_nft_by_name(&parent, context).await?;
anyhow::ensure!(!parent.has_expired(), "parent NFT has expired");
let package_id = parent.package_id(&iota_client).await?;
let module_name = parent.module_name();
ensure!(!parent.has_expired(), "parent NFT has expired");
let package_id = parent.subdomain_package_id(&iota_client).await?;
let module_name = parent.subdomain_module_name();

let target_address = if let Some(target_address) = target_address {
target_address
Expand Down Expand Up @@ -1046,13 +1098,13 @@ impl SubdomainCommand {
let iota_names_config = get_iota_names_config(&iota_client).await?;

let parent = get_proxy_nft_by_name(&parent, context).await?;
anyhow::ensure!(!parent.has_expired(), "parent NFT has expired");
let package_id = parent.package_id(&iota_client).await?;
let module_name = parent.module_name();
ensure!(!parent.has_expired(), "parent NFT has expired");
let package_id = parent.subdomain_package_id(&iota_client).await?;
let module_name = parent.subdomain_module_name();

let expiration_timestamp =
expiration_timestamp.unwrap_or(Timestamp(parent.expiration_timestamp_ms()));
anyhow::ensure!(
ensure!(
expiration_timestamp
.as_system_time()
.duration_since(SystemTime::now())
Expand Down Expand Up @@ -1104,8 +1156,8 @@ impl SubdomainCommand {
let iota_names_config = get_iota_names_config(&iota_client).await?;

let parent = get_proxy_nft_by_name(&parent, context).await?;
let package_id = parent.package_id(&iota_client).await?;
let module_name = parent.module_name();
let package_id = parent.subdomain_package_id(&iota_client).await?;
let module_name = parent.subdomain_module_name();

let res = IotaClientCommands::Call {
package: package_id,
Expand Down Expand Up @@ -1141,7 +1193,7 @@ impl SubdomainCommand {
opts,
} => {
let nft = get_owned_nft_by_name::<SubdomainRegistration>(&domain, context).await?;
anyhow::ensure!(
ensure!(
expiration_timestamp.as_system_time() > nft.expiration_time(),
"new expiration time is not after old expiration: {}",
chrono::DateTime::<chrono::Utc>::from(nft.expiration_time())
Expand Down Expand Up @@ -1911,7 +1963,31 @@ impl IotaNamesNftProxy {
fn id(&self) -> ObjectID;
}

fn type_(&self, package_id: AccountAddress) -> StructTag {
match self {
IotaNamesNftProxy::Domain(_) => IotaNamesRegistration::type_(package_id),
IotaNamesNftProxy::Subdomain(_) => SubdomainRegistration::type_(package_id),
}
}

async fn package_id(&self, client: &IotaClient) -> anyhow::Result<ObjectID> {
Ok(match self {
IotaNamesNftProxy::Domain(_) => {
let names_config = get_iota_names_config(client).await?;
names_config.package_address.into()
Copy link
Member

Choose a reason for hiding this comment

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

the main package?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that's where the controller module is.

Copy link
Member

Choose a reason for hiding this comment

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

But you're not using the controller module anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I am now using it for non-subdomains. For the subdomains it uses the subdomains module

}
IotaNamesNftProxy::Subdomain(_) => {
fetch_package_id_by_module_and_name(
client,
&Identifier::from_str("subdomain_proxy")?,
&Identifier::from_str("SubdomainProxyAuth")?,
)
.await?
}
})
}

async fn subdomain_package_id(&self, client: &IotaClient) -> anyhow::Result<ObjectID> {
Ok(match self {
IotaNamesNftProxy::Domain(_) => {
fetch_package_id_by_module_and_name(
Expand All @@ -1933,6 +2009,13 @@ impl IotaNamesNftProxy {
}

fn module_name(&self) -> &'static str {
match self {
IotaNamesNftProxy::Domain(_) => "controller",
Copy link
Member

Choose a reason for hiding this comment

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

Tbh this feels weird, very tailored to solve this PR's issue

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I know. But how would you rather do it?

Copy link
Member

Choose a reason for hiding this comment

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

I think we need to either:

  • resolve the module name when it's actually needed; or
  • rename this function
    module_name is too broad for this, maybe controller_module_name or something as subdomain_proxy essentially forward to the controller anyway

Copy link
Member

Choose a reason for hiding this comment

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

Also applies to package_id though, I think I need to think a bit more about it, I don't like it

IotaNamesNftProxy::Subdomain(_) => "subdomain_proxy",
}
}

fn subdomain_module_name(&self) -> &'static str {
match self {
IotaNamesNftProxy::Domain(_) => "subdomains",
IotaNamesNftProxy::Subdomain(_) => "subdomain_proxy",
Expand Down Expand Up @@ -2165,7 +2248,7 @@ async fn get_auction_house_id(
let response = client
.execute_to_graphql(query.to_string(), true, vec![variable], vec![])
.await?;
anyhow::ensure!(response.errors().is_empty(), "{:?}", response.errors());
ensure!(response.errors().is_empty(), "{:?}", response.errors());

let response_body = response.response_body_json();
let object_id_str = response_body["data"]["objects"]["edges"][0]["node"]["address"]
Expand All @@ -2183,7 +2266,7 @@ async fn get_object_from_bcs<T: DeserializeOwned>(
.read_api()
.get_object_with_options(object_id, IotaObjectDataOptions::new().with_bcs())
.await?;
anyhow::ensure!(
ensure!(
object_response.error.is_none(),
"{:?}",
object_response.error
Expand Down
Loading