Skip to main content

Create an Address from a Mnemonic

This guide explains how to derive a cryptographic key pair and an IOTA address from a BIP-39 mnemonic phrase using the IOTA SDK. The SDK supports three signature schemes Ed25519, Secp256k1, and Secp256r1 across Rust, Go, Python, Kotlin, and Swift.

Security Warning

The mnemonic phrase used in these examples is for demonstration only. Never share or hard-code a real mnemonic in production code. Store mnemonics securely and never commit them to version control.


Prerequisites

Before running these examples, make sure you have:

  • The IOTA SDK installed for your target language see Installation
  • A valid BIP-39 mnemonic phrase (12 or 24 words)
  • Basic familiarity with your chosen language's build tooling

Key Concepts

ConceptDescription
MnemonicA human-readable BIP-39 seed phrase (12–24 words) used as the root secret for key derivation.
Private KeyA secret scalar derived from the mnemonic. Encoded in Bech32 format for storage and display.
Public KeyThe public counterpart of the private key, used to generate the IOTA address and verify signatures.
Flagged Public KeyThe public key bytes prefixed with a scheme flag byte (Base64-encoded), used internally by the IOTA protocol.
AddressA 32-byte IOTA address derived from the public key, displayed as a 0x-prefixed hex string.
Derivation PathA BIP-44/SLIP-10 path (e.g. m/74'/4218'/0'/0/2) specifying which key to derive from the seed.

Supported Signature Schemes

The IOTA SDK supports three cryptographic schemes. Each has different derivation conventions and use cases:

SchemeDefault PathPassword SupportNotes
Ed25519m/44'/4218'/0'/0'/0'OptionalDefault scheme. Fast and widely supported.
Secp256k1Derived from account indexOptionalBitcoin-compatible curve. Accepts an optional passphrase.
Secp256r1Custom (e.g. m/74'/4218'/0'/0/2)OptionalNIST P-256 curve. Uses fromMnemonicWithPath for full path control.

Code Examples

The following examples all use the same mnemonic and produce the same addresses for each signature scheme, regardless of language.

// Copyright (c) 2025 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use base64ct::{Base64, Encoding};
use iota_sdk::{
crypto::{
DERIVATION_PATH_COIN_TYPE, DERIVATION_PATH_PURPOSE_SECP256R1, FromMnemonic, ToFromBech32,
ed25519::Ed25519PrivateKey, secp256k1::Secp256k1PrivateKey, secp256r1::Secp256r1PrivateKey,
},
types::PublicKeyExt,
};

const MNEMONIC: &str = "round attack kitchen wink winter music trip tiny nephew hire orange what";

fn main() -> eyre::Result<()> {
// Ed25519 – default derivation path, no password
let private_key = Ed25519PrivateKey::from_mnemonic(MNEMONIC, None, None)?;
let private_key_bech32 = private_key.to_bech32()?;
let public_key = private_key.public_key();
let flagged_public_key = Base64::encode_string(&public_key.to_flagged_bytes());
let address = public_key.derive_address();

println!("Ed25519\n---");
println!("Private Key: {private_key_bech32}");
println!("Public Key: {public_key}");
println!("Public Key With Flag: {flagged_public_key}");
println!("Address: {address}");

// Secp256k1 – account index 1, with password
let private_key = Secp256k1PrivateKey::from_mnemonic(MNEMONIC, 1, "my_password".to_owned())?;
let private_key_bech32 = private_key.to_bech32()?;
let public_key = private_key.public_key();
let flagged_public_key = Base64::encode_string(&public_key.to_flagged_bytes());
let address = public_key.derive_address();

println!("\nSecp256k1\n---");
println!("Private Key: {private_key_bech32}");
println!("Public Key: {public_key}");
println!("Public Key With Flag: {flagged_public_key}");
println!("Address: {address}");

// Secp256r1 – custom derivation path
let private_key = Secp256r1PrivateKey::from_mnemonic_with_path(
MNEMONIC,
format!("m/{DERIVATION_PATH_PURPOSE_SECP256R1}'/{DERIVATION_PATH_COIN_TYPE}'/0'/0/2"),
None,
)?;
let private_key_bech32 = private_key.to_bech32()?;
let public_key = private_key.public_key();
let flagged_public_key = Base64::encode_string(&public_key.to_flagged_bytes());
let address = public_key.derive_address();

println!("\nSecp256r1\n---");
println!("Private Key: {private_key_bech32}");
println!("Public Key: {public_key}");
println!("Public Key With Flag: {flagged_public_key}");
println!("Address: {address}");

Ok(())
}

Expected Output

Running any of the above examples with the provided mnemonic should produce output in the following format:

Ed25519
---
Private Key: iotaprivkey1...
Public Key: <base64-encoded-public-key>
Public Key With Flag: <base64-encoded-flagged-public-key>
Address: 0x<64-character-hex-address>

Secp256k1
---
Private Key: iotaprivkey1...
Public Key: <base64-encoded-public-key>
Public Key With Flag: <base64-encoded-flagged-public-key>
Address: 0x<64-character-hex-address>

Secp256r1
---
Private Key: iotaprivkey1...
Public Key: <base64-encoded-public-key>
Public Key With Flag: <base64-encoded-flagged-public-key>
Address: 0x<64-character-hex-address>
tip

All three schemes derive different addresses from the same mnemonic. This is expected — each scheme uses a different cryptographic curve and derivation path.


Next Steps