Skip to content

Commit

Permalink
Add wallet implicit account creation address (#1556)
Browse files Browse the repository at this point in the history
* Add implicit_account_creation_address method

* Add it to CLI

* Use localhost for now

* Add to bindings core

* Nodejs bindings

* Python binding

* Nit

* Remove stronghold required feature

* Docs

* Example doc
  • Loading branch information
thibault-martinez committed Nov 2, 2023
1 parent 8e8f61f commit 4ced6c2
Show file tree
Hide file tree
Showing 14 changed files with 118 additions and 8 deletions.
3 changes: 3 additions & 0 deletions bindings/core/src/method/wallet_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ pub enum WalletCommandMethod {
#[cfg(feature = "participation")]
#[cfg_attr(docsrs, doc(cfg(feature = "participation")))]
GetVotingPower,
/// Returns the implicit account creation address of the wallet if it is Ed25519 based.
/// Expected response: [`Bech32Address`](crate::Response::Bech32Address)
ImplicitAccountCreationAddress,
/// Returns all incoming transactions of the wallet
/// Expected response:
/// [`Transactions`](crate::Response::Transactions)
Expand Down
4 changes: 4 additions & 0 deletions bindings/core/src/method_handler/wallet_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ pub(crate) async fn call_wallet_command_method_internal(
let voting_power = wallet.get_voting_power().await?;
Response::VotingPower(voting_power.to_string())
}
WalletCommandMethod::ImplicitAccountCreationAddress => {
let implicit_account_creation_address = wallet.implicit_account_creation_address().await?;
Response::Bech32Address(implicit_account_creation_address)
}
WalletCommandMethod::IncomingTransactions => {
let transactions = wallet.incoming_transactions().await;
Response::Transactions(transactions.iter().map(TransactionWithMetadataDto::from).collect())
Expand Down
1 change: 1 addition & 0 deletions bindings/core/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ pub enum Response {
/// - [`HexPublicKeyToBech32Address`](crate::method::ClientMethod::HexPublicKeyToBech32Address)
/// - [`HexToBech32`](crate::method::ClientMethod::HexToBech32)
/// - [`NftIdToBech32`](crate::method::ClientMethod::NftIdToBech32)
/// - [`ImplicitAccountCreationAddress`](crate::method::WalletCommandMethod::ImplicitAccountCreationAddress)
Bech32Address(Bech32Address),
/// - [`Faucet`](crate::method::ClientMethod::RequestFundsFromFaucet)
Faucet(String),
Expand Down
4 changes: 4 additions & 0 deletions bindings/nodejs/lib/types/wallet/bridge/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ export type __PendingTransactionsMethod__ = {
name: 'pendingTransactions';
};

export type __ImplicitAccountCreationAddressMethod__ = {
name: 'implicitAccountCreationAddress';
};

export type __IncomingTransactionsMethod__ = {
name: 'incomingTransactions';
};
Expand Down
2 changes: 2 additions & 0 deletions bindings/nodejs/lib/types/wallet/bridge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
__AddressesWithUnspentOutputsMethod__,
__OutputsMethod__,
__PendingTransactionsMethod__,
__ImplicitAccountCreationAddressMethod__,
__IncomingTransactionsMethod__,
__TransactionsMethod__,
__UnspentOutputsMethod__,
Expand Down Expand Up @@ -96,6 +97,7 @@ export type __AccountMethod__ =
| __AddressesWithUnspentOutputsMethod__
| __OutputsMethod__
| __PendingTransactionsMethod__
| __ImplicitAccountCreationAddressMethod__
| __IncomingTransactionsMethod__
| __TransactionsMethod__
| __UnspentOutputsMethod__
Expand Down
16 changes: 16 additions & 0 deletions bindings/nodejs/lib/wallet/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,22 @@ export class Account {
return plainToInstance(TransactionWithMetadata, parsed.payload);
}

/**
* Returns the implicit account creation address of the wallet if it is Ed25519 based.
*
* @returns The implicit account creation address.
*/
async implicitAccountCreationAddress(): Promise<Bech32Address> {
const response = await this.methodHandler.callAccountMethod(
this.meta.index,
{
name: 'implicitAccountCreationAddress',
},
);

return JSON.parse(response).payload;
}

/**
* List all incoming transactions of the account.
*
Expand Down
7 changes: 7 additions & 0 deletions bindings/python/iota_sdk/wallet/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,13 @@ def unspent_outputs(
)
return [from_dict(OutputData, o) for o in outputs]

def implicit_account_creation_address(self) -> str:
"""Returns the implicit account creation address of the wallet if it is Ed25519 based.
"""
return self._call_account_method(
'implicitAccountCreationAddress'
)

def incoming_transactions(self) -> List[Transaction]:
"""Returns all incoming transactions of the account.
"""
Expand Down
2 changes: 1 addition & 1 deletion cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
};

const DEFAULT_LOG_LEVEL: &str = "debug";
const DEFAULT_NODE_URL: &str = "https://api.testnet.shimmer.network";
const DEFAULT_NODE_URL: &str = "http://localhost:8080";
const DEFAULT_STRONGHOLD_SNAPSHOT_PATH: &str = "./stardust-cli-wallet.stronghold";
const DEFAULT_WALLET_DATABASE_PATH: &str = "./stardust-cli-wallet-db";

Expand Down
1 change: 1 addition & 0 deletions cli/src/wallet_cli/completer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const WALLET_COMMANDS: &[&str] = &[
"destroy-foundry",
"exit",
"faucet",
"implicit-account-creation-address",
"melt-native-token",
"mint-native-token",
"mint-nft",
Expand Down
18 changes: 14 additions & 4 deletions cli/src/wallet_cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ pub enum WalletCommand {
Faucet {
/// Address the faucet sends the funds to, defaults to the wallet address.
address: Option<Bech32Address>,
/// URL of the faucet, default to <https://faucet.testnet.shimmer.network/api/enqueue>.
/// URL of the faucet, default to <http://localhost:8088/api/enqueue>.
url: Option<String>,
},
/// Returns the implicit account creation address of the wallet if it is Ed25519 based.
ImplicitAccountCreationAddress,
/// Mint additional native tokens.
MintNativeToken {
/// Token ID to be minted, e.g. 0x087d205988b733d97fb145ae340e27a8b19554d1ceee64574d7e5ff66c45f69e7a0100000000.
Expand Down Expand Up @@ -552,15 +554,20 @@ pub async fn faucet_command(wallet: &Wallet, address: Option<Bech32Address>, url
wallet.address().await
};

let faucet_url = url
.as_deref()
.unwrap_or("https://faucet.testnet.shimmer.network/api/enqueue");
let faucet_url = url.as_deref().unwrap_or("http://localhost:8088/api/enqueue");

println_log_info!("{}", request_funds_from_faucet(faucet_url, &address).await?);

Ok(())
}

// `implicit-account-creation-address` command
pub async fn implicit_account_creation_address(wallet: &Wallet) -> Result<(), Error> {
println_log_info!("{}", wallet.implicit_account_creation_address().await?);

Ok(())
}

// `melt-native-token` command
pub async fn melt_native_token_command(wallet: &Wallet, token_id: String, amount: String) -> Result<(), Error> {
let transaction = wallet
Expand Down Expand Up @@ -1077,6 +1084,9 @@ pub async fn prompt_internal(
return Ok(PromptResponse::Done);
}
WalletCommand::Faucet { address, url } => faucet_command(wallet, address, url).await,
WalletCommand::ImplicitAccountCreationAddress => {
implicit_account_creation_address(wallet).await
}
WalletCommand::MeltNativeToken { token_id, amount } => {
melt_native_token_command(wallet, token_id, amount).await
}
Expand Down
9 changes: 8 additions & 1 deletion sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ wasm-bindgen-futures = { version = "0.4.37", default-features = false, optional

[dev-dependencies]
iota-sdk = { path = ".", default-features = false, features = ["rand"] }
pretty_assertions = { version = "1.4.0", default-features = false, features = [ "alloc" ] }
pretty_assertions = { version = "1.4.0", default-features = false, features = [
"alloc",
] }

dotenvy = { version = "0.15.7", default-features = false }
fern-logger = { version = "0.5.0", default-features = false }
Expand Down Expand Up @@ -340,6 +342,11 @@ name = "destroy_account_output"
path = "examples/how_tos/account/destroy.rs"
required-features = ["wallet", "stronghold"]

[[example]]
name = "implicit_account_creation"
path = "examples/how_tos/account/implicit_account_creation.rs"
required-features = ["wallet"]

# Outputs

[[example]]
Expand Down
38 changes: 38 additions & 0 deletions sdk/examples/how_tos/account/implicit_account_creation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! In this example, we create an implicit account creation address.
//!
//! Rename `.env.example` to `.env` first, then run the command:
//! ```sh
//! cargo run --release --all-features --example implicit_account_creation
//! ```

use iota_sdk::{
client::{constants::SHIMMER_COIN_TYPE, secret::SecretManager},
crypto::keys::bip44::Bip44,
wallet::{ClientOptions, Result, Wallet},
};

#[tokio::main]
async fn main() -> Result<()> {
//  This example uses secrets in environment variables for simplicity which should not be done in production.
dotenvy::dotenv().ok();

let secret_manager = SecretManager::try_from_mnemonic(std::env::var("MNEMONIC").unwrap())?;
let client_options = ClientOptions::new().with_node("https://api.testnet.shimmer.network")?;

let wallet = Wallet::builder()
.with_secret_manager(secret_manager)
.with_client_options(client_options)
.with_storage_path("implicit_account_creation")
.with_bip_path(Bip44::new(SHIMMER_COIN_TYPE))
.finish()
.await?;

let implicit_account_creation_address = wallet.implicit_account_creation_address().await?;

println!("{implicit_account_creation_address}");

Ok(())
}
18 changes: 16 additions & 2 deletions sdk/src/wallet/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{
},
types::{
block::{
address::{Bech32Address, Hrp},
address::{Address, Bech32Address, Hrp, ImplicitAccountCreationAddress},
output::{dto::FoundryOutputDto, AccountId, FoundryId, FoundryOutput, NftId, Output, OutputId, TokenId},
payload::signed_transaction::{dto::TransactionDto, Transaction, TransactionId},
},
Expand All @@ -41,7 +41,7 @@ use crate::{
wallet::{
operations::syncing::SyncOptions,
types::{OutputData, OutputDataDto},
FilterOptions, Result,
Error, FilterOptions, Result,
},
};

Expand Down Expand Up @@ -252,6 +252,20 @@ where
self.data().await.address.clone()
}

/// Returns the implicit account creation address of the wallet if it is Ed25519 based.
pub async fn implicit_account_creation_address(&self) -> Result<Bech32Address> {
let bech32_address = &self.data().await.address;

if let Address::Ed25519(address) = bech32_address.inner() {
Ok(Bech32Address::new(
*bech32_address.hrp(),
ImplicitAccountCreationAddress::from(address.clone()),
))
} else {
return Err(Error::NonEd25519Address);
}
}

/// Get the wallet's configured Bech32 HRP.
pub async fn bech32_hrp(&self) -> Hrp {
self.data().await.address.hrp
Expand Down
3 changes: 3 additions & 0 deletions sdk/src/wallet/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ pub enum Error {
/// Address not the wallet address
#[error("address {0} is not the wallet address")]
WalletAddressMismatch(Bech32Address),
/// Action requires the wallet to be Ed25519 address based
#[error("tried to perform an action that requires the wallet to be Ed25519 address based")]
NonEd25519Address,
}

// Serialize type with Display error
Expand Down

0 comments on commit 4ced6c2

Please sign in to comment.