diff --git a/sway-lib-std/src/outputs.sw b/sway-lib-std/src/outputs.sw index a2b3641e1c3..b881eced12d 100644 --- a/sway-lib-std/src/outputs.sw +++ b/sway-lib-std/src/outputs.sw @@ -12,13 +12,14 @@ use ::tx::{ Transaction, tx_type, }; +use ::option::*; // GTF Opcode const selectors // pub const GTF_OUTPUT_TYPE = 0x201; -// pub const GTF_OUTPUT_COIN_TO = 0x202; +pub const GTF_OUTPUT_COIN_TO = 0x202; pub const GTF_OUTPUT_COIN_AMOUNT = 0x203; -// pub const GTF_OUTPUT_COIN_ASSET_ID = 0x204; +pub const GTF_OUTPUT_COIN_ASSET_ID = 0x204; // pub const GTF_OUTPUT_CONTRACT_INPUT_INDEX = 0x205; // pub const GTF_OUTPUT_CONTRACT_BALANCE_ROOT = 0x206; // pub const GTF_OUTPUT_CONTRACT_STATE_ROOT = 0x207; @@ -88,3 +89,22 @@ pub fn output_amount(index: u64) -> u64 { }, } } + +/// If the output's type is `Output::Coin` return the asset ID as an `Some(id)`. +/// Otherwise, returns `None`. +pub fn output_asset_id(index: u64) -> Option { + match output_type(index) { + Output::Coin => Option::Some(ContractId::from(__gtf::(index, GTF_OUTPUT_COIN_ASSET_ID))), + _ => Option::None, + } +} + +/// If the output's type is `Output::Coin` return the b256 as `Some(to)`. +/// Otherwise, returns `None`. +/// TODO: Update to `Identity` when https://github.com/FuelLabs/sway/issues/4569 is resolved +pub fn output_asset_to(index: u64) -> Option { + match output_type(index) { + Output::Coin => Option::Some(__gtf::(index, GTF_OUTPUT_COIN_TO)), + _ => Option::None, + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw index e0bcdf6303a..55bcb5df7e0 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw @@ -2,7 +2,7 @@ script; use basic_storage_abi::{BasicStorage, Quad}; fn main() -> u64 { - let addr = abi(BasicStorage, 0xe685bfdc8217d28b5710a8e441cf151e3eed6749923aae8b747a7f1f51e7469a); + let addr = abi(BasicStorage, 0x03ce81c0cfbe6287ecff739afeb92cdce9c6d0d3bcb066648b2239f502b86144); let key = 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; let value = 4242; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/src/main.sw index 3e64c0ca3c2..daca8c99d5d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/src/main.sw @@ -7,7 +7,7 @@ fn main() -> bool { let zero = b256::min(); let gas: u64 = u64::max(); let amount: u64 = 11; - let other_contract_id = ContractId::from(0x338000acdb5764cdaaf90a70b2fa73c68fed43ce2b7bdb329d361cb6b5393a43); + let other_contract_id = ContractId::from(0xbc40f25981f0778159e3dea633562c4b633ec2a37de91ca0f43005908d6df935); let base_asset_id = BASE_ASSET_ID; let test_contract = abi(ContextTesting, other_contract_id.into()); diff --git a/test/src/sdk-harness/Forc.lock b/test/src/sdk-harness/Forc.lock index c1db6ba98ac..9cba9f4be6b 100644 --- a/test/src/sdk-harness/Forc.lock +++ b/test/src/sdk-harness/Forc.lock @@ -331,6 +331,11 @@ name = 'tx_contract' source = 'member' dependencies = ['std'] +[[package]] +name = 'tx_output_predicate' +source = 'member' +dependencies = ['std'] + [[package]] name = 'tx_predicate' source = 'member' diff --git a/test/src/sdk-harness/Forc.toml b/test/src/sdk-harness/Forc.toml index f9ba79d19a3..5ca74f3ca6b 100644 --- a/test/src/sdk-harness/Forc.toml +++ b/test/src/sdk-harness/Forc.toml @@ -62,4 +62,5 @@ members = [ "test_artifacts/storage_vec/svec_u16", "test_artifacts/storage_vec/svec_u64", "test_artifacts/tx_contract", + "test_artifacts/tx_output_predicate", ] diff --git a/test/src/sdk-harness/test_artifacts/tx_output_predicate/Forc.lock b/test/src/sdk-harness/test_artifacts/tx_output_predicate/Forc.lock new file mode 100644 index 00000000000..bfc8a86aead --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_output_predicate/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = 'core' +source = 'path+from-root-3E8AFAF9D04B1844' + +[[package]] +name = 'std' +source = 'path+from-root-3E8AFAF9D04B1844' +dependencies = ['core'] + +[[package]] +name = 'tx_output_predicate' +source = 'member' +dependencies = ['std'] diff --git a/test/src/sdk-harness/test_artifacts/tx_output_predicate/Forc.toml b/test/src/sdk-harness/test_artifacts/tx_output_predicate/Forc.toml new file mode 100644 index 00000000000..2b0fbf1a0e7 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_output_predicate/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "tx_output_predicate" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } 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 new file mode 100644 index 00000000000..fccbdc61dc3 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_output_predicate/src/main.sw @@ -0,0 +1,14 @@ +predicate; + +use std::outputs::{output_asset_id, output_asset_to}; + +fn main(index: u64, asset_id: ContractId, to: b256) -> bool { + + let tx_asset_id = output_asset_id(index); + let tx_to = output_asset_to(index); + + assert(tx_asset_id.is_some() && tx_asset_id.unwrap() == asset_id); + assert(tx_to.is_some() && tx_to.unwrap() == to); + + 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 3a8ab93a278..95aa29b6f2b 100644 --- a/test/src/sdk-harness/test_projects/tx_fields/mod.rs +++ b/test/src/sdk-harness/test_projects/tx_fields/mod.rs @@ -5,6 +5,7 @@ use fuel_vm::fuel_tx::{ use fuels::{ accounts::{predicate::Predicate, wallet::WalletUnlocked, Account}, prelude::*, + types::Bits256, }; use std::str::FromStr; @@ -18,6 +19,10 @@ abigen!( Predicate( name = "TestPredicate", abi = "test_projects/tx_fields/out/debug/tx_predicate-abi.json" + ), + Predicate( + name = "TestOutputPredicate", + abi = "test_artifacts/tx_output_predicate/out/debug/tx_output_predicate-abi.json" ) ); @@ -142,6 +147,55 @@ async fn add_message_input(tx: &mut ScriptTransaction, wallet: WalletUnlocked) { tx.tx.inputs_mut().push(message_input); } +async fn setup_output_predicate() -> (WalletUnlocked, WalletUnlocked, Predicate, AssetId, AssetId) { + let asset_id1 = AssetId::default(); + let asset_id2 = AssetId::new([2u8; 32]); + let wallets_config = WalletsConfig::new_multiple_assets( + 2, + vec![ + AssetConfig { + id: asset_id1, + num_coins: 1, + coin_amount: 1_000, + }, + AssetConfig { + id: asset_id2, + num_coins: 1, + coin_amount: 1_000, + }, + ], + ); + + let mut wallets = launch_custom_provider_and_get_wallets(wallets_config, None, None).await; + let wallet1 = wallets.pop().unwrap(); + let wallet2 = wallets.pop().unwrap(); + + let predicate_data = TestOutputPredicateEncoder::encode_data( + 0, + ContractId::zeroed(), + Bits256(*wallet1.address().hash()), + ); + + let predicate = Predicate::load_from( + "test_artifacts/tx_output_predicate/out/debug/tx_output_predicate.bin", + ) + .unwrap() + .with_data(predicate_data) + .with_provider(wallet1.try_provider().unwrap().clone()); + + wallet1 + .transfer(predicate.address(), 100, asset_id1, TxParameters::default()) + .await + .unwrap(); + + wallet1 + .transfer(predicate.address(), 100, asset_id2, TxParameters::default()) + .await + .unwrap(); + + (wallet1, wallet2, predicate, asset_id1, asset_id2) +} + mod tx { use super::*; @@ -726,6 +780,28 @@ mod outputs { .unwrap(); assert_eq!(result.value, Output::Contract); } + + #[tokio::test] + async fn can_get_tx_output_details() { + let (wallet, _, predicate, asset_id, _) = setup_output_predicate().await; + + let balance = predicate.get_asset_balance(&asset_id).await.unwrap(); + + let transfer_amount = 10; + predicate + .transfer( + wallet.address(), + transfer_amount, + asset_id, + TxParameters::default(), + ) + .await + .unwrap(); + + let new_balance = predicate.get_asset_balance(&asset_id).await.unwrap(); + + assert!(balance - transfer_amount == new_balance); + } } mod revert { @@ -746,5 +822,39 @@ mod outputs { .unwrap(); } } + + #[tokio::test] + #[should_panic] + async fn fails_output_predicate_when_incorrect_asset() { + let (wallet1, _, predicate, _, asset_id2) = setup_output_predicate().await; + + let transfer_amount = 10; + predicate + .transfer( + wallet1.address(), + transfer_amount, + asset_id2, + TxParameters::default(), + ) + .await + .unwrap(); + } + + #[tokio::test] + #[should_panic] + async fn fails_output_predicate_when_incorrect_to() { + let (_, wallet2, predicate, asset_id1, _) = setup_output_predicate().await; + + let transfer_amount = 10; + predicate + .transfer( + wallet2.address(), + transfer_amount, + asset_id1, + TxParameters::default(), + ) + .await + .unwrap(); + } } }