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

Update output_asset_to() and output_asset_id() to handle Output::Variable #6781

Merged
merged 7 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
8 changes: 8 additions & 0 deletions sway-lib-std/src/outputs.sw
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,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(48).read::<b256>()))
bitzoic marked this conversation as resolved.
Show resolved Hide resolved
},
_ => None,
}
}
Expand Down Expand Up @@ -260,6 +264,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(8).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
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" }
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
Loading