Skip to content

Commit

Permalink
Update output_asset_to() and output_asset_id() to handle `Output:…
Browse files Browse the repository at this point in the history
…:Variable` (#6781)

## Description

Allows for fetching of the `AssetId` and the to `Address` from an
`Output::Variable`.

## 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).
- [x] 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.

---------

Co-authored-by: K1-R1 <[email protected]>
Co-authored-by: IGI-111 <[email protected]>
  • Loading branch information
3 people authored Dec 12, 2024
1 parent f7e4fb4 commit 192f163
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 51 deletions.
11 changes: 11 additions & 0 deletions sway-lib-std/src/outputs.sw
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ pub const GTF_OUTPUT_COIN_ASSET_ID = 0x303;
// pub const GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID = 0x307;
// pub const GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT = 0x308;

const OUTPUT_VARIABLE_ASSET_ID_OFFSET = 48;
const OUTPUT_VARIABLE_TO_OFFSET = 8;

/// The output type for a transaction.
pub enum Output {
/// A coin output.
Expand Down Expand Up @@ -228,6 +231,10 @@ 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))),
Some(Output::Variable) => {
let ptr = output_pointer(index).unwrap();
Some(AssetId::from(ptr.add_uint_offset(OUTPUT_VARIABLE_ASSET_ID_OFFSET).read::<b256>()))
},
_ => None,
}
}
Expand Down Expand Up @@ -260,6 +267,10 @@ 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)),
Some(Output::Variable) => {
let ptr = output_pointer(index).unwrap();
Some(Address::from(ptr.add_uint_offset(OUTPUT_VARIABLE_TO_OFFSET).read::<b256>()))
},
_ => None,
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/src/sdk-harness/Forc.lock
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ source = "member"
dependencies = ["std"]

[[package]]
name = "tx_output_change_contract"
name = "tx_output_contract"
source = "member"
dependencies = ["std"]

