I have an on-chain program game where players can execute a "move" instruction. I would like to charge 0.0001 SOL for making a "move". What is the proper way of charging players the in-game fees?

I've come up with 2 possible solutions:

  1. On the frontend build a transaction with the 1st instruction = SystemProgram.transfer(from = player, to = game_account, amount = 0.0001) and the 2nd instruction = Game.move(...). The Program uses the instruction introspection to check that the 1st instruction is the transfer to the game_account with a required sum and only then executes the "move".

  2. Use PDAs for every player PDA("game", player_pubkey). The player needs to deposit 0.0001 SOL to his PDA as the first instruction. Then the Program checks that the player's PDA is enough to make a "move" and withdraws money from it.

I came from Ethereum background where a smart-contract's method can be called and ETH attached to the transaction. In Solana this is not possible because SOLs belong to a native program and need an additional instruction.

Any reference to Docs or best-practice will be appreciated!

2 Answers 2


You can do this out-of-the-box with Solana.

In the protocol, you can call a Cross Program Invocation to transfer Sol from the user's wallet to the destination (same idea as your front end concept, but do it in Rust using the System Program), then execute the "move" as usual. If the transfer fails, the whole tx fails, and the move is not executed.

Users will sign the tx with their wallet, which approves the transfer. Wallets like Phantom will show that the tx will cost the user .0001 Sol to execute when they go to approve it.

  • Thank you! I was thinking of a CPI call to System::transfer but thought it wouldn't work because "Program may change only program-owned accounts" rule. Indeed, a CPI allows to change the "program ID" effectively allowing to call the SystemProgram with the same signers. Aug 18, 2022 at 9:07

If you are using a PDA for every player, your program can deduct the fees from a PDA or other program owned account and deposit into your collection/service account without the need to create a series of separate instructions. You also wouldn't need to invoke the system_transfer.

So, you could extract a fee for any, all game moves. Here is a small snippet where from_account is the PDA with lamports you want to transfer out from and to_account is the destination for the lamports. amount_of_lamports is what it says it is:

/// Transfers lamports from one account (must be program owned)
/// to another account. The recipient can by any account
fn transfer_service_fee_lamports(
    from_account: &AccountInfo,
    to_account: &AccountInfo,
    amount_of_lamports: u64,
) -> ProgramResult {
    // Does the from account have enough lamports to transfer?
    if **from_account.try_borrow_lamports()? < amount_of_lamports {
        return Err(CustomError::InsufficientFundsForTransaction.into());
    // Debit from_account and credit to_account
    **from_account.try_borrow_mut_lamports()? -= amount_of_lamports;
    **to_account.try_borrow_mut_lamports()? += amount_of_lamports;

Full example is what I wrote in the Solana Cookbook

  • Thank you, Frank! I've read that awesome Cookbook's article but didn't recognise it can be used for game payments. I would only like to clarify "without the need to create a series of separate instructions". The players would need to prior transfer SOL to their PDA before making "moves". Those can be done in batches, though, for instance for 100 "move"s onwards. Aug 17, 2022 at 10:19
  • I'm wondering about the 1st approach using the instruction introspection. How secure and viable is it? An advantage of this, afaik, is that the Program and client won't need to handle PDAs at all. Aug 17, 2022 at 10:26
  • @SergeiPatrikeev - Ahh, yes. Mea culpa.... I.presumed that players funded their PDA just by participating in the game.
    – Frank C.
    Aug 17, 2022 at 23:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.