Cannot borrow `*host` as mutable more than once at a time the temporary is part of an expression at the end of a block; consider forcing this temporary to be dropped sooner,

hi im getting this cannot borrow *host as mutable more than once at a time
the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner,


Would love if anyone could help me out with suggestions

What is the type of the funds field?

Can you also show the type of remove_and_get?

But I’m guessing something like

let mut state = host.state_mut();
let option_escrow = state.escrows.remove_and_get(&product_name).ok_or(MarketplaceError::EscrowNotFound)?;
drop(state);
host.invoke_transfer(&product.farmer, escrow.funds)

would work.

But if not if you can share more details of your Escrow type.

This is the escrow type #[derive(Serialize, SchemaType, Eq, PartialEq, PartialOrd, Clone)]
pub struct Escrow {
pub funds: Amount,
pub product: String,
pub buyer: AccountAddress,
} and smart contract state /// Smart contract state
#[derive(Serial, DeserialWithState)]
#[concordium(state_parameter = “S”)]
pub struct State<S = StateApi> {
pub product_listings: StateMap<String,ProductListing, S>,
pub orders: StateMap<String,Order,S>,
pub escrows: StateMap<String,Escrow,S>,
}

And this the ProductListing Struct
// Struct to represent a product listing.
#[derive(Serialize, Clone, SchemaType, PartialEq, Eq, Debug)]
pub struct ProductListing {
pub farmer: AccountAddress,
pub product: String,
pub price: Amount,
pub state: ProductState
}

Did the above suggestion fix your problem?

no it doesnt work , i cant seem to use the host.state_mut() more than once

You cannot use it more than once while the previous state is in scope.

You can work around this by either using drop explicitly or by making scopes, so I believe the following should work

let escrow = {
    let mut state = host.state_mut();
    state.escrows.remove_and_get(&product_name).ok_or(MarketplaceError::EscrowNotFound)?
};
host.invoke_transfer(&product.farmer, escrow.funds)

If not then please share a complete example that I can investigate.

#![cfg_attr(not(feature = "std"), no_std)]

//! # A Concordium V1 smart contract

use concordium_std::*;
use core::fmt::Debug;
use concordium_std::Amount;


/// Enum representing the possible states of a product
#[derive(Debug, Serialize, SchemaType, PartialEq, Eq, Clone)]
pub enum ProductState {
    Listed,
    Released,
    Confirmed,
    Cancelled,
}

// Struct to represent a product listing.
#[derive(Serialize, Clone, SchemaType, PartialEq, Eq, Debug)]
pub struct ProductListing {
    pub farmer: AccountAddress,
    pub product: String,
    pub price: Amount,
    pub state: ProductState
}


// Struct to represent an order.
#[derive( Serialize, SchemaType, Eq, PartialEq, PartialOrd, Clone)]
pub struct Order {
    pub buyer: AccountAddress,
    pub product: String,
    pub price: Amount,
}

#[derive(Serialize, SchemaType, Eq, PartialEq, PartialOrd, Clone)]
pub struct Escrow {
    pub funds: Amount,
    pub product: String,
    pub buyer: AccountAddress,
}


/// Smart contract state
#[derive(Serial, DeserialWithState)]
#[concordium(state_parameter = "S")]
pub struct State<S = StateApi>  {
    pub product_listings: StateMap<String,ProductListing, S>,
    pub orders: StateMap<String,Order,S>,
    pub escrows: StateMap<String,Escrow,S>,
}

/// Error types
#[derive(Debug, PartialEq, Eq, Clone, Reject, Serialize, SchemaType)]
pub enum MarketplaceError {
    ProductNotFound,
    OrderNotFound,
    OrderAlreadyExists,
    EscrowNotFound,
    InvalidProductState,
    InsufficientFunds,
    InvalidPrice,
    #[from(ParseError)]
    ParseParams,
    #[from(TransferError)]
    TransferError,
}

#[derive(Serialize, SchemaType)]
pub struct ListProductParameter{
    pub farmer: AccountAddress,
    pub product: String,
    pub price: Amount,
}

#[derive(Serialize, SchemaType)]
pub struct CancelProductParameter{
    pub product_name: String,
}

#[derive(Serialize, SchemaType)]
pub struct PlaceOrderParameter {
    pub product_name: String,
    pub buyer:AccountAddress,
    pub price: Amount
}

// Init function to initialize the marketplace state
#[init(contract = "gonana_marketplace")]
fn init(_ctx: &InitContext, state_builder: &mut StateBuilder) -> InitResult<State>{
    Ok(State { 
         product_listings: state_builder.new_map(),
        orders: state_builder.new_map(),
        escrows: state_builder.new_map(),
     })
}