Expand Down
2 changes: 1 addition & 1 deletion test/src/sdk-harness/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,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",
"test_artifacts/tx_output_contract_creation_predicate",
"test_artifacts/tx_output_count_predicate",
"test_artifacts/tx_output_predicate",
Expand Down
3 changes: 2 additions & 1 deletion test/src/sdk-harness/test_artifacts/tx_contract/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ impl TxContractTest for Contract {

let mut iter = 0;
while iter < expected_data_bytes.len() {
if data.get(iter).unwrap() != expected_data_bytes.get(iter).unwrap() {
if data.get(iter).unwrap() != expected_data_bytes.get(iter).unwrap()
{
return false
}
iter += 1;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "tx_output_change_contract"
name = "tx_output_contract"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
35 changes: 35 additions & 0 deletions test/src/sdk-harness/test_artifacts/tx_output_contract/src/main.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
contract;

use std::asset::transfer;
use std::outputs::*;

abi TxOutputContract {
fn send_assets_change(to: Address, asset: AssetId, amount: u64);
fn send_assets_variable(to: Address, asset: AssetId, index: u64) -> (Address, AssetId, u64);
}

impl TxOutputContract for Contract {
fn send_assets_change(to: Address, asset: AssetId, amount: u64) {
transfer(Identity::Address(to), asset, amount);
}

fn send_assets_variable(to: Address, asset: AssetId, index: u64) -> (Address, AssetId, u64) {
transfer(Identity::Address(to), asset, 1);

get_variable_tx_params(index)
}
}

fn get_variable_tx_params(index: u64) -> (Address, AssetId, u64) {
let tx_asset_id = output_asset_id(index);
let tx_to = output_asset_to(index);
let tx_amount = output_amount(index);

let tx_output_type = output_type(index);
assert(tx_output_type.is_some() && tx_output_type.unwrap() == Output::Variable);
(
tx_to.unwrap_or(Address::zero()),
tx_asset_id.unwrap_or(AssetId::zero()),
tx_amount.unwrap_or(0),
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
predicate;

use std::outputs::{output_asset_id, output_asset_to, output_type, Output};
use std::outputs::{Output, output_asset_id, output_asset_to, output_type};

fn main(index: u64, asset_id: b256, to: b256, expected_type: Output) -> bool {
let tx_asset_id = output_asset_id(index);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
predicate;

use std::tx::{tx_type, Transaction};
use std::tx::{Transaction, tx_type};

fn main(expected_type: Transaction) -> bool {
tx_type() == expected_type
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
predicate;

use std::tx::{tx_witnesses_count, tx_witness_data_length, tx_witness_data};
use std::tx::{tx_witness_data, tx_witness_data_length, tx_witnesses_count};

fn main(index: u64, expected_count: u64, expected_length: u64, expected_data: [u8; 64]) -> bool {
fn main(
index: u64,
expected_count: u64,
expected_length: u64,
expected_data: [u8; 64],
) -> bool {
let count: u64 = tx_witnesses_count();
let length: Option<u64> = tx_witness_data_length(index);
let data: Option<[u8; 64]> = tx_witness_data(index);
Expand All @@ -11,7 +16,7 @@ fn main(index: u64, expected_count: u64, expected_length: u64, expected_data: [u
assert(length.is_some() && length.unwrap() == expected_length);

assert(data.is_some());
let data = data.unwrap();
let data = data.unwrap();
let mut iter = 0;
while iter < 64 {
assert(data[iter] == expected_data[iter]);
Expand Down
115 changes: 86 additions & 29 deletions test/src/sdk-harness/test_projects/tx_fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +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_OUTPUT_CONTRACT_BYTECODE_PATH: &str =
"test_artifacts/tx_output_contract/out/release/tx_output_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 @@ -28,17 +28,17 @@ const TX_INPUT_COUNT_PREDICATE_BYTECODE_PATH: &str =
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;
use crate::tx_fields::Transaction as SwayTransaction;

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",
name = "TxOutputContract",
abi = "test_artifacts/tx_output_contract/out/release/tx_output_contract-abi.json",
),
Predicate(
name = "TestPredicate",
Expand Down Expand Up @@ -172,7 +172,10 @@ async fn generate_predicate_inputs(
(predicate_code, predicate_input, predicate_message)
}

async fn setup_output_predicate(index: u64, expected_output_type: SwayOutput) -> (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 @@ -201,7 +204,12 @@ async fn setup_output_predicate(index: u64, expected_output_type: SwayOutput) ->
let wallet2 = wallets.pop().unwrap();

let predicate_data = TestOutputPredicateEncoder::default()
.encode_data(index, Bits256([0u8; 32]), Bits256(*wallet1.address().hash()), expected_output_type)
.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 @@ -1547,7 +1555,8 @@ mod outputs {

#[tokio::test]
async fn can_get_tx_output_details() {
let (wallet, _, predicate, asset_id, _) = setup_output_predicate(0, SwayOutput::Coin).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 @@ -1678,32 +1687,39 @@ 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 (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());
let contract_id = Contract::load_from(
TX_OUTPUT_CONTRACT_BYTECODE_PATH,
LoadConfiguration::default(),
)
.unwrap()
.deploy(&wallet, TxPolicies::default())
.await
.unwrap();

let instance = TxOutputContract::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();
.await
.unwrap();

// Build transaction
let call_handler = instance.methods().send_assets(wallet.clone().address(), asset_id, 10);
let call_handler =
instance
.methods()
.send_assets_change(wallet.clone().address(), asset_id, 10);
let mut tb = call_handler.transaction_builder().await.unwrap();

// Inputs for predicate
Expand All @@ -1714,27 +1730,66 @@ mod outputs {
.unwrap();

// Outputs for predicate
let predicate_output = wallet.get_asset_outputs_for_amount(
&wallet.address(),
asset_id,
transfer_amount,
);
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});
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);
}

#[tokio::test]
async fn can_get_tx_output_variable_details() {
// Prepare wallet
let (wallet, _, _, asset_id, _) = setup_output_predicate(1, SwayOutput::Variable).await;

// Deploy contract
let contract_id = Contract::load_from(
TX_OUTPUT_CONTRACT_BYTECODE_PATH,
LoadConfiguration::default(),
)
.unwrap()
.deploy(&wallet, TxPolicies::default())
.await
.unwrap();

let instance = TxOutputContract::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();

// Run transaction with variable output
let (tx_to, tx_asset_id, tx_amount) = instance
.methods()
.send_assets_variable(wallet.clone().address(), asset_id, 2)
.with_variable_output_policy(VariableOutputPolicy::Exactly(1))
.call()
.await
.unwrap()
.value;

assert_eq!(tx_to, wallet.clone().address().into());
assert_eq!(tx_asset_id, asset_id);
assert_eq!(tx_amount, 1);
}
}

mod revert {
Expand All @@ -1743,7 +1798,8 @@ mod outputs {
#[tokio::test]
#[should_panic]
async fn fails_output_predicate_when_incorrect_asset() {
let (wallet1, _, predicate, _, asset_id2) = setup_output_predicate(0, SwayOutput::Coin).await;
let (wallet1, _, predicate, _, asset_id2) =
setup_output_predicate(0, SwayOutput::Coin).await;

let transfer_amount = 10;
predicate
Expand All @@ -1760,7 +1816,8 @@ mod outputs {
#[tokio::test]
#[should_panic]
async fn fails_output_predicate_when_incorrect_to() {
let (_, wallet2, predicate, asset_id1, _) = setup_output_predicate(0, SwayOutput::Coin).await;
let (_, wallet2, predicate, asset_id1, _) =
setup_output_predicate(0, SwayOutput::Coin).await;

let transfer_amount = 10;
predicate
Expand Down

0 comments on commit 192f163

Please sign in to comment.