Skip to content

Commit

Permalink
Add Output::ContractCreated transaction to Output enum (#6194)
Browse files Browse the repository at this point in the history
## Description

The `Output` enum did not have the `ContractCreated` variant. This has
been added as well as a test to ensure this output type is returned when
a contact is deployed with a predicate in the inputs.

`Eq` for `Output` has also been added.

## 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.

---------

Co-authored-by: K1-R1 <[email protected]>
  • Loading branch information
bitzoic and K1-R1 authored Jul 12, 2024
1 parent f43342b commit b92c7a2
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 5 deletions.
18 changes: 18 additions & 0 deletions sway-lib-std/src/outputs.sw
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub enum Output {
Change: (),
/// A variable output.
Variable: (),
/// A contract deployment.
ContractCreated: (),
}

/// Get the type of an output at `index`.
Expand Down Expand Up @@ -66,6 +68,7 @@ pub enum Output {
/// Output::Contract => { log("The output is a contract") },
/// Output::Change => { log("The output is change") },
/// Output::Variable => { log("The output is a variable") },
/// Output::ContractCreated => { log("The output is a contract creation") },
/// };
/// }
/// ```
Expand All @@ -75,6 +78,7 @@ pub fn output_type(index: u64) -> Output {
1u8 => Output::Contract,
2u8 => Output::Change,
3u8 => Output::Variable,
4u8 => Output::ContractCreated,
_ => revert(0),
}
}
Expand Down Expand Up @@ -193,6 +197,7 @@ pub fn output_amount(index: u64) -> u64 {
r1: u64
}
},
Output::ContractCreated => revert(0),
}
}

Expand Down Expand Up @@ -257,3 +262,16 @@ pub fn output_asset_to(index: u64) -> Option<Address> {
_ => None,
}
}

