Skip to content

Commit

Permalink
Basic transfer implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
danenbm committed Feb 18, 2024
1 parent 3ccc714 commit 8cdf58f
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 92 deletions.
8 changes: 8 additions & 0 deletions programs/mpl-asset/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ pub enum MplAssetError {
/// 4 - Plugin not found
#[error("Plugin not found")]
PluginNotFound,

/// 5 - Incorrect account
#[error("Incorrect account")]
IncorrectAccount,

/// 5 - Provided data does not match asset hash.
#[error("Incorrect asset hash")]
IncorrectAssetHash,
}

impl PrintProgramError for MplAssetError {
Expand Down
3 changes: 2 additions & 1 deletion programs/mpl-asset/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ pub enum MplAssetInstruction {
#[account(4, optional, name="log_wrapper", desc = "The SPL Noop Program")]
Burn(BurnArgs),

// danenbm working on
// Transfer an asset.
// danenbm WIP
#[account(0, writable, name="asset_address", desc = "The address of the asset")]
#[account(1, optional, name="collection", desc = "The collection to which the asset belongs")]
#[account(2, signer, name="authority", desc = "The owner or delegate of the asset")]
Expand Down
5 changes: 0 additions & 5 deletions programs/mpl-asset/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ pub(crate) use compress::*;
mod decompress;
pub(crate) use decompress::*;

//TODO: Implement this struct
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
pub struct CompressionProof {}

pub fn process_instruction<'a>(
_program_id: &Pubkey,
accounts: &'a [AccountInfo<'a>],
Expand Down
114 changes: 37 additions & 77 deletions programs/mpl-asset/src/processor/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,103 +2,63 @@ use borsh::{BorshDeserialize, BorshSerialize};
use mpl_utils::assert_signer;
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, program::invoke,
program_memory::sol_memcpy, system_instruction, system_program,
program_memory::sol_memcpy,
};

use crate::{
error::MplAssetError,
instruction::accounts::TransferAccounts,
state::{Asset, Compressible, DataState, HashedAsset, Key},
utils::DataBlob,
state::{Asset, Compressible, CompressionProof, HashedAsset, Key},
utils::{load_key, DataBlob},
};

#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
pub struct TransferArgs {}

// // danenbm working on
// #[account(0, writable, name="asset_address", desc = "The address of the asset")]
// #[account(1, optional, name="collection", desc = "The collection to which the asset belongs")]
// #[account(2, signer, name="authority", desc = "The owner or delegate of the asset")]
// #[account(3, optional, writable, signer, name="payer", desc = "The account paying for the storage fees")]
// #[account(4, name="new_owner", desc = "The new owner to which to transfer the asset")]
// #[account(5, optional, name="log_wrapper", desc = "The SPL Noop Program")]
// Transfer(TransferArgs),
pub struct TransferArgs {
compression_proof: CompressionProof,
}

pub(crate) fn transfer<'a>(accounts: &'a [AccountInfo<'a>], args: TransferArgs) -> ProgramResult {
// Accounts.
let ctx = TransferAccounts::context(accounts)?;

// Guards.
assert_signer(ctx.accounts.authority)?;

if let Some(payer) = ctx.accounts.payer {
assert_signer(payer)?;
}

let asset = Asset::load(ctx.accounts.asset_address, 0)?;

asset.owner = ctx.accounts.new_owner.key();

// if *ctx.accounts.system_program.key != system_program::id() {
// return Err(MplAssetError::InvalidSystemProgram.into());
// }

// let updated_asset = Asset {
// key: Key::Asset,
// update_authority: *ctx
// .accounts
// .update_authority
// .unwrap_or(ctx.accounts.payer)
// .key,
// owner: *ctx
// .accounts
// .owner
// .unwrap_or(ctx.accounts.update_authority.unwrap_or(ctx.accounts.payer))
// .key,
// name: args.name,
// uri: args.uri,
// };

// let serialized_data = new_asset.try_to_vec()?;

// let serialized_data = match args.data_state {
// DataState::AccountState => serialized_data,
// DataState::LedgerState => {
// invoke(&spl_noop::instruction(serialized_data.clone()), &[])?;

// let hashed_asset = HashedAsset {
// key: Key::HashedAsset,
// hash: new_asset.hash()?,
// };

// hashed_asset.try_to_vec()?
// }
// };

//let lamports = rent.minimum_balance(serialized_data.len());

// CPI to the System Program.
// invoke(
// &system_instruction::create_account(
// ctx.accounts.payer.key,
// ctx.accounts.asset_address.key,
// lamports,
// serialized_data.len() as u64,
// &crate::id(),
// ),
// &[
// ctx.accounts.payer.clone(),
// ctx.accounts.asset_address.clone(),
// ctx.accounts.system_program.clone(),
// ],
// )?;

// sol_memcpy(
// &mut ctx.accounts.asset_address.try_borrow_mut_data()?,
// &serialized_data,
// serialized_data.len(),
// );
let serialized_data = match load_key(ctx.accounts.asset_address, 0)? {
Key::HashedAsset => {
// Check that arguments passed in result in on-chain hash.
let mut asset = Asset::from(args.compression_proof);
let args_asset_hash = asset.hash()?;
let current_account_hash = HashedAsset::load(ctx.accounts.asset_address, 0)?.hash;
if args_asset_hash != current_account_hash {
return Err(MplAssetError::IncorrectAssetHash.into());
}

// Update owner and send Noop instruction.
asset.owner = *ctx.accounts.new_owner.key;
let serialized_data = asset.try_to_vec()?;
invoke(&spl_noop::instruction(serialized_data), &[])?;

// Make a new hashed asset with updated owner.
HashedAsset::new(asset.hash()?).try_to_vec()?
}
Key::Asset => {
let mut asset = Asset::load(ctx.accounts.asset_address, 0)?;
asset.owner = *ctx.accounts.new_owner.key;
asset.try_to_vec()?
}
_ => return Err(MplAssetError::IncorrectAccount.into()),
};

sol_memcpy(
&mut ctx.accounts.asset_address.try_borrow_mut_data()?,
&serialized_data,
serialized_data.len(),
);

Ok(())
}
19 changes: 14 additions & 5 deletions programs/mpl-asset/src/state/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize};
use shank::ShankAccount;
use solana_program::{keccak, program_error::ProgramError, pubkey::Pubkey};

