How to save "amount" given to payable entrypoint. In case of error in execution. Perticularly If Errors like "concordium_cis2" ContractError is triggered

In case of multiple interaction of a user to same entry-point with same input parameter. I wish to revert the amount provided by user to the payable contract if their is multiple execution.

type MatchId = u64;

#[receive(

    contract = "cryptogammon",

    name = "enter_player_one",

    parameter = "MatchId",

    error = "CryptogammonError",

    mutable,

    enable_logger,

    payable

)]

fn enter_player_one<S: HasStateApi>(

    ctx: &impl HasReceiveContext,

    host: &mut impl HasHost<State<S>, StateApiType = S>,

    _amount: Amount,

    logger: &mut impl HasLogger,

) -> ContractResult<()> {

    // Parse the parameter.

    let params = ctx.parameter_cursor().get()?;

    let (state, _builder) = host.state_and_builder();

    let y = match ctx.sender() {

        Address::Account(a) => a,

        _ => return Err(CryptogammonError::Unauthorized),

    };

    // Check if the player is already in the match.

    state.enter_player_one(&y, &_amount, &params)?;

    logger.log(&CryptogammonEvent::Player1(Player1Event {

        player1: y,

        amount: _amount,

        match_id: params,

    }))?;

    return Ok(());

}

Implementation of helper function is

fn enter_player_one(

        &mut self,

        address: &AccountAddress,

        amount: &Amount,

        match_id: &u64,

    ) -> ContractResult<()> {

        let _fee = self.fee;

        let _amount = amount.micro_ccd();

        let _admin_fee = (_amount * (_fee)) / 10000;

        self.total_fees += _admin_fee;

        let player_amount = _amount - _admin_fee;

        let _amount = Amount::from_micro_ccd(player_amount);

        let match_instance = Match {

            player1: Option::Some(*address),

            player2: Option::None,

            amount: _amount,

            status: Status::Initialized,

            winner: Winner::None,

        };

        self.address_to_match_id

            .insert(*address, Option::Some(*match_id));

        self.match_id_to_match

            .entry(*match_id)

            .or_insert(match_instance);

        let mut instance = self.match_id_to_match.get_mut(match_id).unwrap();

        ensure_eq!(

            instance.status,

            Status::Initialized,

            CryptogammonError::Player1Error

        );

        if instance.player2 != Option::None {

            instance.player1 = Option::Some(*address);

            instance.status = Status::Ongoing;

            ensure_eq!(instance.amount, _amount, CryptogammonError::Player1Error);

        }

        Ok(())

    }

It’s a bit difficult to read, but it looks like your helper function never returns an error.

What I would recommend is that in case the player wants to play the second time you reject the transaction.

The way to do that is that in the helper function you check if the player is already playing, and if so return some Err(…).

Then you just propagate the error in the entrypoint (which you already do by using ?). All state changes and the amount transferred will be reverted when the contract rejects the input.

Does this make sense?

1 Like