impl core::ops::Eq for Output {
fn eq(self, other: Self) -> bool {
match (self, other) {
(Output::Coin, Output::Coin) => true,
(Output::Contract, Output::Contract) => true,
(Output::Change, Output::Change) => true,
(Output::Variable, Output::Variable) => true,
(Output::ContractCreated, Output::ContractCreated) => true,
_ => false,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use test_fuel_coin_abi::*;
#[cfg(experimental_new_encoding = false)]
const FUEL_COIN_CONTRACT_ID = 0xec2277ebe007ade87e3d797c3b1e070dcd542d5ef8f038b471f262ef9cebc87c;
#[cfg(experimental_new_encoding = true)]
const FUEL_COIN_CONTRACT_ID = 0x1a88d0982d216958d18378b6784614b75868a542dc05f8cc85cf3da44268c76c; // AUTO-CONTRACT-ID ../../test_contracts/test_fuel_coin_contract --release
const FUEL_COIN_CONTRACT_ID = 0xbac7800e48f9a98d99ded3bf123f0f25bf0170de6ef991a3035f76d4570c8b1e;

#[cfg(experimental_new_encoding = false)]
const BALANCE_CONTRACT_ID = 0xf6cd545152ac83225e8e7df2efb5c6fa6e37bc9b9e977b5ea8103d28668925df;
Expand Down
1 change: 1 addition & 0 deletions test/src/in_language_tests/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"test_programs/identity_inline_tests",
"test_programs/math_inline_tests",
"test_programs/option_inline_tests",
"test_programs/output_inline_tests",
"test_programs/result_inline_tests",
"test_programs/revert_inline_tests",
"test_programs/string_inline_tests",
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 = "output_inline_tests"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
library;

use std::outputs::Output;

#[test]
fn output_output_eq() {
let output_1 = Output::Coin;
let output_2 = Output::Coin;
let output_3 = Output::Contract;
let output_4 = Output::Contract;
let output_5 = Output::Change;
let output_6 = Output::Change;
let output_7 = Output::Variable;
let output_8 = Output::Variable;
let output_9 = Output::ContractCreated;
let output_10 = Output::ContractCreated;

assert(output_1 == output_2);
assert(output_3 == output_4);
assert(output_5 == output_6);
assert(output_7 == output_8);
assert(output_9 == output_10);
}

#[test]
fn output_output_neq() {
let output_1 = Output::Coin;
let output_2 = Output::Contract;
let output_3 = Output::Change;
let output_4 = Output::Variable;
let output_5 = Output::ContractCreated;

assert(output_1 != output_2);
assert(output_1 != output_3);
assert(output_1 != output_4);
assert(output_1 != output_5);

assert(output_2 != output_3);
assert(output_2 != output_4);
assert(output_2 != output_5);

assert(output_3 != output_4);
assert(output_3 != output_5);

assert(output_4 != output_5);
}
5 changes: 5 additions & 0 deletions test/src/sdk-harness/Forc.lock
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,11 @@ name = "tx_fields"
source = "member"
dependencies = ["std"]

[[package]]
name = "tx_output_contract_creation_predicate"
source = "member"
dependencies = ["std"]

[[package]]
name = "tx_output_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,4 +76,5 @@ members = [
"test_artifacts/storage_vec/svec_u64",
"test_artifacts/tx_contract",
"test_artifacts/tx_output_predicate",
"test_artifacts/tx_output_contract_creation_predicate",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[package]]
name = "core"
source = "path+from-root-1EABD6DBC885D01E"

[[package]]
name = "std"
source = "path+from-root-1EABD6DBC885D01E"
dependencies = ["core"]

[[package]]
name = "tx_output_contract_creation_predicate"
source = "member"
dependencies = ["std"]
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_contract_creation_predicate"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
predicate;

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

fn main() -> bool {
output_type(2) == Output::ContractCreated
}
113 changes: 109 additions & 4 deletions test/src/sdk-harness/test_projects/tx_fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ use fuels::types::transaction_builders::TransactionBuilder;
use fuels::{
accounts::{predicate::Predicate, wallet::WalletUnlocked, Account},
prelude::*,
types::{input::Input as SdkInput, Bits256},
types::{input::Input as SdkInput, Bits256, output::Output as SdkOutput},
tx::StorageSlot,
};
use std::fs;

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_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";

abigen!(
Contract(
Expand Down Expand Up @@ -69,7 +79,7 @@ async fn get_contracts(
deployment_wallet.set_provider(provider);

let contract_id = Contract::load_from(
"test_artifacts/tx_contract/out/release/tx_contract.bin",
TX_CONTRACT_BYTECODE_PATH,
LoadConfiguration::default(),
)
.unwrap()
Expand All @@ -87,7 +97,7 @@ async fn generate_predicate_inputs(
wallet: &WalletUnlocked,
) -> (Vec<u8>, SdkInput, TxInput) {
let provider = wallet.provider().unwrap();
let predicate = Predicate::load_from("test_projects/tx_fields/out/release/tx_fields.bin")
let predicate = Predicate::load_from(TX_FIELDS_PREDICATE_BYTECODE_PATH)
.unwrap()
.with_provider(provider.clone());

Expand Down Expand Up @@ -163,7 +173,7 @@ async fn setup_output_predicate() -> (WalletUnlocked, WalletUnlocked, Predicate,
.unwrap();

let predicate = Predicate::load_from(
"test_artifacts/tx_output_predicate/out/release/tx_output_predicate.bin",
TX_OUTPUT_PREDICATE_BYTECODE_PATH,
)
.unwrap()
.with_data(predicate_data)
Expand Down Expand Up @@ -905,6 +915,101 @@ mod outputs {
assert_eq!(result.value, Output::Contract);
}

#[tokio::test]
async fn can_get_tx_output_type_for_contract_deployment() {
// Setup Wallet
let wallet = launch_custom_provider_and_get_wallets(
WalletsConfig::new(
Some(1), /* Single wallet */
Some(1), /* Single coin (UTXO) */
Some(1_000_000_000), /* Amount per coin */
),
None,
None,
)
.await
.unwrap()
.pop()
.unwrap();
let provider = wallet.try_provider().unwrap();

// Get the predicate
let predicate: Predicate = Predicate::load_from(TX_CONTRACT_CREATION_PREDICATE_BYTECODE_PATH).unwrap()
.with_provider(provider.clone());
let predicate_coin_amount = 100;

// Predicate has no funds
let predicate_balance = predicate.get_asset_balance(&provider.base_asset_id()).await.unwrap();
assert_eq!(predicate_balance, 0);

// Transfer funds to predicate
wallet
.transfer(
predicate.address(),
predicate_coin_amount,
*provider.base_asset_id(),
TxPolicies::default(),
)
.await.unwrap();

// Predicate has funds
let predicate_balance = predicate.get_asset_balance(&provider.base_asset_id()).await.unwrap();
assert_eq!(predicate_balance, predicate_coin_amount);

// Get contract ready for deployment
let binary = fs::read(TX_CONTRACT_BYTECODE_PATH).unwrap();
let salt = Salt::new([2u8; 32]);
let storage_slots = Vec::<StorageSlot>::new();
let contract = Contract::new(binary.clone(), salt, storage_slots.clone());

// Start building the transaction
let tb: CreateTransactionBuilder = CreateTransactionBuilder::prepare_contract_deployment(
binary,
contract.contract_id(),
contract.state_root(),
salt,
storage_slots,
TxPolicies::default(),
);

// Inputs
let inputs = predicate
.get_asset_inputs_for_amount(*provider.base_asset_id(), predicate_coin_amount, None)
.await.unwrap();

// Outputs
let mut outputs = wallet.get_asset_outputs_for_amount(
&wallet.address(),
*provider.base_asset_id(),
predicate_coin_amount,
);
outputs.push(SdkOutput::contract_created(contract.contract_id(), contract.state_root()));

let mut tb = tb.with_inputs(inputs).with_outputs(outputs);

wallet.add_witnesses(&mut tb).unwrap();
wallet.adjust_for_fee(&mut tb, 1).await.unwrap();

// Build transaction
let tx = tb.build(provider).await.unwrap();

// Send trandaction
provider
.send_transaction_and_await_commit(tx)
.await
.unwrap()
.check(None)
.unwrap();

// Verify contract was deployed
let instance = TxContractTest::new(contract.contract_id(), wallet.clone());
assert!(instance.methods().get_output_type(0).call().await.is_ok());

// Verify predicate funds transferred
let predicate_balance = predicate.get_asset_balance(&AssetId::default()).await.unwrap();
assert_eq!(predicate_balance, 0);
}

#[tokio::test]
async fn can_get_tx_output_details() {
let (wallet, _, predicate, asset_id, _) = setup_output_predicate().await;
Expand Down

0 comments on commit b92c7a2

Please sign in to comment.