use crate::{state::Key, utils::DataBlob};
use crate::{
state::{CompressionProof, Key},
utils::DataBlob,
};

use super::Compressible;

Expand Down Expand Up @@ -41,8 +44,14 @@ impl DataBlob for Asset {
}
}

#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, ShankAccount)]
pub struct HashedAsset {
pub key: Key, //1
pub hash: [u8; 32], //32
impl From<CompressionProof> for Asset {
fn from(compression_proof: CompressionProof) -> Self {
Self {
key: Self::key(),
update_authority: compression_proof.update_authority,
owner: compression_proof.owner,
name: compression_proof.name,
uri: compression_proof.uri,
}
}
}
35 changes: 35 additions & 0 deletions programs/mpl-asset/src/state/hashed_asset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use borsh::{BorshDeserialize, BorshSerialize};
use shank::ShankAccount;

use crate::{state::Key, utils::DataBlob};

#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, ShankAccount, PartialEq, Eq)]
pub struct HashedAsset {
pub key: Key, //1
pub hash: [u8; 32], //32
}

impl HashedAsset {
pub const LENGTH: usize = 1 + 32;

pub fn new(hash: [u8; 32]) -> Self {
Self {
key: Key::HashedAsset,
hash,
}
}
}

impl DataBlob for HashedAsset {
fn get_initial_size() -> usize {
HashedAsset::LENGTH
}

fn get_size(&self) -> usize {
HashedAsset::LENGTH
}

fn key() -> Key {
Key::HashedAsset
}
}
14 changes: 14 additions & 0 deletions programs/mpl-asset/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod asset;
pub use asset::*;

mod hashed_asset;
pub use hashed_asset::*;

mod plugin_header;
use num_derive::FromPrimitive;
pub use plugin_header::*;
Expand Down Expand Up @@ -62,3 +65,14 @@ pub enum MigrationLevel {
MigrateOnly,
MigrateAndBurn,
}

//TODO: Implement this struct
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
pub struct CompressionProof {
pub key: Key, //1
pub update_authority: Pubkey, //32
pub owner: Pubkey, //32
pub name: String, //4
pub uri: String, //4
}
3 changes: 1 addition & 2 deletions programs/mpl-asset/src/state/plugin_header.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use borsh::{BorshDeserialize, BorshSerialize};

use crate::{state::Key, utils::DataBlob};
use borsh::{BorshDeserialize, BorshSerialize};

#[repr(C)]
#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)]
Expand Down
10 changes: 8 additions & 2 deletions programs/mpl-asset/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ pub trait DataBlob: BorshSerialize + BorshDeserialize {
fn key() -> Key;

fn load(account: &AccountInfo, offset: usize) -> Result<Self, ProgramError> {
let key = Key::from_u8((*account.data).borrow()[offset])
.ok_or(MplAssetError::DeserializationError)?;
let key = load_key(account, offset)?;

if key != Self::key() {
return Err(MplAssetError::DeserializationError.into());
Expand All @@ -33,3 +32,10 @@ pub trait DataBlob: BorshSerialize + BorshDeserialize {
})
}
}

pub fn load_key(account: &AccountInfo, offset: usize) -> Result<Key, ProgramError> {
let key = Key::from_u8((*account.data).borrow()[offset])
.ok_or(MplAssetError::DeserializationError)?;

Ok(key)
}

0 comments on commit 8cdf58f

Please sign in to comment.