Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Output::Change in output_asset_to() and output_asset_id() #6562

Merged
merged 5 commits into from
Sep 24, 2024
Merged
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
10 changes: 6 additions & 4 deletions sway-lib-std/src/outputs.sw
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,15 @@ pub fn output_amount(index: u64) -> Option<u64> {
}
}

/// Gets the AssetId of the output if it is a `Output::Coin`.
/// Gets the AssetId of the output.
///
/// # Arguments
///
/// * `index`: [u64] - The index of the output to get the AssetId of.
///
/// # Returns
///
/// * [Option<AssetId>] - The AssetId of the output if it is a `Output::Coin`. None otherwise.
/// * [Option<AssetId>] - The AssetId of the output. None otherwise.
///
/// # Reverts
///
Expand All @@ -227,19 +227,20 @@ pub fn output_amount(index: u64) -> Option<u64> {
pub fn output_asset_id(index: u64) -> Option<AssetId> {
match output_type(index) {
Some(Output::Coin) => Some(AssetId::from(__gtf::<b256>(index, GTF_OUTPUT_COIN_ASSET_ID))),
Some(Output::Change) => Some(AssetId::from(__gtf::<b256>(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
///
/// * `index`: [u64] - The index of the output to get the receiver of.
///
/// # Returns
///
/// * [Option<Address>] - The receiver of the output if it is a `Output::Coin`. None otherwise.
/// * [Option<Address>] - The receiver of the output. None otherwise.
///
/// # Reverts
///
Expand All @@ -258,6 +259,7 @@ pub fn output_asset_id(index: u64) -> Option<AssetId> {
pub fn output_asset_to(index: u64) -> Option<Address> {
match output_type(index) {
Some(Output::Coin) => Some(__gtf::<Address>(index, GTF_OUTPUT_COIN_TO)),
Some(Output::Change) => Some(__gtf::<Address>(index, GTF_OUTPUT_COIN_TO)),
_ => None,
}
}
Expand Down
5 changes: 5 additions & 0 deletions test/src/sdk-harness/Forc.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions test/src/sdk-harness/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "tx_output_change_contract"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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
}
74 changes: 69 additions & 5 deletions test/src/sdk-harness/test_projects/tx_fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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"
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading