I am creating a simple Android application that is interfaced with a Counter program deployed on the Devnet Cluster.
Here is the Counter program
use anchor_lang::prelude::*;
declare_id!("AR14fj2ohwVkbZuAWhiox87EXGvB8Q1V1LukSXCqQWTN");
pub const ANCHOR_DISCRIMINATOR_SIZE: usize = 8;
#[program]
pub mod counter_dapp {
use super::*;
/// Initialises a Counter account
pub fn initialise_counter(
ctx: Context<InitialiseCounter>
) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count = 0;
Ok(())
}
/// Increment the Counter.count value
///
/// # Arguments
/// * `amount` - An unsigned integer to increment the counter value by
pub fn increment_counter(
ctx: Context<IncrementCounter>,
amount: u64
) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count += amount;
Ok(())
}
}
#[derive(Accounts)]
pub struct IncrementCounter<'info> {
#[account(
mut,
seeds = [b"counter"],
bump
)]
pub counter: Account<'info, Counter>,
pub signer: Signer<'info>
}
#[derive(Accounts)]
pub struct InitialiseCounter<'info> {
#[account(mut)]
pub signer: Signer<'info>,
#[account(
init,
payer = signer,
space = ANCHOR_DISCRIMINATOR_SIZE + Counter::INIT_SPACE,
seeds = [b"counter"],
bump
)]
pub counter: Account<'info, Counter>,
pub system_program: Program<'info, System>
}
#[account]
#[derive(InitSpace)]
pub struct Counter {
pub count: u64
}
Here is the Android Kotlin code within the ViewModel.
import com.funkatronics.kborsh.Borsh
import com.hansel.counterdapp.counter.domain.model.ArgsIncrementCounter
import com.hansel.counterdapp.counter.domain.model.Counter
import com.portto.solana.web3.Connection
import com.portto.solana.web3.PublicKey
import com.portto.solana.web3.rpc.types.config.Commitment
import com.portto.solana.web3.util.Cluster
import com.solana.mobilewalletadapter.clientlib.ActivityResultSender
import com.solana.mobilewalletadapter.clientlib.MobileWalletAdapter
import com.solana.mobilewalletadapter.clientlib.RpcCluster
import com.solana.mobilewalletadapter.clientlib.TransactionResult
import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClient
import com.solana.mobilewalletadapter.clientlib.successPayload
import com.solana.programs.SystemProgram
import com.solana.publickey.ProgramDerivedAddress
import com.solana.publickey.SolanaPublicKey
import com.solana.serialization.AnchorInstructionSerializer
import com.solana.transaction.AccountMeta
import com.solana.transaction.Message
import com.solana.transaction.Transaction
import com.solana.transaction.TransactionInstruction
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.serialization.decodeFromByteArray
import org.bitcoinj.core.Base58
import javax.inject.Inject
fun initialiseCounter(
activityResultSender: ActivityResultSender
) = viewModelScope.launch(Dispatchers.IO) {
try {
if (_counterState.value.authToken.isEmpty()) connectToWallet(activityResultSender)
val userWalletAddress = SolanaPublicKey.from(_counterState.value.userAddress)
Log.d("INITIALISE", "Wallet Address: $userWalletAddress")
val blockhash = rpcClient.getLatestBlockhash(Commitment.CONFIRMED)
val seeds = listOf("counter".encodeToByteArray())
val counterAccountPDA =
ProgramDerivedAddress.find(seeds, counterProgramId).getOrNull()!!
val txInstruction = TransactionInstruction(
counterProgramId,
listOf(
AccountMeta(userWalletAddress, isSigner = true, isWritable = true),
AccountMeta(counterAccountPDA, isSigner = false, isWritable = true),
AccountMeta(SystemProgram.PROGRAM_ID, isSigner = false, isWritable = false)
),
ByteArray(0)
)
val txMessage = Message.Builder()
.addInstruction(txInstruction)
.setRecentBlockhash(blockhash!!)
.build()
val unsignedTx = Transaction(txMessage)
val serialisedUnsignedTx = unsignedTx.serialize()
val result = walletAdapter.transact(activityResultSender) { authResult ->
reauthorize(
identityUri = Uri.parse(IDENTITY_URI),
iconUri = Uri.parse(ICON_URI),
identityName = IDENTITY_APP_NAME,
authToken = _counterState.value.authToken
)
signAndSendTransactions(arrayOf(serialisedUnsignedTx))
}
handleTransactionResult(result)
getCounterValue()
} catch (e: Exception) {
Log.e("FAILED_INITIALISATION", "Exception in initialising counter: ${e.message}")
}
}
This instruction will create a new Counter Account. I think I have done everything necessary when creating a Transaction. I have included the three accounts listed in the Account Context within the program; the signer, the PDA of the account being created and the System program. Is this the correct approach? Additionally, the mobile wallet is opened successfully but the transaction fails. What could I be doing wrong?