diff --git a/sway-lib-std/src/outputs.sw b/sway-lib-std/src/outputs.sw index 34f0c55eb13..69ccac1b23b 100644 --- a/sway-lib-std/src/outputs.sw +++ b/sway-lib-std/src/outputs.sw @@ -200,7 +200,7 @@ pub fn output_amount(index: u64) -> Option { } } -/// Gets the AssetId of the output if it is a `Output::Coin`. +/// Gets the AssetId of the output. /// /// # Arguments /// @@ -208,7 +208,7 @@ pub fn output_amount(index: u64) -> Option { /// /// # Returns /// -/// * [Option] - The AssetId of the output if it is a `Output::Coin`. None otherwise. +/// * [Option] - The AssetId of the output. None otherwise. /// /// # Reverts /// @@ -227,11 +227,12 @@ pub fn output_amount(index: u64) -> Option { pub fn output_asset_id(index: u64) -> Option { match output_type(index) { Some(Output::Coin) => Some(AssetId::from(__gtf::(index, GTF_OUTPUT_COIN_ASSET_ID))), + Some(Output::Change) => Some(AssetId::from(__gtf::(index, GTF_OUTPUT_COIN_ASSET_ID))), _ => None, } } -/// Returns the receiver of the output if it is a `Output::Coin`. +/// Returns the receiver of the output. /// /// # Arguments /// @@ -239,7 +240,7 @@ pub fn output_asset_id(index: u64) -> Option { /// /// # Returns /// -/// * [Option
] - The receiver of the output if it is a `Output::Coin`. None otherwise. +/// * [Option
] - The receiver of the output. None otherwise. /// /// # Reverts /// @@ -258,6 +259,7 @@ pub fn output_asset_id(index: u64) -> Option { pub fn output_asset_to(index: u64) -> Option
{ match output_type(index) { Some(Output::Coin) => Some(__gtf::
(index, GTF_OUTPUT_COIN_TO)), + Some(Output::Change) => Some(__gtf::
(index, GTF_OUTPUT_COIN_TO)), _ => None, } } diff --git a/test/src/sdk-harness/Forc.lock b/test/src/sdk-harness/Forc.lock index 37d81fdd61b..b05cb399826 100644 --- a/test/src/sdk-harness/Forc.lock +++ b/test/src/sdk-harness/Forc.lock @@ -409,6 +409,11 @@ name = "tx_input_count_predicate" source = "member" dependencies = ["std"] +[[package]] +name = "tx_output_change_contract" +source = "member" +dependencies = ["std"] + [[package]] name = "tx_output_contract_creation_predicate" source = "member" diff --git a/test/src/sdk-harness/Forc.toml b/test/src/sdk-harness/Forc.toml index f2a1df25d47..97a7001d7da 100644 --- a/test/src/sdk-harness/Forc.toml +++ b/test/src/sdk-harness/Forc.toml @@ -76,6 +76,7 @@ members = [ "test_artifacts/storage_vec/svec_u64", "test_artifacts/tx_contract", "test_artifacts/tx_input_count_predicate", + "test_artifacts/tx_output_change_contract", "test_artifacts/tx_output_contract_creation_predicate", "test_artifacts/tx_output_count_predicate", "test_artifacts/tx_output_predicate", diff --git a/test/src/sdk-harness/test_artifacts/tx_output_change_contract/Forc.toml b/test/src/sdk-harness/test_artifacts/tx_output_change_contract/Forc.toml new file mode 100644 index 00000000000..5a053f78b77 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_output_change_contract/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "tx_output_change_contract" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } diff --git a/test/src/sdk-harness/test_artifacts/tx_output_change_contract/src/main.sw b/test/src/sdk-harness/test_artifacts/tx_output_change_contract/src/main.sw new file mode 100644 index 00000000000..32aeeb5e7c9 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_output_change_contract/src/main.sw @@ -0,0 +1,13 @@ +contract; + +use std::asset::transfer; + +abi TxOutputChangeContract { + fn send_assets(to: Address, asset: AssetId, amount: u64); +} + +impl TxOutputChangeContract for Contract { + fn send_assets(to: Address, asset: AssetId, amount: u64) { + transfer(Identity::Address(to), asset, amount); + } +} diff --git a/test/src/sdk-harness/test_artifacts/tx_output_predicate/src/main.sw b/test/src/sdk-harness/test_artifacts/tx_output_predicate/src/main.sw index c7ca99d0d19..e639bd11833 100644 --- a/test/src/sdk-harness/test_artifacts/tx_output_predicate/src/main.sw +++ b/test/src/sdk-harness/test_artifacts/tx_output_predicate/src/main.sw @@ -1,13 +1,15 @@ predicate; -use std::outputs::{output_asset_id, output_asset_to}; +use std::outputs::{output_asset_id, output_asset_to, output_type, Output}; -fn main(index: u64, asset_id: b256, to: b256) -> bool { +fn main(index: u64, asset_id: b256, to: b256, expected_type: Output) -> bool { let tx_asset_id = output_asset_id(index); let tx_to = output_asset_to(index); + let tx_output_type = output_type(index); assert(tx_asset_id.is_some() && tx_asset_id.unwrap().bits() == asset_id); assert(tx_to.is_some() && tx_to.unwrap().bits() == to); + assert(tx_output_type.is_some() && tx_output_type.unwrap() == expected_type); true } diff --git a/test/src/sdk-harness/test_projects/tx_fields/mod.rs b/test/src/sdk-harness/test_projects/tx_fields/mod.rs index b162010e398..0ac934999c7 100644 --- a/test/src/sdk-harness/test_projects/tx_fields/mod.rs +++ b/test/src/sdk-harness/test_projects/tx_fields/mod.rs @@ -14,6 +14,8 @@ const MESSAGE_DATA: [u8; 3] = [1u8, 2u8, 3u8]; const TX_CONTRACT_BYTECODE_PATH: &str = "test_artifacts/tx_contract/out/release/tx_contract.bin"; const TX_OUTPUT_PREDICATE_BYTECODE_PATH: &str = "test_artifacts/tx_output_predicate/out/release/tx_output_predicate.bin"; +const TX_OUTPUT_CHANGE_CONTRACT_BYTECODE_PATH: &str = + "test_artifacts/tx_output_change_contract/out/release/tx_output_change_contract.bin"; const TX_FIELDS_PREDICATE_BYTECODE_PATH: &str = "test_projects/tx_fields/out/release/tx_fields.bin"; const TX_CONTRACT_CREATION_PREDICATE_BYTECODE_PATH: &str = "test_artifacts/tx_output_contract_creation_predicate/out/release/tx_output_contract_creation_predicate.bin"; @@ -27,12 +29,17 @@ const TX_OUTPUT_COUNT_PREDICATE_BYTECODE_PATH: &str = "test_artifacts/tx_output_count_predicate/out/release/tx_output_count_predicate.bin"; use crate::tx_fields::Transaction as SwayTransaction; +use crate::tx_fields::Output as SwayOutput; abigen!( Contract( name = "TxContractTest", abi = "test_artifacts/tx_contract/out/release/tx_contract-abi.json", ), + Contract( + name = "TxOutputChangeContract", + abi = "test_artifacts/tx_output_change_contract/out/release/tx_output_change_contract-abi.json", + ), Predicate( name = "TestPredicate", abi = "test_projects/tx_fields/out/release/tx_fields-abi.json" @@ -165,7 +172,7 @@ async fn generate_predicate_inputs( (predicate_code, predicate_input, predicate_message) } -async fn setup_output_predicate() -> (WalletUnlocked, WalletUnlocked, Predicate, AssetId, AssetId) { +async fn setup_output_predicate(index: u64, expected_output_type: SwayOutput) -> (WalletUnlocked, WalletUnlocked, Predicate, AssetId, AssetId) { let asset_id1 = AssetId::default(); let asset_id2 = AssetId::new([2u8; 32]); let wallets_config = WalletsConfig::new_multiple_assets( @@ -194,7 +201,7 @@ async fn setup_output_predicate() -> (WalletUnlocked, WalletUnlocked, Predicate, let wallet2 = wallets.pop().unwrap(); let predicate_data = TestOutputPredicateEncoder::default() - .encode_data(0, Bits256([0u8; 32]), Bits256(*wallet1.address().hash())) + .encode_data(index, Bits256([0u8; 32]), Bits256(*wallet1.address().hash()), expected_output_type) .unwrap(); let predicate = Predicate::load_from(TX_OUTPUT_PREDICATE_BYTECODE_PATH) @@ -1540,7 +1547,7 @@ mod outputs { #[tokio::test] async fn can_get_tx_output_details() { - let (wallet, _, predicate, asset_id, _) = setup_output_predicate().await; + let (wallet, _, predicate, asset_id, _) = setup_output_predicate(0, SwayOutput::Coin).await; let balance = predicate.get_asset_balance(&asset_id).await.unwrap(); @@ -1671,6 +1678,63 @@ mod outputs { assert_eq!(predicate_balance, 0); } } + + #[tokio::test] + async fn can_get_tx_output_change_details() { + // Prepare predicate + let (wallet, _, predicate, asset_id, _) = setup_output_predicate(2, SwayOutput::Change).await; + let provider = wallet.try_provider().unwrap().clone(); + + let balance = predicate.get_asset_balance(&asset_id).await.unwrap(); + + // Deploy contract + let contract_id = Contract::load_from(TX_OUTPUT_CHANGE_CONTRACT_BYTECODE_PATH, LoadConfiguration::default()) + .unwrap() + .deploy(&wallet, TxPolicies::default()) + .await + .unwrap(); + + let instance = TxOutputChangeContract::new(contract_id.clone(), wallet.clone()); + + // Send tokens to the contract + let _ = wallet + .force_transfer_to_contract(&contract_id, 10, asset_id, TxPolicies::default()) + .await + .unwrap(); + + // Build transaction + let call_handler = instance.methods().send_assets(wallet.clone().address(), asset_id, 10); + let mut tb = call_handler.transaction_builder().await.unwrap(); + + // Inputs for predicate + let transfer_amount = 100; + let predicate_input = predicate + .get_asset_inputs_for_amount(asset_id, transfer_amount, None) + .await + .unwrap(); + + // Outputs for predicate + let predicate_output = wallet.get_asset_outputs_for_amount( + &wallet.address(), + asset_id, + transfer_amount, + ); + + // Append the inputs and outputs to the transaction + tb.inputs.push(predicate_input.get(0).unwrap().clone()); + tb.outputs.push(predicate_output.get(0).unwrap().clone()); + tb.outputs.push(SdkOutput::Change{to: wallet.address().into(), amount: 0, asset_id}); + + wallet.adjust_for_fee(&mut tb, 0).await.unwrap(); + tb.add_signer(wallet.clone()).unwrap(); + + let tx = tb.build(provider.clone()).await.unwrap(); + let _tx_id = provider.send_transaction(tx).await.unwrap(); + + // Assert the predicate balance has changed + let new_balance = predicate.get_asset_balance(&asset_id).await.unwrap(); + assert!(balance - transfer_amount == new_balance); + } } mod revert { @@ -1679,7 +1743,7 @@ mod outputs { #[tokio::test] #[should_panic] async fn fails_output_predicate_when_incorrect_asset() { - let (wallet1, _, predicate, _, asset_id2) = setup_output_predicate().await; + let (wallet1, _, predicate, _, asset_id2) = setup_output_predicate(0, SwayOutput::Coin).await; let transfer_amount = 10; predicate @@ -1696,7 +1760,7 @@ mod outputs { #[tokio::test] #[should_panic] async fn fails_output_predicate_when_incorrect_to() { - let (_, wallet2, predicate, asset_id1, _) = setup_output_predicate().await; + let (_, wallet2, predicate, asset_id1, _) = setup_output_predicate(0, SwayOutput::Coin).await; let transfer_amount = 10; predicate