Skip to content

Commit

Permalink
Created HeartToken & tested
Browse files Browse the repository at this point in the history
  • Loading branch information
GabeDottl committed Apr 20, 2021
1 parent 1e87916 commit 5d982ea
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 25 deletions.
28 changes: 19 additions & 9 deletions src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,16 @@ use std::convert::TryInto;
use std::mem::size_of;

pub enum HeartTokenInstruction {
/// Creates a
/// Creates a HeartToken.
///
///
/// Accounts expected:
///
/// 0. `[signer]` The account of the owner of the HeartToken.
/// 1. `[writable]` The HeartToken account, it will hold all necessary info about the HT.
/// 2. `[]` The rent sysvar
CreateHeartToken {
heart_token_owner: Pubkey, // Wallet of holder.
// heart_token_owner: Pubkey, // Wallet of holder.
// verified_credentials: Vec<VerifiedCredential>
},
// RecoverHeartToken {
Expand All @@ -37,9 +44,9 @@ impl HeartTokenInstruction {

Ok(match tag {
0 => {
let (pubkey, _rest) = Self::unpack_pubkey(rest)?;
// let (pubkey, _rest) = Self::unpack_pubkey(rest)?;
Self::CreateHeartToken {
heart_token_owner: pubkey,
// heart_token_owner: pubkey,
}
}
_ => return Err(HeartTokenError::InvalidInstruction.into()),
Expand All @@ -58,22 +65,25 @@ impl HeartTokenInstruction {
fn pack(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(size_of::<Self>());
match self {
&Self::CreateHeartToken { heart_token_owner } => {
&Self::CreateHeartToken { } => {
buf.push(0);
buf.extend_from_slice(heart_token_owner.as_ref());
// buf.extend_from_slice(heart_token_owner.as_ref());
}
}
buf
}

pub fn create_heart_token(
heart_token_program_id: &Pubkey,
heart_token_owner: &Pubkey
heart_token_owner: &Pubkey,
heart_token_account: &Pubkey
) -> Result<Instruction, ProgramError> {
let accounts = vec![
AccountMeta::new(*heart_token_owner, true),
AccountMeta::new_readonly(*heart_token_owner, true),
AccountMeta::new(*heart_token_account, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let data = HeartTokenInstruction::CreateHeartToken { heart_token_owner: *heart_token_owner }.pack();
let data = HeartTokenInstruction::CreateHeartToken { }.pack();
Ok(Instruction {
program_id: *heart_token_program_id,
accounts,
Expand Down
30 changes: 24 additions & 6 deletions src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use solana_program::{
sysvar::{rent::Rent, Sysvar},
};

use crate::{error::EscrowError, instruction::{EscrowInstruction, HeartTokenInstruction}, state::{HeartToken, Escrow}};
use crate::{error::{HeartTokenError, EscrowError}, instruction::{EscrowInstruction, HeartTokenInstruction}, state::{HeartToken, Escrow}};

pub struct Processor;
impl Processor {
Expand All @@ -22,24 +22,42 @@ impl Processor {
let instruction = HeartTokenInstruction::unpack(instruction_data)?;

match instruction {
HeartTokenInstruction::CreateHeartToken { heart_token_owner } => {
HeartTokenInstruction::CreateHeartToken { } => {
msg!("Instruction: CreateHeartToken");
Self::process_create_heart_token(accounts, heart_token_owner, program_id)
Self::process_create_heart_token(accounts, program_id)
}
}
}

fn process_create_heart_token(
accounts: &[AccountInfo],
heart_token_owner: Pubkey,
// heart_token_owner: Pubkey,
program_id: &Pubkey,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();

let initializer = next_account_info(account_info_iter)?;
if !initializer.is_signer {
let owner = next_account_info(account_info_iter)?;
if !owner.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}

let heart_token_account = next_account_info(account_info_iter)?;
let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;

if !rent.is_exempt(heart_token_account.lamports(), heart_token_account.data_len()) {
return Err(HeartTokenError::NotRentExempt.into());
}

let mut heart_token_info = HeartToken::unpack_unchecked(&heart_token_account.data.borrow())?;
if heart_token_info.is_initialized() {
return Err(ProgramError::AccountAlreadyInitialized);
}
heart_token_info.is_initialized = true;
heart_token_info.owner_pubkey = *owner.key;

// Write the heart_token info to the actual account.
HeartToken::pack(heart_token_info, &mut heart_token_account.data.borrow_mut())?;

Ok(())
}

Expand Down
54 changes: 52 additions & 2 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,60 @@ pub struct VerifiedCredential {

pub struct HeartToken {
// ID is account ID.
pub owner: Pubkey,
pub verifiedCredential: Vec<VerifiedCredential>
pub is_initialized: bool,
pub owner_pubkey: Pubkey,
// pub verifiedCredential: Vec<VerifiedCredential>
}


impl Sealed for HeartToken {}

impl IsInitialized for HeartToken {
fn is_initialized(&self) -> bool {
self.is_initialized
}
}

impl Pack for HeartToken {
const LEN: usize = 1 + 32;
fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
let src = array_ref![src, 0, HeartToken::LEN];
let (
is_initialized,
owner_pubkey,
) = array_refs![src, 1, 32];
let is_initialized = match is_initialized {
[0] => false,
[1] => true,
_ => return Err(ProgramError::InvalidAccountData),
};

Ok(HeartToken {
is_initialized,
owner_pubkey: Pubkey::new_from_array(*owner_pubkey),
})
}

fn pack_into_slice(&self, dst: &mut [u8]) {
let dst = array_mut_ref![dst, 0, HeartToken::LEN];
let (
is_initialized_dst,
owner_pubkey_dst,
) = mut_array_refs![dst, 1, 32];

let HeartToken {
is_initialized,
owner_pubkey
} = self;

is_initialized_dst[0] = *is_initialized as u8;
owner_pubkey_dst.copy_from_slice(owner_pubkey.as_ref());
}
}




pub struct Escrow {
pub is_initialized: bool,
pub initializer_pubkey: Pubkey,
Expand Down
68 changes: 60 additions & 8 deletions tests/functional_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use {
assert_matches::*,
hearttoken::{entrypoint::process_instruction, instruction::EscrowInstruction},
hearttoken::{entrypoint::process_instruction, instruction::{HeartTokenInstruction, EscrowInstruction}},
solana_program::{
borsh::get_packed_len,
instruction::{AccountMeta, Instruction},
Expand Down Expand Up @@ -76,9 +76,47 @@ impl AddPacked for ProgramTest {
// ProgramTest::new("spl_record", id(), processor!(process_instruction))
// }

#[tokio::test]
async fn test_create_heart_token() {
let account_alice = Keypair::new();
let account_heart_token = Keypair::new();
let mut program_test = ProgramTest::new(
"heart_token_test",
hearttoken::id(),
processor!(hearttoken::processor::Processor::process_heart_token),
);

// Start the test client
let (mut banks_client, payer, recent_blockhash) = program_test.start().await;

// Create HeartToken.
let account_space = spl_token::state::Mint::LEN;
let mut transaction = Transaction::new_with_payer(
&[
system_instruction::create_account(
&payer.pubkey(),
&account_heart_token.pubkey(),
1.max(Rent::default().minimum_balance(hearttoken::state::HeartToken::LEN)),
hearttoken::state::HeartToken::LEN as u64,
&hearttoken::id(),
),
HeartTokenInstruction::create_heart_token(
&hearttoken::id(),
&account_alice.pubkey(),
&account_heart_token.pubkey()
)
.unwrap(),
],
Some(&payer.pubkey()),
);
transaction.sign(&[&payer, &account_alice, &account_heart_token], recent_blockhash);
// Create mint:
assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
}

// Based on Record functional test: https://github.com/solana-labs/solana-program-library/blob/2b3f71ead5b81f4ea4a2fd3e4fe9583a6e39b6a4/record/program/tests/functional.rs
// Unisqap example test https://github.com/dzmitry-lahoda/solana-uniswap-example/blob/a8f108adefe8fa61a947d408a5ce0064b1d8c2df/tests/tests.rs
#[tokio::test]
// DO NOT COMMIT XXX: #[tokio::test]
async fn test_token() {
// Create a SPL token
// Create a main token account for Alice
Expand Down Expand Up @@ -195,18 +233,27 @@ async fn test_token() {
&authority.pubkey(),
&[&&authority.pubkey()],
100,
).unwrap()],
)
.unwrap()],
Some(&payer.pubkey()),
);
transaction.sign(&[&payer, &authority], recent_blockhash);
assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));

// Verify some data on Alice's temp account for sanity checking & fun.
let alice_account_temp_account = banks_client.get_account(account_alice_temp.pubkey()).await.unwrap().expect("Account unretrievable");
let alice_account_temp_account = banks_client
.get_account(account_alice_temp.pubkey())
.await
.unwrap()
.expect("Account unretrievable");
assert_eq!(alice_account_temp_account.owner, spl_token::id());
let internal_account = spl_token::state::Account::unpack(&alice_account_temp_account.data).unwrap();
let internal_account =
spl_token::state::Account::unpack(&alice_account_temp_account.data).unwrap();
assert_eq!(internal_account.owner, account_alice.pubkey());
assert_matches!(internal_account.state, spl_token::state::AccountState::Initialized);
assert_matches!(
internal_account.state,
spl_token::state::AccountState::Initialized
);

// // Create Escrow account
let mut transaction = Transaction::new_with_payer(
Expand Down Expand Up @@ -244,9 +291,14 @@ async fn test_token() {
// Create Alice's account with 1000 $A & temp-account for escrow.
assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
// Verify some data on Alice's temp account for sanity checking & fun.
let alice_account_temp_account = banks_client.get_account(account_alice_temp.pubkey()).await.unwrap().expect("Account unretrievable");
let alice_account_temp_account = banks_client
.get_account(account_alice_temp.pubkey())
.await
.unwrap()
.expect("Account unretrievable");
assert_eq!(alice_account_temp_account.owner, spl_token::id());
let internal_account = spl_token::state::Account::unpack(&alice_account_temp_account.data).unwrap();
let internal_account =
spl_token::state::Account::unpack(&alice_account_temp_account.data).unwrap();
let (pda, _bump_seed) = Pubkey::find_program_address(&[b"escrow"], &hearttoken::id());

// Ensure that the escrow account's ownership
Expand Down

0 comments on commit 5d982ea

Please sign in to comment.