Transfer from contract reverts - any idea why?

I’ve banged my head against the wall on this one.

Given the following contract function:

#[storage(read, write)]
    fn distribute_funds(amount: u64) {
        let total_shares = storage.total_shares.try_read().unwrap_or(1);
        let mut i = 0;

        // Debug: Print total shares and amount
        log("Total shares: ");
        log(total_shares);
        log("Distributing amount: ");
        log(amount);

        while i < storage.shares.len() {
            let (recipient, share) = storage.shares.get(i).unwrap().read();

            // Debug: Print recipient and share
            log("Recipient: ");
            log(recipient);
            log("Share: ");
            log(share);

            // Calculate the amount to send
            let amount_to_send: u64 = ((amount * share) / total_shares);

            require(amount_to_send > 0, DistributionError::CanNotSendZero);

            log("Amount to send: ");
            log(amount_to_send);

            transfer(recipient, AssetId::base(), amount_to_send);
            i += 1;
        }
    }

And the following test:

mod success {

    use super::*;

    #[tokio::test]
    async fn should_distribute_funds() {
        let (owner_wallet, other_wallet, another_wallet, id, instance_1, instance_2) = setup().await;
        let (
            owner_identity,
            other_identity,
            _another_identity,
        ) = defaults(id, owner_wallet.clone(), other_wallet.clone(), another_wallet.clone());

        constructor(&instance_1, owner_identity).await;

        let initial_owner_wallet_balance = owner_wallet.get_asset_balance(&AssetId::zeroed()).await.unwrap();
        let initial_other_wallet_balance = other_wallet.get_asset_balance(&AssetId::zeroed()).await.unwrap();
        let initial_another_wallet_balance = another_wallet.get_asset_balance(&AssetId::zeroed()).await.unwrap();

        // Set shares for owner_identity and other_identity
        set_shares(&instance_1, vec![owner_identity.clone(), other_identity.clone()], vec![40, 60]).await;

        // Amount to be sent to the contract
        let amount = 1_000;

        // Call receive_funds with the specified amount
        receive_funds(&instance_1, amount).await;

        // Verify that the contract now has the specified amount of base_asset
        let contract_balances = instance_1.get_balances().await.unwrap();
        let base_asset_balance = contract_balances.get(&AssetId::zeroed()).copied().unwrap_or(0);
        println!("base_asset_balance: {:?}", base_asset_balance);
        assert_eq!(base_asset_balance, amount);

        // Call distribute_funds with the same amount
        let response = distribute_funds(&instance_1, 1_000).await;

        let logs = response.decode_logs();
        println!("LOGS:::");
        println!("{:?}", logs);

        // Verify that the correct amount of asset has been received by the wallets
        let owner_balance = owner_wallet.get_asset_balance(&AssetId::zeroed()).await.unwrap();
        let other_balance = other_wallet.get_asset_balance(&AssetId::zeroed()).await.unwrap();

        // Calculate expected balances
        let total_shares = 40 + 60;
        let owner_expected_balance = initial_owner_wallet_balance + (amount * 40) / total_shares;
        let other_expected_balance = initial_other_wallet_balance + (amount * 60) / total_shares;

        println!("owner_balance: {:?}", owner_balance);
        println!("other_balance: {:?}", other_balance);

        assert_eq!(owner_balance, owner_expected_balance);
        assert_eq!(other_balance, other_expected_balance);
    }
}

Any idea why it would panic at the transfer? Given the test it seems that all balances on the contract and on wallets are correct, but the transfer function returns

I see this somewhere in the docs:

.with_variable_output_policy(VariableOutputPolicy::Exactly(2))

Where do you import VariableOutputPolicy from?

Here’s my function wrapper for distribute_funds():
I’ve tried all combinations of with_variable_output_policy etc to no avail. Help really appreciated.

pub(crate) async fn distribute_funds(
    contract: &OctaneFeeSplitter<WalletUnlocked>,
    amount: u64,
    recipientCount: u64,
) -> FuelCallResponse<()> {
    contract
        .methods()
        .distribute_funds(amount)
        .call()
        .await
        .unwrap()
}

For anyone looking for a solution, found this deep in the stack of fuels-rs…

.append_variable_outputs(1)

hey @xpluscal, did this work for you or are you still facing the issue?