Skip to content

Commit

Permalink
Support Output::Change in output_asset_to() and `output_asset_id(…
Browse files Browse the repository at this point in the history
…)` (#6562)

## Description

Support to get the `to` and `asset_id` fields of an `Output::Change`
were added in FuelLabs/fuel-vm#675. The std-lib
now enables this.

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
bitzoic authored Sep 24, 2024
1 parent a98d908 commit 3e0acc1
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 11 deletions.
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 <[email protected]>"]
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

0 comments on commit 3e0acc1

Please sign in to comment.