Skip to content

Commit

Permalink
feat(iota-sdk): add transaction builder examples (#2040)
Browse files Browse the repository at this point in the history
* feat(iota-sdk): add transfer example

* feat(iota-sdk): add pay example

* feat(iota-sdk): add batch_tx example

* feat(iota-sdk): add single_move_call example

* feat(iota-sdk): add move_package example

* feat(iota-sdk): add object_ref example

* Remove numbering

* Apply suggestions from code review

Co-authored-by: Thibault Martinez <[email protected]>

* Remove gas_coin prints

* Build package from path

---------

Co-authored-by: Thibault Martinez <[email protected]>
  • Loading branch information
Thoralf-M and thibault-martinez authored Sep 4, 2024
1 parent 17fea43 commit 0c3d2ea
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/iota-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ tracing.workspace = true
[dev-dependencies]
async-recursion.workspace = true
dirs.workspace = true
iota-move-build.workspace = true
rand.workspace = true
tempfile.workspace = true

Expand Down
57 changes: 57 additions & 0 deletions crates/iota-sdk/examples/batch_tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! This example shows how to create a batch transaction.
//!
//! cargo run --example batch_tx

mod utils;

use iota_json_rpc_types::{RPCTransactionRequestParams, TransferObjectParams};
use utils::{setup_for_write, sign_and_execute_transaction};

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let (client, sender, recipient) = setup_for_write().await?;

// Get the coins we will transfer and use as gas
let coins = client
.coin_read_api()
.get_coins(sender, None, None, None)
.await?;
let gas_coin_object_id = coins.data[0].coin_object_id;
let coin_object_id_1 = coins.data[1].coin_object_id;
let coin_object_id_2 = coins.data[2].coin_object_id;

let gas_budget = 5_000_000;

// Build the transaction data, to transfer the coins to the recipient address
let tx_data = client
.transaction_builder()
.batch_transaction(
sender,
vec![
RPCTransactionRequestParams::TransferObjectRequestParams(TransferObjectParams {
recipient,
object_id: coin_object_id_1,
}),
RPCTransactionRequestParams::TransferObjectRequestParams(TransferObjectParams {
recipient,
object_id: coin_object_id_2,
}),
],
Some(gas_coin_object_id),
gas_budget,
)
.await?;

let transaction_response = sign_and_execute_transaction(&client, &sender, tx_data).await?;

println!("Transaction sent {}", transaction_response.digest);
println!("Object changes:");
for object_change in transaction_response.object_changes.unwrap() {
println!("{:?}", object_change);
}

Ok(())
}
106 changes: 106 additions & 0 deletions crates/iota-sdk/examples/move_package.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! This example shows how to publish and upgrade a move package.
//!
//! cargo run --example move_package

mod utils;

use iota_json_rpc_types::ObjectChange;
use iota_move_build::BuildConfig;
use iota_types::move_package::MovePackage;
use utils::{setup_for_write, sign_and_execute_transaction};

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let (client, sender, _) = setup_for_write().await?;

let coins = client
.coin_read_api()
.get_coins(sender, None, None, None)
.await?;
let gas_coin_object_id = coins.data[0].coin_object_id;

let gas_budget = 10_000_000;

let module = BuildConfig::default().build("../../examples/move/first_package".into())?;

let tx_data = client
.transaction_builder()
.publish(
sender,
module.get_package_bytes(false),
module.published_dependency_ids(),
Some(gas_coin_object_id),
gas_budget,
)
.await?;

let transaction_response = sign_and_execute_transaction(&client, &sender, tx_data).await?;

println!("Transaction sent {}", transaction_response.digest);
println!("Object changes:");
let object_changes = transaction_response.object_changes.unwrap();
for object_change in &object_changes {
println!("{:?}", object_change);
}

// Wait some time for the indexer to process the tx
tokio::time::sleep(std::time::Duration::from_secs(3)).await;

// Upgrade

let package_id = object_changes
.iter()
.find_map(|c| {
if let ObjectChange::Published { .. } = c {
Some(c.object_id())
} else {
None
}
})
.expect("missing published package");
let upgrade_capability = object_changes
.iter()
.find_map(|c| {
if let ObjectChange::Created { .. } = c {
Some(c.object_id())
} else {
None
}
})
.expect("missing upgrade cap");

// In reality you would like to do some changes to the package before upgrading
let module = BuildConfig::default().build("../../examples/move/first_package".into())?;
let deps = module.published_dependency_ids();
let package_bytes = module.get_package_bytes(false);

let package_digest =
MovePackage::compute_digest_for_modules_and_deps(&package_bytes, &deps, true);
let tx_data = client
.transaction_builder()
.upgrade(
sender,
package_id,
package_bytes,
deps,
upgrade_capability,
0,
package_digest.to_vec(),
Some(gas_coin_object_id),
gas_budget,
)
.await?;

let transaction_response = sign_and_execute_transaction(&client, &sender, tx_data).await?;

println!("Transaction sent {}", transaction_response.digest);
println!("Object changes:");
for object_change in transaction_response.object_changes.unwrap() {
println!("{:?}", object_change);
}

Ok(())
}
22 changes: 22 additions & 0 deletions crates/iota-sdk/examples/object_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! This example shows how to get an object ref from its object id.
//!
//! cargo run --example object_ref

use iota_sdk::IotaClientBuilder;

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let client = IotaClientBuilder::default().build_testnet().await?;

let object_ref = client
.transaction_builder()
.get_object_ref("0x8".parse()?)
.await?;

println!("{object_ref:?}");

Ok(())
}
50 changes: 50 additions & 0 deletions crates/iota-sdk/examples/pay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! This example shows how to pay coins to another address. This also works with
//! non-IOTA coins.
//!
//! cargo run --example pay

mod utils;

use utils::{setup_for_write, sign_and_execute_transaction};

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let (client, sender, recipient) = setup_for_write().await?;

// Get the coins we will use for the payment and gas
let coins = client
.coin_read_api()
.get_coins(sender, None, None, None)
.await?;
let coin_object_id = coins.data[0].coin_object_id;
let gas_coin_object_id = coins.data[1].coin_object_id;

let gas_budget = 5_000_000;

// Build the transaction data to transfer 1_000 from the provided coin to the
// recipient address
let tx_data = client
.transaction_builder()
.pay(
sender,
vec![coin_object_id],
vec![recipient],
vec![1_000],
Some(gas_coin_object_id),
gas_budget,
)
.await?;

let transaction_response = sign_and_execute_transaction(&client, &sender, tx_data).await?;

println!("Transaction sent {}", transaction_response.digest);
println!("Object changes:");
for object_change in transaction_response.object_changes.unwrap() {
println!("{:?}", object_change);
}

Ok(())
}
65 changes: 65 additions & 0 deletions crates/iota-sdk/examples/single_move_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! This example shows how to update a PTB with a single move call.
//!
//! cargo run --example single_move_call

mod utils;

use iota_json::IotaJsonValue;
use iota_types::{
programmable_transaction_builder::ProgrammableTransactionBuilder, transaction::TransactionData,
};
use serde_json::json;
use utils::{setup_for_write, sign_and_execute_transaction};

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let (client, sender, recipient) = setup_for_write().await?;

let coins = client
.coin_read_api()
.get_coins(sender, None, None, None)
.await?;
let coin = &coins.data[0];
let gas_coin = &coins.data[1];

let gas_budget = 5_000_000;
let gas_price = client.read_api().get_reference_gas_price().await?;

let mut ptb = ProgrammableTransactionBuilder::new();
client
.transaction_builder()
.single_move_call(
&mut ptb,
"0x2".parse()?,
"iota",
"transfer",
vec![],
vec![
IotaJsonValue::new(json!(coin.coin_object_id))?,
IotaJsonValue::new(json!(recipient))?,
],
)
.await?;
let pt = ptb.finish();

let tx_data = TransactionData::new_programmable(
sender,
vec![gas_coin.object_ref()],
pt,
gas_budget,
gas_price,
);

let transaction_response = sign_and_execute_transaction(&client, &sender, tx_data).await?;

println!("Transaction sent {}", transaction_response.digest);
println!("Object changes:");
for object_change in transaction_response.object_changes.unwrap() {
println!("{:?}", object_change);
}

Ok(())
}
68 changes: 68 additions & 0 deletions crates/iota-sdk/examples/transfer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! This example shows how to transfer IOTAs or an object.
//!
//! cargo run --example transfer

mod utils;

use utils::{setup_for_write, sign_and_execute_transaction};

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let (client, sender, recipient) = setup_for_write().await?;

// Get the coin we will use as gas and for the payment
let coins_page = client
.coin_read_api()
.get_coins(sender, None, None, None)
.await?;
let mut coins = coins_page.data.into_iter();
let gas_coin = coins.next().expect("missing gas coin");

let gas_budget = 5_000_000;

// Build the transaction data to transfer the gas coin to the recipient address
let tx_data = client
.transaction_builder()
.transfer_iota(sender, gas_coin.coin_object_id, gas_budget, recipient, None)
.await?;

println!("Executing the transaction...");
let transaction_response = sign_and_execute_transaction(&client, &sender, tx_data).await?;

println!("Transaction sent {}", transaction_response.digest);
println!("Object changes:");
for object_change in transaction_response.object_changes.unwrap() {
println!("{:?}", object_change);
}

// Very similar to above, but works with any object, not just with IOTAs

let object_to_transfer = coins.next().expect("missing coin");
let gas_coin = coins.next().expect("missing gas coin");

// Build the transaction data to transfer the object to the recipient address
let tx_data = client
.transaction_builder()
.transfer_object(
sender,
object_to_transfer.coin_object_id,
Some(gas_coin.coin_object_id),
gas_budget,
recipient,
)
.await?;

println!("Executing the transaction...");
let transaction_response = sign_and_execute_transaction(&client, &sender, tx_data).await?;

println!("Transaction sent {}", transaction_response.digest);
println!("Object changes:");
for object_change in transaction_response.object_changes.unwrap() {
println!("{:?}", object_change);
}

Ok(())
}

0 comments on commit 0c3d2ea

Please sign in to comment.