Exercise – Instruction Handling

Follow the video!
https://academy.moralis.io/lessons/exercise-instruction-handling

Let us know if you have any questions.

The easiest way to handle the different counting operations using the Instructions enum would be to use a match statement which handles the operations depending on the value passed through from the client.

So, before where we had the if-else statements in the contract:

/src/program-rust/src/lib.rs

    if greeting_account.counter == 0 {
        greeting_account.counter += 1;
    } else {
        greeting_account.counter *= 2;
    }

We can now use the following code to handle the same thing:

    let instruction = Instructions::unpack(instruction_data)?;

    match instruction {
        Instructions::Reset => {
            greeting_account.counter = 0;
        }
        Instructions::Add(value) => {
            greeting_account.counter += value;
        }
        Instructions::Times(value) => {
            greeting_account.counter *= value;
        }
    }

This works assuming you have the following, prior to entering into the contract:

#[derive(Debug)]
pub enum Instructions {
    Reset,
    Add(u32),
    Times(u32)
}

impl Instructions {
    pub fn unpack(data: &[u8]) -> Result<Self, ProgramError> {
        if data.len() == 1 && data[0] == 0 {
            return Ok(Instructions::Reset)
        } else if data.len() == 5 && data[0] <= 2 && data[0] >= 1 {
            let value = u32::from_le_bytes([data[1], data[2], data[3], data[4]]);
            if data[0] == 1 {
                return Ok(Instructions::Add(value));
            } else /* if data[0] == 2 */ {
                return Ok(Instructions::Times(value));
            }
        }

        return Err(ProgramError::InvalidInstructionData)
    }
}

Now, when creating a TransactionInstruction when loading a instruction from the sayHello() function from the client, as part of the payload, you can use multiple different types of data, and the contract will handle it accordingly.

Below, you can see from lines 6-9 (inclusive), all are handled properly by the smart contract (by their attached comment):

/src/client/hello_world.ts

export async function sayHello(): Promise<void> {
  console.log('Saying hello to', greetedPubkey.toBase58());
  const instruction = new TransactionInstruction({
    keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}],
    programId,
    // data: Buffer.from('00', 'hex'), // Reset
    // data: Buffer.from('013b010000', 'hex'), // Add
    // data: Buffer.from('023a010000', 'hex'), // Times
    // data: Buffer.from('0001010000', 'hex'), // Error
  });
  await sendAndConfirmTransaction(
    connection,
    new Transaction().add(instruction),
    [payer],
  );
}
2 Likes

/src/program-rust/src/lib.rs

    let instruction = Instructions::unpack(instruction_data);
    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    let instruction_unwrap = instruction.unwrap();
    match instruction_unwrap {
        Instructions::Reset => {greeting_account.counter = 0;}
        Instructions::Add(value) => {greeting_account.counter += value;}
        Instructions::Times(value) => {greeting_account.counter *= value;}
    }

For instruction handling I changed from the if statement here:
src/program-rust/src/lib.rs
pub fn process_instructions()

...
let instruction = Instructions::unpack(instruction_data)?;
msg!("Unpacked instruction: {:?}", instruction);

// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;

if greeting_account.counter == 0 {
    greeting_account.counter += 1;  // add
} else {
    greeting_account.counter *= 2;  // times
}

// greeting_account.counter = 0;  // reset

greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;

msg!("Greeted {} time(s)!", greeting_account.counter);

to a match statement here

...
let instruction = Instructions::unpack(instruction_data)?;
msg!("Unpacked instruction: {:?}", instruction);

// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;

match instruction {
        Instructions::Reset => {
            greeting_account.counter = 0;
        }
        Instructions::Add(value) => {
            greeting_account.counter += value;
        }
        Instructions::Times(value) => {
            greeting_account.counter *= value;
        }
    }

greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;

msg!("Greeted {} time(s)!", greeting_account.counter);
1 Like

Hi, this is my code:

lib.rs

   let _instructios = Instruction::unpack(instruction_data)?;
   msg!("Instreuction {:?}", _instructios);
   let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;

   match _instructios {
        Instruction::Reset => {
            greeting_account.counter = 0;
            msg!("Reset Counter!");
        }
        Instruction::Add(value) => {
            greeting_account.counter += value;
            msg!(
                "Add => {} + {} = {}",
                value,
                (greeting_account.counter - value),
                greeting_account.counter
            );
        }
        Instruction::Times(value) => {
            greeting_account.counter *= value;
            msg!(
                "Add => {} * {} = {}",
                value,
                (greeting_account.counter / value),
                greeting_account.counter
            );
        }
        _ => {
            msg!("operation not coplete");
        }
    }
    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;

 

    Ok(())
}

1 Like