From b2676a03d11779d8325303668e7b34469ce8943f Mon Sep 17 00:00:00 2001 From: Ahmed Sagdati <37515857+segfault-magnet@users.noreply.github.com> Date: Sat, 18 Nov 2023 00:37:26 +0100 Subject: [PATCH] bug: cannot fetch mint transactions (#1203) closes #1199 Mint transactions can now be fetched through the `Provider`. Implemented what made sense for `Mint`, left the rest as `unimplemented!`. Without some hefty redesign don't see what other option is there but to violate the LSP (L for Liskov). Decided to wrap `Mint`, so that users may extract the id, outputs, etc, without having to know about the various traits from fuel. ### Checklist - [x] I have linked to any relevant issues. - [ ] I have updated the documentation. - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added necessary labels. - [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. --- .../src/program_bindings/utils.rs | 3 +- packages/fuels-core/src/codec/logs.rs | 3 +- .../src/types/transaction_builders.rs | 3 +- .../src/types/wrappers/coin_type.rs | 3 +- .../src/types/wrappers/transaction.rs | 203 +++++------------- .../types/wrappers/transaction_response.rs | 2 +- packages/fuels/tests/providers.rs | 39 ++++ 7 files changed, 100 insertions(+), 156 deletions(-) diff --git a/packages/fuels-code-gen/src/program_bindings/utils.rs b/packages/fuels-code-gen/src/program_bindings/utils.rs index b78b7317dc..bd2cc04832 100644 --- a/packages/fuels-code-gen/src/program_bindings/utils.rs +++ b/packages/fuels-code-gen/src/program_bindings/utils.rs @@ -6,10 +6,9 @@ use itertools::Itertools; use proc_macro2::{Ident, TokenStream}; use quote::quote; -use super::resolved_type::GenericType; use crate::{ error::Result, - program_bindings::resolved_type::{ResolvedType, TypeResolver}, + program_bindings::resolved_type::{GenericType, ResolvedType, TypeResolver}, utils::{self, safe_ident, TypePath}, }; diff --git a/packages/fuels-core/src/codec/logs.rs b/packages/fuels-core/src/codec/logs.rs index dd3798eaf7..2a40cf1565 100644 --- a/packages/fuels-core/src/codec/logs.rs +++ b/packages/fuels-core/src/codec/logs.rs @@ -7,9 +7,8 @@ use std::{ use fuel_tx::{ContractId, Receipt}; -use super::ABIDecoder; use crate::{ - codec::DecoderConfig, + codec::{ABIDecoder, DecoderConfig}, traits::{Parameterize, Tokenizable}, types::{ errors::{error, Error, Result}, diff --git a/packages/fuels-core/src/types/transaction_builders.rs b/packages/fuels-core/src/types/transaction_builders.rs index 26ee5206d1..dcc2052ad1 100644 --- a/packages/fuels-core/src/types/transaction_builders.rs +++ b/packages/fuels-core/src/types/transaction_builders.rs @@ -15,7 +15,6 @@ use fuel_tx::{ use fuel_types::{bytes::padded_len_usize, canonical::Serialize, Bytes32, ChainId, Salt}; use zeroize::{Zeroize, ZeroizeOnDrop}; -use super::{chain_info::ChainInfo, node_info::NodeInfo}; use crate::{ constants::{ BASE_ASSET_ID, DEFAULT_CREATE_WITNESS_LIMIT, DEFAULT_SCRIPT_WITNESS_LIMIT, WORD_SIZE, @@ -23,11 +22,13 @@ use crate::{ offsets, types::{ bech32::Bech32Address, + chain_info::ChainInfo, coin::Coin, coin_type::CoinType, errors::{error, Result}, input::Input, message::Message, + node_info::NodeInfo, transaction::{ CreateTransaction, EstimablePredicates, ScriptTransaction, Transaction, TxPolicies, }, diff --git a/packages/fuels-core/src/types/wrappers/coin_type.rs b/packages/fuels-core/src/types/wrappers/coin_type.rs index 3cb47839d3..93cba94f70 100644 --- a/packages/fuels-core/src/types/wrappers/coin_type.rs +++ b/packages/fuels-core/src/types/wrappers/coin_type.rs @@ -3,10 +3,9 @@ use fuel_core_client::client::types::CoinType as ClientCoinType; use fuel_types::AssetId; -use super::coin_type_id::CoinTypeId; use crate::{ constants::BASE_ASSET_ID, - types::{bech32::Bech32Address, coin::Coin, message::Message}, + types::{bech32::Bech32Address, coin::Coin, coin_type_id::CoinTypeId, message::Message}, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/packages/fuels-core/src/types/wrappers/transaction.rs b/packages/fuels-core/src/types/wrappers/transaction.rs index bb8d77fa64..6559edc500 100644 --- a/packages/fuels-core/src/types/wrappers/transaction.rs +++ b/packages/fuels-core/src/types/wrappers/transaction.rs @@ -2,8 +2,8 @@ use std::{collections::HashMap, fmt::Debug}; use fuel_tx::{ field::{ - GasPrice, Inputs, Maturity, Outputs, Script as ScriptField, ScriptData, ScriptGasLimit, - Witnesses, + GasPrice, Inputs, Maturity, MintAmount, MintAssetId, Outputs, Script as ScriptField, + ScriptData, ScriptGasLimit, Witnesses, }, input::{ coin::{CoinPredicate, CoinSigned}, @@ -12,7 +12,7 @@ use fuel_tx::{ }, }, Buildable, Bytes32, Cacheable, Chargeable, ConsensusParameters, Create, FormatValidityChecks, - Input, Output, Salt as FuelSalt, Script, StorageSlot, Transaction as FuelTransaction, + Input, Mint, Output, Salt as FuelSalt, Script, StorageSlot, Transaction as FuelTransaction, TransactionFee, UniqueIdentifier, Witness, }; use fuel_types::{AssetId, ChainId}; @@ -24,8 +24,56 @@ use crate::{ types::{bech32::Bech32Address, errors::error, Result}, }; -#[derive(Default, Debug, Copy, Clone)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct MintTransaction { + tx: Box, +} + +impl From for FuelTransaction { + fn from(mint: MintTransaction) -> Self { + (*mint.tx).into() + } +} + +impl From for Mint { + fn from(tx: MintTransaction) -> Self { + *tx.tx + } +} + +impl From for MintTransaction { + fn from(tx: Mint) -> Self { + Self { tx: Box::new(tx) } + } +} + +impl MintTransaction { + pub fn check_without_signatures( + &self, + block_height: u32, + consensus_parameters: &ConsensusParameters, + ) -> Result<()> { + Ok(self + .tx + .check_without_signatures(block_height.into(), consensus_parameters)?) + } + #[must_use] + pub fn id(&self, chain_id: ChainId) -> Bytes32 { + self.tx.id(&chain_id) + } + #[must_use] + pub fn mint_asset_id(&self) -> &AssetId { + self.tx.mint_asset_id() + } + + #[must_use] + pub fn mint_amount(&self) -> u64 { + *self.tx.mint_amount() + } +} + +#[derive(Default, Debug, Copy, Clone)] //ANCHOR: tx_policies_struct pub struct TxPolicies { gas_price: Option, @@ -101,12 +149,13 @@ impl TxPolicies { use fuel_tx::field::{BytecodeLength, BytecodeWitnessIndex, Salt, StorageSlots}; -use super::coin_type_id::CoinTypeId; +use crate::types::coin_type_id::CoinTypeId; #[derive(Debug, Clone)] pub enum TransactionType { Script(ScriptTransaction), Create(CreateTransaction), + Mint(MintTransaction), } pub trait EstimablePredicates { @@ -171,149 +220,7 @@ impl From for FuelTransaction { match value { TransactionType::Script(tx) => tx.into(), TransactionType::Create(tx) => tx.into(), - } - } -} - -impl EstimablePredicates for TransactionType { - fn estimate_predicates(&mut self, consensus_parameters: &ConsensusParameters) -> Result<()> { - match self { - TransactionType::Script(tx) => tx.estimate_predicates(consensus_parameters), - TransactionType::Create(tx) => tx.estimate_predicates(consensus_parameters), - } - } -} - -impl GasValidation for TransactionType { - fn validate_gas(&self, min_gas_price: u64, gas_used: u64) -> Result<()> { - match self { - TransactionType::Script(tx) => tx.validate_gas(min_gas_price, gas_used), - TransactionType::Create(tx) => tx.validate_gas(min_gas_price, gas_used), - } - } -} - -impl Transaction for TransactionType { - fn fee_checked_from_tx( - &self, - consensus_parameters: &ConsensusParameters, - ) -> Option { - match self { - TransactionType::Script(tx) => tx.fee_checked_from_tx(consensus_parameters), - TransactionType::Create(tx) => tx.fee_checked_from_tx(consensus_parameters), - } - } - - fn max_gas(&self, consensus_parameters: &ConsensusParameters) -> u64 { - match self { - TransactionType::Script(tx) => tx.max_gas(consensus_parameters), - TransactionType::Create(tx) => tx.max_gas(consensus_parameters), - } - } - - fn check_without_signatures( - &self, - block_height: u32, - consensus_parameters: &ConsensusParameters, - ) -> Result<()> { - match self { - TransactionType::Script(tx) => { - tx.check_without_signatures(block_height, consensus_parameters) - } - TransactionType::Create(tx) => { - tx.check_without_signatures(block_height, consensus_parameters) - } - } - } - - fn id(&self, chain_id: ChainId) -> Bytes32 { - match self { - TransactionType::Script(tx) => tx.id(chain_id), - TransactionType::Create(tx) => tx.id(chain_id), - } - } - - fn maturity(&self) -> u32 { - match self { - TransactionType::Script(tx) => tx.maturity(), - TransactionType::Create(tx) => tx.maturity(), - } - } - - fn with_maturity(self, maturity: u32) -> Self { - match self { - TransactionType::Script(tx) => TransactionType::Script(tx.with_maturity(maturity)), - TransactionType::Create(tx) => TransactionType::Create(tx.with_maturity(maturity)), - } - } - - fn gas_price(&self) -> u64 { - match self { - TransactionType::Script(tx) => tx.gas_price(), - TransactionType::Create(tx) => tx.gas_price(), - } - } - - fn with_gas_price(self, gas_price: u64) -> Self { - match self { - TransactionType::Script(tx) => TransactionType::Script(tx.with_gas_price(gas_price)), - TransactionType::Create(tx) => TransactionType::Create(tx.with_gas_price(gas_price)), - } - } - - fn metered_bytes_size(&self) -> usize { - match self { - TransactionType::Script(tx) => tx.metered_bytes_size(), - TransactionType::Create(tx) => tx.metered_bytes_size(), - } - } - - fn inputs(&self) -> &Vec { - match self { - TransactionType::Script(tx) => tx.inputs(), - TransactionType::Create(tx) => tx.inputs(), - } - } - - fn outputs(&self) -> &Vec { - match self { - TransactionType::Script(tx) => tx.outputs(), - TransactionType::Create(tx) => tx.outputs(), - } - } - - fn witnesses(&self) -> &Vec { - match self { - TransactionType::Script(tx) => tx.witnesses(), - TransactionType::Create(tx) => tx.witnesses(), - } - } - - fn is_using_predicates(&self) -> bool { - match self { - TransactionType::Script(tx) => tx.is_using_predicates(), - TransactionType::Create(tx) => tx.is_using_predicates(), - } - } - - fn precompute(&mut self, chain_id: &ChainId) -> Result<()> { - match self { - TransactionType::Script(tx) => tx.precompute(chain_id), - TransactionType::Create(tx) => tx.precompute(chain_id), - } - } - - fn append_witness(&mut self, witness: Witness) -> usize { - match self { - TransactionType::Script(tx) => tx.append_witness(witness), - TransactionType::Create(tx) => tx.append_witness(witness), - } - } - - fn used_coins(&self) -> HashMap<(Bech32Address, AssetId), Vec> { - match self { - TransactionType::Script(tx) => tx.used_coins(), - TransactionType::Create(tx) => tx.used_coins(), + TransactionType::Mint(tx) => tx.into(), } } } diff --git a/packages/fuels-core/src/types/wrappers/transaction_response.rs b/packages/fuels-core/src/types/wrappers/transaction_response.rs index 1bf559a287..f875470cc7 100644 --- a/packages/fuels-core/src/types/wrappers/transaction_response.rs +++ b/packages/fuels-core/src/types/wrappers/transaction_response.rs @@ -47,7 +47,7 @@ impl From for TransactionResponse { let transaction = match client_response.transaction { Transaction::Script(tx) => TransactionType::Script(ScriptTransaction::from(tx)), Transaction::Create(tx) => TransactionType::Create(CreateTransaction::from(tx)), - Transaction::Mint(_) => unimplemented!(), + Transaction::Mint(tx) => TransactionType::Mint(tx.into()), }; Self { diff --git a/packages/fuels/tests/providers.rs b/packages/fuels/tests/providers.rs index 97c51f279c..0c7f8a9e12 100644 --- a/packages/fuels/tests/providers.rs +++ b/packages/fuels/tests/providers.rs @@ -934,6 +934,45 @@ async fn test_cache_invalidation_on_await() -> Result<()> { Ok(()) } +#[tokio::test] +async fn can_fetch_mint_transactions() -> Result<()> { + setup_program_test!( + Wallets("wallet"), + Abigen(Contract( + name = "TestContract", + project = "packages/fuels/tests/contracts/contract_test" + )), + Deploy( + name = "contract_instance", + contract = "TestContract", + wallet = "wallet" + ), + ); + + let provider = wallet.try_provider()?; + + let transactions = provider + .get_transactions(PaginationRequest { + cursor: None, + results: 100, + direction: PageDirection::Forward, + }) + .await? + .results; + + // TODO: remove once (fuels-rs#1093)[https://github.com/FuelLabs/fuels-rs/issues/1093] is in + // until then the type is explicitly mentioned to check that we're reexporting it through fuels + let _: ::fuels::types::transaction::MintTransaction = transactions + .into_iter() + .find_map(|tx| match tx.transaction { + TransactionType::Mint(tx) => Some(tx), + _ => None, + }) + .expect("Should have had at least one mint transaction"); + + Ok(()) +} + #[tokio::test] async fn test_build_with_provider() -> Result<()> { let wallet = launch_provider_and_get_wallet().await?;