diff --git a/bindings/go/examples/sign_send_iota/main.go b/bindings/go/examples/sign_send_iota/main.go new file mode 100644 index 000000000..7a29e090e --- /dev/null +++ b/bindings/go/examples/sign_send_iota/main.go @@ -0,0 +1,68 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "log" + + sdk "bindings/iota_sdk_ffi" +) + +func main() { + // Amount to send in nanos + amount := uint64(1000) + recipientAddress, err := sdk.AddressFromHex("0x0000a4984bd495d4346fa208ddff4f5d5e5ad48c21dec631ddebc99809f16900") + if err != nil { + log.Fatalf("Failed to parse recipient address: %v", err) + } + + privateKey, err := sdk.NewEd25519PrivateKey(make([]byte, 32)) + if err != nil { + log.Fatalf("Failed to create private key: %v", err) + } + publicKey := privateKey.PublicKey() + senderAddress := publicKey.DeriveAddress() + log.Printf("Sender address: %s", senderAddress.ToHex()) + + // Request funds from faucet + faucet := sdk.FaucetClientNewLocalnet() + _, err = faucet.RequestAndWait(senderAddress) + if err.(*sdk.SdkFfiError) != nil { + log.Fatalf("Failed to request faucet: %v", err) + } + + client := sdk.GraphQlClientNewLocalnet() + + builder := sdk.TransactionBuilderInit(senderAddress, client) + builder.SendIota(recipientAddress, &amount) + txn, err := builder.Finish() + if err.(*sdk.SdkFfiError) != nil { + log.Fatalf("Failed to create transaction: %v", err) + } + + dryRunResult, err := client.DryRunTx(txn, nil) + if err.(*sdk.SdkFfiError) != nil { + log.Fatalf("Failed to dry run: %v", err) + } + if dryRunResult.Error != nil { + log.Fatalf("Dry run failed: %v", *dryRunResult.Error) + } + + signature, err := privateKey.TrySignSimple(txn.SigningDigest()) + if err != nil { + log.Fatalf("Failed to sign: %v", err) + } + userSignature := sdk.UserSignatureNewSimple(signature) + + effects, err := client.ExecuteTx([]*sdk.UserSignature{userSignature}, txn) + if err.(*sdk.SdkFfiError) != nil { + log.Fatalf("Failed to execute: %v", err) + } + if effects == nil { + log.Fatalf("Transaction execution failed") + } + log.Printf("Digest: %s", sdk.HexEncode((*effects).Digest().ToBytes())) + log.Printf("Transaction status: %v", (*effects).AsV1().Status) + log.Printf("Effects: %+v", (*effects).AsV1()) +} diff --git a/bindings/kotlin/examples/SignSendIota.kt b/bindings/kotlin/examples/SignSendIota.kt new file mode 100644 index 000000000..9fcec3b29 --- /dev/null +++ b/bindings/kotlin/examples/SignSendIota.kt @@ -0,0 +1,49 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import iota_sdk.* +import kotlinx.coroutines.runBlocking + +fun main() = runBlocking { + try { + // Amount to send in nanos + val amount = 1000uL + val recipientAddress = + Address.fromHex( + "0x0000a4984bd495d4346fa208ddff4f5d5e5ad48c21dec631ddebc99809f16900" + ) + + val privateKey = Ed25519PrivateKey(ByteArray(32)) + val publicKey = privateKey.publicKey() + val senderAddress = publicKey.deriveAddress() + println("Sender address: ${senderAddress.toHex()}") + + // Request funds from faucet + val faucet = FaucetClient.newLocalnet() + faucet.requestAndWait(senderAddress) + + val client = GraphQlClient.newLocalnet() + + val builder = TransactionBuilder.init(senderAddress, client) + builder.sendIota(recipientAddress, amount) + val txn = builder.finish() + + val dryRunResult = client.dryRunTx(txn, false) + if (dryRunResult.error != null) { + throw Exception("Dry run failed: ${dryRunResult.error}") + } + + val signature = privateKey.trySignSimple(txn.signingDigest()) + val userSignature = UserSignature.newSimple(signature) + + val effects = client.executeTx(listOf(userSignature), txn) + if (effects == null) { + throw Exception("Transaction execution failed") + } + println("Digest: ${hexEncode(effects.digest().toBytes())}") + println("Transaction status: ${effects.asV1().status}") + println("Effects: ${effects.asV1()}") + } catch (e: Exception) { + e.printStackTrace() + } +} diff --git a/bindings/python/examples/sign_send_iota.py b/bindings/python/examples/sign_send_iota.py new file mode 100644 index 000000000..b797e0329 --- /dev/null +++ b/bindings/python/examples/sign_send_iota.py @@ -0,0 +1,51 @@ +# Copyright (c) 2025 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from lib.iota_sdk_ffi import * + +import asyncio + + +async def main(): + try: + # Amount to send in nanos + amount = 1000 + recipient_address = Address.from_hex( + "0x0000a4984bd495d4346fa208ddff4f5d5e5ad48c21dec631ddebc99809f16900" + ) + + private_key = Ed25519PrivateKey(b'\x00' * 32) + public_key = private_key.public_key() + sender_address = public_key.derive_address() + print(f"Sender address: {sender_address.to_hex()}") + + # Request funds from faucet + faucet = FaucetClient.new_localnet() + await faucet.request_and_wait(sender_address) + + client = GraphQlClient.new_localnet() + + builder = await TransactionBuilder.init(sender_address, client) + builder.send_iota(recipient_address, [amount]) + txn = await builder.finish() + + dry_run_result = await client.dry_run_tx(txn, False) + if dry_run_result.error is not None: + raise Exception(f"Dry run failed: {dry_run_result.error}") + + signature = private_key.try_sign_simple(txn.signing_digest()) + user_signature = UserSignature.new_simple(signature) + + effects = await client.execute_tx([user_signature], txn) + if effects is None: + raise Exception("Transaction execution failed") + print(f"Digest: {hex_encode(effects.digest().to_bytes())}") + print(f"Transaction status: {effects.as_v1().status}") + print(f"Effects: {effects.as_v1()}") + + except Exception as e: + print(f"Error: {e}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/crates/iota-graphql-client/examples/sign_send_iota.rs b/crates/iota-graphql-client/examples/sign_send_iota.rs new file mode 100644 index 000000000..b12368997 --- /dev/null +++ b/crates/iota-graphql-client/examples/sign_send_iota.rs @@ -0,0 +1,50 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use eyre::Result; +use iota_crypto::{IotaSigner, ed25519::Ed25519PrivateKey}; +use iota_graphql_client::{Client, faucet::FaucetClient}; +use iota_transaction_builder::TransactionBuilder; +use iota_types::Address; + +#[tokio::main] +async fn main() -> Result<()> { + // Amount to send in nanos + let amount = 1_000u64; + let recipient_address = + Address::from_hex("0x0000a4984bd495d4346fa208ddff4f5d5e5ad48c21dec631ddebc99809f16900")?; + + let private_key = Ed25519PrivateKey::new([0; Ed25519PrivateKey::LENGTH]); + let public_key = private_key.public_key(); + let sender_address = public_key.derive_address(); + println!("Sender address: {sender_address}"); + + // Request funds from faucet + FaucetClient::new_localnet() + .request_and_wait(sender_address) + .await?; + + let client = Client::new_localnet(); + + let mut builder = TransactionBuilder::new(sender_address).with_client(client.clone()); + builder.send_iota(recipient_address, amount); + let tx = builder.finish().await?; + + let dry_run_result = client.dry_run_tx(&tx, false).await?; + if let Some(err) = dry_run_result.error { + eyre::bail!("Dry run failed: {err}"); + } + + let signature = private_key.sign_transaction(&tx)?; + + let effects = client.execute_tx(&[signature], &tx).await?; + if let Some(effects) = effects { + println!("Digest: {}", effects.digest()); + println!("Transaction status: {:?}", effects.status()); + println!("Effects: {effects:#?}"); + } else { + println!("Transaction execution failed"); + } + + Ok(()) +}