Skip to content

Commit

Permalink
fix!: get_asset_inputs_for_amount uses resources (#692)
Browse files Browse the repository at this point in the history
Closes #691

Preparing asset inputs now works with `Resource` instead of `Coin`. I
didn't pay too much attention to cleanliness to push it quickly.

Co-authored-by: Rodrigo Araujo <rod.dearaujo@gmail.com>
  • Loading branch information
MujkicA and digorithm committed Nov 10, 2022
1 parent fd27f86 commit b90d664
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 185 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ env:
CARGO_TERM_COLOR: always
DASEL_VERSION: https://github.com/TomWright/dasel/releases/download/v1.24.3/dasel_linux_amd64
RUSTFLAGS: "-D warnings"
FUEL_CORE_VERSION: 0.14.0
FUEL_CORE_VERSION: 0.14.1
RUST_VERSION: 1.64.0
FORC_VERSION: 0.30.0

Expand Down
8 changes: 4 additions & 4 deletions docs/src/providers/querying.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Once you have set up a provider, you're ready to interact with the Fuel blockcha
- [Interacting with the blockchain](#interacting-with-the-blockchain)
- [Set up](#set-up)
- [Get all coins from an address](#get-all-coins-from-an-address)
- [Get spendable coins from an address](#get-spendable-coins-from-an-address)
- [Get spendable resources from an address](#get-spendable-resources-from-an-address)
- [Get balances from an address](#get-balances-from-an-address)

## Set up
Expand All @@ -24,12 +24,12 @@ This method returns all coins (of a given asset ID) from a wallet, including spe
{{#include ../../../examples/providers/src/lib.rs:get_coins}}
```

## Get spendable coins from an address
## Get spendable resources from an address

The last argument says how much you want to spend. This method returns only spendable, i.e., unspent coins (of a given asset ID). If you ask for more spendable than the amount of unspent coins you have, it returns an error.
The last argument says how much you want to spend. This method returns only spendable, i.e., unspent coins (of a given asset ID) or messages. If you ask for more spendable resources than the amount of resources you have, it returns an error.

```rust,ignore
{{#include ../../../examples/providers/src/lib.rs:get_spendable_coins}}
{{#include ../../../examples/providers/src/lib.rs:get_spendable_resources}}
```

## Get balances from an address
Expand Down
10 changes: 5 additions & 5 deletions examples/providers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ mod tests {
assert_eq!(coins.len(), 1);
// ANCHOR_END: get_coins

// ANCHOR: get_spendable_coins
let spendable_coins = provider
.get_spendable_coins(wallet.address(), BASE_ASSET_ID, 1)
// ANCHOR: get_spendable_resources
let spendable_resources = provider
.get_spendable_resources(wallet.address(), BASE_ASSET_ID, 1)
.await?;
assert_eq!(spendable_coins.len(), 1);
// ANCHOR_END: get_spendable_coins
assert_eq!(spendable_resources.len(), 1);
// ANCHOR_END: get_spendable_resources

// ANCHOR: get_balances
let _balances = provider.get_balances(wallet.address()).await?;
Expand Down
103 changes: 61 additions & 42 deletions packages/fuels-contract/src/script.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Result;
use fuel_gql_client::client::schema::resource::Resource;
use fuel_gql_client::fuel_tx::{
field::Script as ScriptField, ConsensusParameters, Input, Output, Receipt, Transaction,
TxPointer, UtxoId,
Expand All @@ -7,9 +8,9 @@ use fuel_gql_client::fuel_types::{
bytes::padded_len_usize, AssetId, Bytes32, ContractId, Immediate18, Word,
};
use fuel_gql_client::fuel_vm::{consts::REG_ONE, prelude::Opcode};
use fuels_core::constants::BASE_ASSET_ID;
use itertools::{chain, Itertools};

use fuel_gql_client::client::schema::coin::Coin;
use fuel_tx::{Checkable, ScriptExecutionResult, Witness};
use fuels_core::parameters::TxParameters;
use fuels_signers::provider::Provider;
Expand Down Expand Up @@ -64,10 +65,11 @@ impl Script {
let script = Self::get_instructions(calls, call_param_offsets);

let required_asset_amounts = Self::calculate_required_asset_amounts(calls);
let spendable_coins = Self::get_spendable_coins(wallet, &required_asset_amounts).await?;
let spendable_resources =
Self::get_spendable_resources(wallet, &required_asset_amounts).await?;

let (inputs, outputs) =
Self::get_transaction_inputs_outputs(calls, wallet.address(), spendable_coins);
Self::get_transaction_inputs_outputs(calls, wallet.address(), spendable_resources);

let mut tx = Transaction::script(
tx_parameters.gas_price,
Expand All @@ -94,18 +96,18 @@ impl Script {

/// Calculates how much of each asset id the given calls require and
/// proceeds to request spendable coins from `wallet` to cover that cost.
async fn get_spendable_coins(
async fn get_spendable_resources(
wallet: &WalletUnlocked,
required_asset_amounts: &[(AssetId, u64)],
) -> Result<Vec<Coin>, Error> {
let mut coins = vec![];
) -> Result<Vec<Resource>, Error> {
let mut resources = vec![];

for (asset_id, amount) in required_asset_amounts {
let spendable_coins = wallet.get_spendable_coins(*asset_id, *amount).await?;
coins.extend(spendable_coins);
let spendable_resources = wallet.get_spendable_resources(*asset_id, *amount).await?;
resources.extend(spendable_resources);
}

Ok(coins)
Ok(resources)
}

fn calculate_required_asset_amounts(calls: &[ContractCall]) -> Vec<(AssetId, u64)> {
Expand Down Expand Up @@ -245,15 +247,15 @@ impl Script {
fn get_transaction_inputs_outputs(
calls: &[ContractCall],
wallet_address: &Bech32Address,
spendable_coins: Vec<Coin>,
spendable_resources: Vec<Resource>,
) -> (Vec<Input>, Vec<Output>) {
let asset_ids = Self::extract_unique_asset_ids(&spendable_coins);
let asset_ids = Self::extract_unique_asset_ids(&spendable_resources);
let contract_ids = Self::extract_unique_contract_ids(calls);
let num_of_contracts = contract_ids.len();

let inputs = chain!(
Self::generate_contract_inputs(contract_ids),
Self::convert_to_signed_coins(spendable_coins),
Self::convert_to_signed_resources(spendable_resources),
)
.collect();

Expand All @@ -271,10 +273,13 @@ impl Script {
(inputs, outputs)
}

fn extract_unique_asset_ids(spendable_coins: &[Coin]) -> HashSet<AssetId> {
fn extract_unique_asset_ids(spendable_coins: &[Resource]) -> HashSet<AssetId> {
spendable_coins
.iter()
.map(|coin| coin.asset_id.clone().into())
.map(|resource| match resource {
Resource::Coin(coin) => coin.asset_id.clone().into(),
Resource::Message(_) => BASE_ASSET_ID,
})
.collect()
}

Expand Down Expand Up @@ -310,19 +315,28 @@ impl Script {
.collect()
}

fn convert_to_signed_coins(spendable_coins: Vec<Coin>) -> Vec<Input> {
spendable_coins
fn convert_to_signed_resources(spendable_resources: Vec<Resource>) -> Vec<Input> {
spendable_resources
.into_iter()
.map(|coin| {
Input::coin_signed(
.map(|resource| match resource {
Resource::Coin(coin) => Input::coin_signed(
UtxoId::from(coin.utxo_id),
coin.owner.into(),
coin.amount.0,
coin.asset_id.into(),
TxPointer::default(),
0,
0,
)
),
Resource::Message(message) => Input::message_signed(
message.message_id.into(),
message.sender.into(),
message.recipient.into(),
message.amount.into(),
message.nonce.into(),
0,
message.data.into(),
),
})
.collect()
}
Expand Down Expand Up @@ -407,7 +421,7 @@ impl Script {
#[cfg(test)]
mod test {
use super::*;
use fuel_gql_client::client::schema::coin::CoinStatus;
use fuel_gql_client::client::schema::coin::{Coin, CoinStatus};
use fuels_core::abi_encoder::ABIEncoder;
use fuels_core::parameters::CallParameters;
use fuels_core::Token;
Expand Down Expand Up @@ -650,14 +664,16 @@ mod test {

let coins = asset_ids
.into_iter()
.map(|asset_id| Coin {
amount: 100u64.into(),
block_created: 0u64.into(),
asset_id: asset_id.into(),
utxo_id: Default::default(),
maturity: 0u64.into(),
owner: Default::default(),
status: CoinStatus::Unspent,
.map(|asset_id| {
Resource::Coin(Coin {
amount: 100u64.into(),
block_created: 0u64.into(),
asset_id: asset_id.into(),
utxo_id: Default::default(),
maturity: 0u64.into(),
owner: Default::default(),
status: CoinStatus::Unspent,
})
})
.collect();
let call = ContractCall::new_with_random_id();
Expand Down Expand Up @@ -685,18 +701,20 @@ mod test {
// given
let asset_ids = [AssetId::default(), AssetId::from([1; 32])];

let generate_spendable_coins = || {
let generate_spendable_resources = || {
asset_ids
.into_iter()
.enumerate()
.map(|(index, asset_id)| Coin {
amount: (index * 10).into(),
block_created: 1u64.into(),
asset_id: asset_id.into(),
utxo_id: Default::default(),
maturity: 0u64.into(),
owner: Default::default(),
status: CoinStatus::Unspent,
.map(|(index, asset_id)| {
Resource::Coin(Coin {
amount: (index * 10).into(),
block_created: 1u64.into(),
asset_id: asset_id.into(),
utxo_id: Default::default(),
maturity: 0u64.into(),
owner: Default::default(),
status: CoinStatus::Unspent,
})
})
.collect::<Vec<_>>()
};
Expand All @@ -707,24 +725,25 @@ mod test {
let (inputs, _) = Script::get_transaction_inputs_outputs(
&[call],
&random_bech32_addr(),
generate_spendable_coins(),
generate_spendable_resources(),
);

// then
let inputs_as_signed_coins: HashSet<Input> = inputs[1..].iter().cloned().collect();

let expected_inputs = generate_spendable_coins()
let expected_inputs = generate_spendable_resources()
.into_iter()
.map(|coin| {
Input::coin_signed(
.map(|resource| match resource {
Resource::Coin(coin) => Input::coin_signed(
fuel_tx::UtxoId::from(coin.utxo_id),
coin.owner.into(),
coin.amount.0,
coin.asset_id.into(),
TxPointer::default(),
0,
0,
)
),
Resource::Message(_) => panic!("Resources contained messages."),
})
.collect::<HashSet<_>>();

Expand Down
5 changes: 3 additions & 2 deletions packages/fuels-signers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,13 @@ mod tests {
assert_eq!(script.price(), gas_price);
assert_eq!(*script.maturity(), maturity);

let wallet_1_spendable_coins = wallet_1.get_spendable_coins(BASE_ASSET_ID, 0).await?;
let wallet_1_spendable_resources =
wallet_1.get_spendable_resources(BASE_ASSET_ID, 0).await?;
let wallet_1_all_coins = wallet_1.get_coins(BASE_ASSET_ID).await?;
let wallet_2_all_coins = wallet_2.get_coins(BASE_ASSET_ID).await?;

// wallet_1 has now only 1 spent coin (so 0 spendable)
assert_eq!(wallet_1_spendable_coins.len(), 0);
assert_eq!(wallet_1_spendable_resources.len(), 0);
assert_eq!(wallet_1_all_coins.len(), 1);
// Check that wallet two now has two coins.
assert_eq!(wallet_2_all_coins.len(), 2);
Expand Down
42 changes: 4 additions & 38 deletions packages/fuels-signers/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,59 +210,25 @@ impl Provider {
/// Get some spendable coins of asset `asset_id` for address `from` that add up at least to
/// amount `amount`. The returned coins (UTXOs) are actual coins that can be spent. The number
/// of coins (UXTOs) is optimized to prevent dust accumulation.
pub async fn get_spendable_coins(
pub async fn get_spendable_resources(
&self,
from: &Bech32Address,
asset_id: AssetId,
amount: u64,
) -> Result<Vec<Coin>, ProviderError> {
) -> Result<Vec<Resource>, ProviderError> {
let res = self
.client
.resources_to_spend(
&from.hash().to_string(),
vec![(format!("{:#x}", asset_id).as_str(), amount, None)],
None,
)
.await?;

let coins = res
.into_iter()
.flatten()
.filter_map(|r| match r {
Resource::Coin(c) => Some(c),
_ => None,
})
.collect();

Ok(coins)
}

/// Get some spendable messages for address `from`.
/// The returned messages are actual messages that can be spent. The number
/// of messages is optimized to prevent dust accumulation.
pub async fn get_spendable_messages(
&self,
from: &Bech32Address,
) -> Result<Vec<Message>, ProviderError> {
let res = self
.client
.resources_to_spend(
&from.hash().to_string(),
vec![(format!("{:#x}", AssetId::default()).as_str(), 1, None)],
None,
)
.await?;

let messages = res
.await?
.into_iter()
.flatten()
.filter_map(|r| match r {
Resource::Message(m) => Some(m),
_ => None,
})
.collect();

Ok(messages)
Ok(res)
}

/// Get the balance of all spendable coins `asset_id` for address `address`. This is different
Expand Down
Loading

0 comments on commit b90d664

Please sign in to comment.