FuelLabs / fuels-rs

Fuel Network Rust SDK
https://fuellabs.github.io/fuels-rs
Apache License 2.0
44.37k stars 1.33k forks source link

Error When Calling Contract Functions Using MultiContractCallHandler #1427

Open chlenc opened 3 weeks ago

chlenc commented 3 weeks ago

Issue: Error When Calling Contract Functions Using MultiContractCallHandler

Description

When invoking contract functions using MultiContractCallHandler, an error occurs:

called `Result::unwrap()` on an `Err` value: Other("expected `Unit`, got `Tuple([Unit])`")

However, the transaction actually succeeds.

Steps to Reproduce

git clone git@github.com:compolabs/orderbook-contract.git 

cd orderbook-contract/  

git checkout testnet

forc build -v --release

echo "ADMIN=<YOUR PK WITH SOME TEST ETH>" >> .env 

cargo run --bin match_in_pairs_multicall

Error Details

While executing the script, the following error is encountered:

called `Result::unwrap()` on an `Err` value: Other("expected `Unit`, got `Tuple([Unit])`")
image

Despite this error, the transaction is successfully processed.

References

Function being called

https://github.com/compolabs/orderbook-contract/blob/testnet/src/orderbook_utils.rs#L308

 pub async fn match_in_pairs_multicall(
        &self,
        orders: Vec<(Bits256, Bits256)>,
    ) -> Result<FuelCallResponse<()>, fuels::types::errors::Error> {
        let wallet = self.instance.account();
        let mut multi_call_handler = MultiContractCallHandler::new(wallet.clone());

        for (sell_order_id, buy_order_id) in orders {
            let call_handler = self
                .instance
                .methods()
                .match_orders(sell_order_id.clone(), buy_order_id.clone())
                .append_variable_outputs(2)
                .with_tx_policies(TxPolicies::default().with_script_gas_limit(3500000));
            multi_call_handler.add_call(call_handler);
        }

        multi_call_handler
            .with_tx_policies(
                TxPolicies::default()
                    .with_tip(1)
                    .with_script_gas_limit(9_999_000),
            )
            .call()
            .await
    }

Script used to call the function

https://github.com/compolabs/orderbook-contract/blob/testnet/scripts/match_in_pairs_multicall.rs


#[tokio::main]
async fn main() {
    print_title("Match in pairs multicall");
    dotenv().ok();
    let provider = Provider::connect(RPC).await.unwrap();
    let secret = std::env::var("ADMIN").unwrap();
    let wallet =
        WalletUnlocked::new_from_private_key(secret.parse().unwrap(), Some(provider.clone()));
    println!("wallet.address() = {:?}", wallet.address());
    let token_contract = TokenContract::new(
        &ContractId::from_str(TOKEN_CONTRACT_ID).unwrap().into(),
        wallet.clone(),
    );

    let token_contract_id = token_contract.contract_id().into();
    let base_asset = Asset::new(wallet.clone(), token_contract_id, MARKET_SYMBOL);
    let quote_asset = Asset::new(wallet.clone(), token_contract_id, "USDC");

    let orderbook = Orderbook::new(&wallet, ORDERBOOK_CONTRACT_ID).await;
    let price = (BASE_PRICE * 10f64.powf(orderbook.price_decimals as f64)) as u64;

    //mint base asset to sell
    let base_size = base_asset.parse_units(BASE_SIZE as f64) as u64;
    base_asset
        .mint(wallet.address().into(), base_size)
        .await
        .unwrap();

    // sell
    let sell_order_id = orderbook
        .open_order(base_asset.asset_id, -1 * base_size as i64, price - 1)
        .await
        .unwrap()
        .value;
    println!(
        "sell_order = {:?}",
        orderbook.order_by_id(&sell_order_id).await.unwrap().value
    );

    //mint quote asset to buy
    let quote_size = quote_asset.parse_units(BASE_SIZE as f64 * BASE_PRICE as f64);
    quote_asset
        .mint(wallet.address().into(), quote_size as u64)
        .await
        .unwrap();

    //buy
    let buy_order_id = orderbook
        .open_order(base_asset.asset_id, base_size as i64, price)
        .await
        .unwrap()
        .value;

    println!(
        "buy_order = {:?}\n",
        orderbook.order_by_id(&buy_order_id).await.unwrap().value
    );

    orderbook
        .match_in_pairs_multicall(vec![(sell_order_id, buy_order_id)])
        .await
        .unwrap();
}

Expected Behavior

The MultiContractCallHandler should not throw an error if the transaction is successful. The handler should correctly interpret the result type without encountering the Unit vs Tuple([Unit]) mismatch.

hal3e commented 3 weeks ago

The problem is that for the MultiContractCallHandler you have to specify the output type of the call. It is a tuple of types of the added calls. Let's say you added 3 calls and each returns an u8 the output type of the multi-call would be (u8, u8, u8) Please have a look at the docs: https://rust.fuel.network/v0.63.1/calling-contracts/multicalls.html#output-values

As you are dynamically processing orders, I think you will not be able to use MultiCallHandler in this way as you can not dynamically set the output type.