4

I am trying to write a client code in typescript for a contract written in vanilla rust. I am unable to serialize an instruction such that it can be deserialized by the program

#[derive(Debug, BorshDeserialize, BorshSerialize)]

pub enum WalletInstruction {
    /// Initialize a Personal Savings Wallet
    ///
    /// Passed accounts:
    ///
    /// (1) Wallet account
    /// (2) Vault accounts
    /// (3) Authority
    /// (4) Rent sysvar
    /// (5) System program
    Initialize,
    /// Deposit
    ///
    /// Passed accounts:
    ///
    /// (1) Wallet account
    /// (2) Vault accounts
    /// (3) Money Source
    Deposit { amount: u64 },
    /// Withdraw from Wallet
    ///
    /// Passed accounts:
    ///
    /// (1) Wallet account
    /// (2) Vault accounts
    /// (3) Authority
    /// (4) Target Wallet account
    Withdraw { amount: u64 },
}

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    mut instruction_data: &[u8],
) -> ProgramResult {
    match WalletInstruction::deserialize(&mut instruction_data)? {
        WalletInstruction::Initialize => initialize(program_id, accounts),
        WalletInstruction::Deposit { amount } => deposit(program_id, accounts, amount),
        WalletInstruction::Withdraw { amount } => withdraw(program_id, accounts, amount),
    }
}

In my client side, I have this

class Assignable {
    constructor(properties) {
        Object.keys(properties).map((key) => {
            return (this[key] = properties[key]);
        });
    };
};

class Deposit extends Assignable {
    toBuffer() {
        return Buffer.from(borsh.serialize(DepositSchema, this));
    }
}

const DepositSchema = new Map([
    [
        Deposit, {
            kind: 'struct',
            fields: [
                ['amount', 'u8'],
            ]
        }
    ]
]);


const DepositInstructionData = new Deposit({
    amount: deposit_amount,
})
let ix = new TransactionInstruction({
    keys:[
        {pubkey: wallet_adddress, isSigner: false, isWritable: true},// Wallet Account
        {pubkey: vault_adddress, isSigner: false, isWritable: true},// Vault Account
        {pubkey: rich_boi.publicKey, isSigner: true, isWritable: true},// Rich Boi Account
        {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}// System Program
    ],
    programId: program.publicKey,
    data: DepositInstructionData.toBuffer(),
})

When i execute the transaction, i am getting this error

Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Failed to serialize or deserialize account data: Unknown

3 Answers 3

5

First correct your schema declaration:

// Instruction variant indexes
enum InstructionVariant {
    InitializeAccount = 0,
    Deposit,
    Withdraw,
}

const DepositSchema = new Map([
    [
        Deposit, {
            kind: 'struct',
            fields: [
                ['id', 'u8'],   // This is for the instruction invariant
                ['amount', 'u64'],
            ]
        }
    ]
]);

Then serialize a meaningful instance of the Deposit, for example:

    // Construct the payload
    const deposit = new Deposit({
        id: InstructionVariant.Deposit,
        amount: 200,
    });

    // Serialize the payload
    const depositSerBuf = Buffer.from(serialize(DepositSchema, deposit));

Construct your transaction:

let ix = new TransactionInstruction({
    keys:[
        {pubkey: wallet_adddress, isSigner: false, isWritable: true},// Wallet Account
        {pubkey: vault_adddress, isSigner: false, isWritable: true},// Vault Account
        {pubkey: rich_boi.publicKey, isSigner: true, isWritable: true},// Rich Boi Account
        {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}// System Program
    ],
    programId: program.publicKey,
    data: depositSerBuf,
})
2

amount is declared as u64 in the Rust program, but only u8 on the client side schema. All type declarations must match in both size and layout on both sides

2
  • 1
    Isn't the index of the instruction also missing from this code? Commented Jul 21, 2022 at 5:32
  • well... the entire transaction construction and broadcast is missing, yet they got a simulation error. presumably there's more missing :shrug:
    – trent.sol
    Commented Jul 21, 2022 at 5:56
2

this is another solution that worked for me

function DepositInstructionData(deposit_amount): Buffer {
    const dataLayout = BufferLayout.struct([
        BufferLayout.u8('instruction'),
        BufferLayout.nu64('amount'),
      ]);

      const data = Buffer.alloc(dataLayout.span);
      dataLayout.encode({
        instruction: 1,
        amount: deposit_amount,
      }, data);
    
      return data;
}
   
let deposit_amount = 200;
let ix = new TransactionInstruction({
            keys:[
                {pubkey: wallet_adddress, isSigner: false, isWritable: true},// Wallet Account
                {pubkey: vault_adddress, isSigner: false, isWritable: true},// Vault Account
                {pubkey: rich_boi.publicKey, isSigner: true, isWritable: true},// Rich Boi Account
                {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}// System Program
            ],
            programId: program.publicKey,
            data: DepositInstructionData(deposit_amount),
        })

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.