/// Function to list a product in the marketplace
#[receive(contract = "gonana_marketplace", name = "list_product", parameter = "ListProductParameter", mutable )]
fn list_product(ctx: &ReceiveContext, host: &mut Host<State>) -> Result<(), MarketplaceError>{
    let parameter: ListProductParameter = ctx.parameter_cursor().get()?;
   
    // Check if the price is 0
    if parameter.price <= Amount::zero() {
        return Err(MarketplaceError::InvalidPrice);
    }

    // Check if the product name is empty
    if parameter.product.is_empty() {
        return Err(MarketplaceError::ParseParams);
    }

    let listing = ProductListing {
        farmer: parameter.farmer,
        product: parameter.product.clone(),
        price: parameter.price,
        state: ProductState::Listed,
    };
    
    host.state_mut().product_listings.insert(parameter.product.clone(), listing);

    Ok(()) 
}



/// Function to cancel or unlist a product
#[receive(contract = "gonana_marketplace", name = "cancel_product", parameter = "CancelProductParameter", mutable )]
fn cancel_product(ctx: &ReceiveContext, host: &mut Host<State>) -> Result<(), MarketplaceError>{
    let parameter: CancelProductParameter = ctx.parameter_cursor().get()?;
    let product_name = parameter.product_name.clone();

    // Check if the product is found
    if let Some(mut listing) = host.state_mut().product_listings.get_mut(&product_name) {
        // Check if the product is in a cancellable state
        match listing.state {
            ProductState::Listed | ProductState::Released => {
                listing.state = ProductState::Cancelled;
                Ok(())
            }
            _ => Err(MarketplaceError::InvalidProductState),
        }
    } else {
        // Product not found
        Err(MarketplaceError::ProductNotFound)
    }
}

#[receive(contract = "gonana_marketplace", name="place_order", parameter = "PlaceOrderParameter", mutable, payable)]
fn place_order(ctx: &ReceiveContext, host: &mut Host<State>, _amount: Amount) -> Result<(), MarketplaceError> {
    let parameter: PlaceOrderParameter = ctx.parameter_cursor().get()?;
    
        // Find the product by name
        let mut product = {
            let  state = host.state_mut();
            state.product_listings.remove_and_get(&parameter.product_name).ok_or(MarketplaceError::ProductNotFound)?
        };
       

        // Ensure that the product is in a valid state for placing an order
    ensure!(product.state == ProductState::Listed, MarketplaceError::InvalidProductState);
    
     // Create an order
     let order = Order {
        buyer: parameter.buyer,
        product: product.product.clone(),
        price: parameter.price,
    };

   // Deduct the order amount from the buyer's account
   host.invoke_transfer(&product.farmer, parameter.price)?;

   // Insert the order and update the product state to Released in one statement
   if host.state_mut().orders.insert(parameter.product_name.clone(), order).is_none() {
    // If the insert is successful, update the product state
    product.state = ProductState::Released;
    Ok(())
} else {
    // Handle the case when the order already exists
    Err(MarketplaceError::OrderAlreadyExists)
}

}




/// Function to confirm an escrow in the marketplace
#[receive(contract = "gonana_marketplace", name = "confirm_escrow", parameter = "CancelProductParameter", mutable)]
fn confirm_escrow(ctx: &ReceiveContext, host: &mut Host<State>) -> Result<(), MarketplaceError> {
    let parameter: CancelProductParameter = ctx.parameter_cursor().get()?;
    let product_name = parameter.product_name.clone();

     // Find the product by name
     let mut product = {
        let state = host.state_mut();
        state.product_listings.remove_and_get(&product_name).ok_or(MarketplaceError::ProductNotFound)?
    };

     // Ensure that the product is in a valid state for confirming the escrow
     ensure!(product.state == ProductState::Released, MarketplaceError::InvalidProductState);

   // Remove the escrow and get its value
   if let Some(escrow) = host.state_mut().escrows.remove_and_get(&product_name) {
    // Transfer funds
    host.invoke_transfer(&product.farmer, escrow.funds)?;

    // It's important to call `Deletable::delete` on the value
    escrow.delete();
    // If the removal is successful, update the product state
    product.state = ProductState::Confirmed;

    Ok(())
} else {
    // Escrow not found
    Err(MarketplaceError::EscrowNotFound)
}
}


// // View function to get all product listings
#[receive(contract = "gonana_marketplace", name = "view_product_listings", return_value = "Vec<ProductListing>")]
fn view_product_listings(_ctx: &ReceiveContext, host: &Host<State>) -> ReceiveResult<Vec<ProductListing>> {
    let state = host.state();
    let product_listings: Vec<ProductListing> = state.product_listings.iter().map(|(_, product)| product.clone()).collect();
    Ok(product_listings)
}

