-
Notifications
You must be signed in to change notification settings - Fork 35
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
base: develop
Are you sure you want to change the base?
Changes from all commits
1d59dcc
19dbacf
9846d86
b01225c
6025755
da5b883
2bc324c
670be1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}; | ||
|
@@ -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, | ||
|
@@ -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)] | ||
|
@@ -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 | ||
|
@@ -325,7 +337,7 @@ impl NameCommand { | |
verbose, | ||
mut opts, | ||
} => { | ||
anyhow::ensure!( | ||
ensure!( | ||
domain.num_labels() == 2, | ||
"domain to register must consist of two labels" | ||
); | ||
|
@@ -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, | ||
|
@@ -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, | ||
|
@@ -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, | ||
|
@@ -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" | ||
); | ||
|
@@ -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" | ||
); | ||
|
@@ -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 | ||
|
@@ -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()) | ||
|
@@ -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, | ||
|
@@ -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()) | ||
|
@@ -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() | ||
} | ||
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( | ||
|
@@ -1933,6 +2009,13 @@ impl IotaNamesNftProxy { | |
} | ||
|
||
fn module_name(&self) -> &'static str { | ||
match self { | ||
IotaNamesNftProxy::Domain(_) => "controller", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tbh this feels weird, very tailored to solve this PR's issue There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I know. But how would you rather do it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to either:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also applies to |
||
IotaNamesNftProxy::Subdomain(_) => "subdomain_proxy", | ||
} | ||
} | ||
|
||
fn subdomain_module_name(&self) -> &'static str { | ||
match self { | ||
IotaNamesNftProxy::Domain(_) => "subdomains", | ||
IotaNamesNftProxy::Subdomain(_) => "subdomain_proxy", | ||
|
@@ -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"] | ||
|
@@ -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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the main package?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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