Anchor has macros that make it easier to write and deploy Solana contracts.

The following is a simple comparision b/w a native contract and a solana contract

Native contract

use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    entrypoint,
    account_info::{next_account_info, AccountInfo},
    entrypoint::ProgramResult,
    pubkey::Pubkey,
    program_error::ProgramError,
    msg,
};

#[derive(BorshSerialize, BorshDeserialize)]
struct CounterState {
    count: u32,
}

#[derive(BorshSerialize, BorshDeserialize)]
enum CounterInstruction {
    Double,
    Half,
}

entrypoint!(process_instruction);

fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    let mut iter = accounts.iter();
    let data_account = next_account_info(&mut iter)?;

    if !data_account.is_signer {
        return Err(ProgramError::MissingRequiredSignature);
    }

    match instruction {
        CounterInstruction::Double => {
            msg!("Doubling counter");
            let mut counter_state = CounterState::try_from_slice(&data_account.data.borrow())?;
            counter_state.count = counter_state.count.saturating_mul(2);
            counter_state.serialize(&mut *data_account.data.borrow_mut())?;
        }
        CounterInstruction::Half => {
            msg!("Halving counter");
            let mut counter_state = CounterState::try_from_slice(&data_account.data.borrow())?;
            counter_state.count = counter_state.count / 2;
            counter_state.serialize(&mut *data_account.data.borrow_mut())?;
        }
    }

    Ok(())
}

Anchor contract

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkgPp8eEB2hdD");

#[program]
pub mod anchor_contract {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>, value: u64) -> Result<()> {
        let data_account = &mut ctx.accounts.data_account;
        data_account.value = value;
        Ok(())
    }

    pub fn double(ctx: Context<Modify>) -> Result<()> {
        let data_account = &mut ctx.accounts.data_account;
        data_account.value *= 2;
        Ok(())
    }

    pub fn half(ctx: Context<Modify>) -> Result<()> {
        let data_account = &mut ctx.accounts.data_account;
        data_account.value /= 2;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 8 + 8)] // 8 for discriminator, 8 for u64
    pub data_account: Account<'info, DataAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Modify<'info> {
    #[account(mut)]
    pub data_account: Account<'info, DataAccount>,
}

#[account]
pub struct DataAccount {
    pub value: u64,
}

What is the one big difference b/w both of these contracts?

  1. Space that the integer takes
  2. Native contract expects data account to be there already (initialized on the client)