// // View function to get all orders
#[receive(contract = "gonana_marketplace", name = "view_orders", return_value = "Vec<Order>")]
fn view_orders(_ctx: &ReceiveContext, host: &Host<State>) -> ReceiveResult<Vec<Order>> {
    let state = host.state();
    let orders: Vec<Order> = state.orders.iter().map(|(_, order)| order.clone()).collect();
    Ok(orders)
}

// // View function to get all escrows
#[receive(contract = "gonana_marketplace", name = "view_escrows", return_value = "Vec<Escrow>")]
fn view_escrows(_ctx: &ReceiveContext, host: &Host<State>) -> ReceiveResult<Vec<Escrow>> {
    let state = host.state();
    let escrows: Vec<Escrow> = state.escrows.iter().map(|(_, escrow)| escrow.clone()).collect();
    Ok(escrows)
}

This is the complete example , the placeOrder and confirm_escrow functions doesn’t work because I need to look up the product_name from the state but when I do that I cant use the host anymore because of borrowing rules could you help me with suggestions on how I could implement this

So I don’t really understand the intent, but I’m guessing that for place_order you want something like this


#[receive(
    contract = "gonana_marketplace",
    name = "place_order",
    parameter = "PlaceOrderParameter",
    mutable,
    payable
)]
fn place_order(
    ctx: &ReceiveContext,
    host: &mut Host<State>,
    _amount: Amount,
) -> Result<(), MarketplaceError> {
    let parameter: PlaceOrderParameter = ctx.parameter_cursor().get()?;

    let state_mut = host.state_mut();

    // Find the product by name
    let mut product = 
        state_mut
        .product_listings
        .get_mut(&parameter.product_name)
        .ok_or(MarketplaceError::ProductNotFound)?;

    // Ensure that the product is in a valid state for placing an order
    ensure!(product.state == ProductState::Listed, MarketplaceError::InvalidProductState);

    // Create an order
    let order = Order {
        buyer:   parameter.buyer,
        product: product.product.clone(),
        price:   parameter.price,
    };

    // Insert the order and update the product state to Released in one statement
    ensure!(state_mut.orders.insert(parameter.product_name.clone(), order).is_none(), MarketplaceError::OrderAlreadyExists);
    // If the insert is successful, update the product state
    product.state = ProductState::Released;
    let farmer = product.farmer;
    drop(product);
    // Deduct the order amount from the buyer's account
    host.invoke_transfer(&farmer, parameter.price)?;
    Ok(())
}

For confirm_escrow I believe what you want is


/// Function to confirm an escrow in the marketplace
#[receive(
    contract = "gonana_marketplace",
    name = "confirm_escrow",
    parameter = "CancelProductParameter",
    mutable
)]
fn confirm_escrow(ctx: &ReceiveContext, host: &mut Host<State>) -> Result<(), MarketplaceError> {
    let parameter: CancelProductParameter = ctx.parameter_cursor().get()?;
    let product_name = parameter.product_name.clone();

    let state_mut = host.state_mut();
    // Find the product by name
    let mut product = 
        state_mut
            .product_listings
            .get_mut(&product_name)
            .ok_or(MarketplaceError::ProductNotFound)?;

    // Ensure that the product is in a valid state for confirming the escrow
    ensure!(product.state == ProductState::Released, MarketplaceError::InvalidProductState);

    let farmer = product.farmer;
    product.state = ProductState::Confirmed;
    drop(product);
    // Remove the escrow and get its value
    if let Some(escrow) = host.state_mut().escrows.remove_and_get(&product_name) {
        // Transfer funds
        host.invoke_transfer(&farmer, escrow.funds)?;

        // It's important to call `Deletable::delete` on the value
        escrow.delete();

        Ok(())
    } else {
        // Escrow not found
        Err(MarketplaceError::EscrowNotFound)
    }
}

Note that we update the product state here before checking the escrow, but that is fine since we’ll fail if the escrow lookup or transfer fail, and so the state is rolled back.

Ok this works.
However i discovered that my implementation for the escrow wouldn’t work, want i want to achieve is when placing an order for a product an escrow should be created
Which means i might have to create another smart contract for the escrow and use host.invoke_contract
Is there a way to call the init of the escrow contract in the place_order function

You cannot create an instance during a contract call if that is what you are asking.

You have to do that in a separate transaction. Why does each escrow need a separate instance?

I’m just looking for the best way to ensure when a product is ordered an escrow for that product is also created and the funds transferred to that escrow
Till it is confirmed
@abizjak