Hello, I was wondering if there is a specific way to call a function in a contract that sends tokens when using the multicall feature.
For example, when not using multicall, this works just fine:
market.withdraw(quote_withdraw_amount, AssetType::Quote).await;
This function allows a user to withdraw assets.
However, when adding this same function call to the multi_call_handler, it will fail. I was wondering if there are specific call parameters I need to add to this call?
let mut multi_call_handler = CallHandler::new_multi_call(main_wallet.clone());
let withdraw_quote_call = market.get_instance().methods().withdraw(quote_withdraw_amount, AssetType::Quote);
multi_call_handler = multi_call_handler.add_call(withdraw_quote_call);
Here’s a script that does what I describe above, you can try it yourself:
.get_instance()
.methods()
.withdraw(base_withdraw_amount, AssetType::Base)
.call_params(CallParameters::new(0, AssetId::default(), 100_000_000)) // Adjust gas limit
.unwrap();
multi_call_handler = multi_call_handler.add_call(withdraw_base_call);
}
// Withdraw quote asset (e.g., USDC) if balance is greater than zero
if quote_withdraw_amount > 0 {
let withdraw_quote_call = btc_market
.get_instance()
.methods()
.withdraw(quote_withdraw_amount, AssetType::Quote)
.call_params(CallParameters::new(0, AssetId::default(), 100_000_000)) // Adjust gas limit
.unwrap();
multi_call_handler = multi_call_handler.add_call(withdraw_quote_call);
}
Here’s a tx on the block explorer that has the failed transaction when calling withdraw in the multicall: Fuel Explorer
hal3e
September 18, 2024, 8:31am
2
What is the error message you get? Is there a way for us to reproduce this locally ?
1 Like
Yes, the best way to replicate this is to clone the examples repo and run the withdraw_multicall.rs script:
use dotenv::dotenv;
use std::{env, error::Error, str::FromStr};
use fuels::{
accounts::{provider::Provider, wallet::WalletUnlocked},
prelude::{CallParameters, VariableOutputPolicy},
programs::calls::CallHandler,
types::transaction::TxPolicies,
types::{AssetId, ContractId, Identity},
};
use spark_market_sdk::{AssetType, SparkMarketContract};
pub fn format_value_with_decimals(value: u64, decimals: u32) -> u64 {
value * 10u64.pow(decimals)
}
// 1) Run withdraw.rs on BTC & ETH markets
// 2) Run batch_open_orders.rs for BTC market
// 3) Run this script:
This file has been truncated. show original
Here’s an isolated test that is easy to run locally. In the test we withdraw and then deposit an asset from a single contract.
In our flow in production we would like to withdraw from one contract, then deposit to a second contract.
The test: orderbook-contract/spark-market/tests/functions/core/withdraw.rs at aeca39ce9dab2ed5ec01b47a30222fac7a4b4d89 · compolabs/orderbook-contract · GitHub
We get the error: “not enough coins to fit the target”
hal3e
September 20, 2024, 8:26am
5
After setting the correct number of variable outputs the issue should be resolved.
fuel
September 20, 2024, 10:42am
6
added a test to our repo
let new_balance = owner.balance(&assets.base.id).await;
let user_account = contract.account(owner.identity()).await?.value;
assert_eq!(new_balance, user_balance + deposit_amount);
assert_eq!(user_account, expected_account);
Ok(())
}
#[tokio::test]
async fn base_asset_multicall() -> anyhow::Result<()> {
let defaults = Defaults::default();
let (contract, owner, _user, _, _, assets) = setup(
defaults.base_decimals,
defaults.quote_decimals,
defaults.price_decimals,
)
.await?;
let deposit_amount = owner.balance(&assets.base.id).await;
assert!(deposit_amount != 0);
we are using variable_outputs in our sdk
pub async fn withdraw(
&self,
amount: u64,
asset_type: AssetType,
) -> anyhow::Result<CallResponse<()>> {
Ok(self
.instance
.methods()
.withdraw(amount, asset_type)
.with_variable_output_policy(VariableOutputPolicy::Exactly(1))
.call()
.await?)
}
pub async fn open_order(
&self,
amount: u64,
order_type: OrderType,
price: u64,
) -> anyhow::Result<CallResponse<Bits256>> {
The error is still here, I checked it with different versions of forc
Steps to reproduce:
Using forc version “0.63.5”:
Clone the repository:git clone git@github.com:compolabs/orderbook-contract.git
Switch to the branch:git checkout bug-multicall-withdraw-deposit
Run the test:cargo test --package spark-market --test integration_tests -- functions::core::withdraw::success --show-output
Using forc version “0.63.6”:
1 Like
Just for reference, I was able to perform a multicall, withdrawing assets from one contract, and depositing them to another using our spark-rust-sdk-examples repo.
The issue is doing this within our tests in our orderbook-contract repo, as well as on the frontend using fuels-ts.