Skip to content
Open
Show file tree
Hide file tree
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
98 changes: 98 additions & 0 deletions bindings/go/examples/sign_send_iota/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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.FaucetClientNewLocal()
_, err = faucet.RequestAndWait(senderAddress)
if err.(*sdk.SdkFfiError) != nil {
log.Fatalf("Failed to request faucet: %v", err)
}

client := sdk.GraphQlClientNewLocalhost()
// Get coins for the sender address
coinsPage, err := client.Coins(senderAddress, nil, nil)
if err.(*sdk.SdkFfiError) != nil {
log.Fatalf("Failed to get coins: %v", err)
}
if len(coinsPage.Data) == 0 {
log.Fatalf("No coins found")
}
gasCoin := coinsPage.Data[0]

gasObject, err := client.Object(gasCoin.Id(), nil)
if err.(*sdk.SdkFfiError) != nil {
log.Fatalf("Failed to get gas object: %v", err)
}
if gasObject == nil {
log.Fatalf("Missing gas object")
}

builder := sdk.TransactionBuilderInit(senderAddress, client)

// Split the amount from the gas coin
builder.SplitCoins(gasCoin.Id(), []uint64{amount}, []string{"coin1"})

// Transfer the split coin
builder.TransferObjects(recipientAddress, []*sdk.PtbArgument{sdk.PtbArgumentRes("coin1")})

builder.Gas(gasCoin.Id()).GasBudget(50000000)
gasPrice, err := client.ReferenceGasPrice(nil)
if err.(*sdk.SdkFfiError) != nil {
log.Fatalf("Failed to get gas price: %v", err)
}
builder.GasPrice(*gasPrice)

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())
}
68 changes: 68 additions & 0 deletions bindings/kotlin/examples/SignSendIota.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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.newLocal()
faucet.requestAndWait(senderAddress)

val client = GraphQlClient.newLocalhost()
// Get coins for the sender address
val coinsPage = client.coins(senderAddress, null, null)
if (coinsPage.data.isEmpty()) {
throw Exception("No coins found")
}
val gasCoin = coinsPage.data[0]

val builder = TransactionBuilder.init(senderAddress, client)

// Split the amount from the gas coin
builder.splitCoins(gasCoin.id(), listOf(amount), listOf("coin1"))

// Transfer the split coin
builder.transferObjects(recipientAddress, listOf(PtbArgument.res("coin1")))

builder.gas(gasCoin.id()).gasBudget(50000000.toULong())
val gasPrice = client.referenceGasPrice(null)
if (gasPrice == null) {
throw Exception("Failed to get gas price")
}
builder.gasPrice(gasPrice)

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()
}
}
68 changes: 68 additions & 0 deletions bindings/python/examples/sign_send_iota.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 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_local()
await faucet.request_and_wait(sender_address)

client = GraphQlClient.new_localhost()
# Get coins for the sender address
coins_page = await client.coins(sender_address, None, None)
if not coins_page.data:
raise Exception("No coins found")
gas_coin = coins_page.data[0]

builder = await TransactionBuilder.init(sender_address, client)

# Split the amount from the gas coin
builder.split_coins(gas_coin.id(), [amount], ["coin1"])

# Transfer the split coin
builder.transfer_objects(recipient_address, [PtbArgument.res("coin1")])

builder.gas(gas_coin.id()).gas_budget(50000000)
gas_price = await client.reference_gas_price(None)
if gas_price is None:
raise Exception("Failed to get gas price")
builder.gas_price(gas_price)

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())
71 changes: 71 additions & 0 deletions crates/iota-graphql-client/examples/sign_send_iota.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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, res};
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::local()
.request_and_wait(sender_address)
.await?;

let client = Client::new_localhost();
// Get coins for the sender address
let coins_page = client
.coins(sender_address, None, Default::default())
.await?;
let gas_coin = coins_page.data.first().expect("no gas coin found");

let mut builder = TransactionBuilder::new(sender_address).with_client(client.clone());

// Split the amount from the gas coin
builder
.split_coins(*gas_coin.id(), vec![amount])
.name("coin1");

// Transfer the split coin
builder.transfer_objects(recipient_address, vec![res("coin1")]);

builder.gas(*gas_coin.id()).gas_budget(50_000_000);
builder.gas_price(
client
.reference_gas_price(None)
.await?
.expect("missing ref gas price"),
);

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(())
}
24 changes: 24 additions & 0 deletions crates/iota-transaction-builder/src/builder/ptb_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,30 @@ impl PTBArguments for Vec<iota_types::Input> {
}
}

impl PTBArguments for Vec<Res> {
fn push_args(&self, ptb: &mut TransactionBuildData, args: &mut Vec<Argument>) {
for input in self {
input.push_args(ptb, args);
}
}
}

impl<T: PTBArguments> PTBArguments for [T] {
fn push_args(&self, ptb: &mut TransactionBuildData, args: &mut Vec<Argument>) {
for input in self {
input.push_args(ptb, args);
}
}
}

impl<const N: usize, T: PTBArguments> PTBArguments for [T; N] {
fn push_args(&self, ptb: &mut TransactionBuildData, args: &mut Vec<Argument>) {
for input in self {
input.push_args(ptb, args);
}
}
}

/// Allows specifying shared parameters.
pub struct Shared<T>(pub T);

Expand Down
Loading