use borsh::BorshDeserialize;
use mpl_utils::{assert_derivation, assert_signer, resize_or_reallocate_account_raw};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
program_memory::sol_memcpy,
system_program,
};
use crate::{
error::OnchainMetadataError, instruction::AppendValueArgs, pda::PREFIX, state::JsonMetadata,
};
pub(crate) fn process_append_value(
accounts: &[AccountInfo],
args: AppendValueArgs,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let json_account = next_account_info(account_info_iter)?;
if (json_account.owner != &crate::ID) || json_account.data_is_empty() {
return Err(OnchainMetadataError::NotInitialized.into());
}
let json_metadata_account = next_account_info(account_info_iter)?;
if (json_metadata_account.owner != &crate::ID) || json_metadata_account.data_is_empty() {
return Err(OnchainMetadataError::NotInitialized.into());
}
let json_metadata = JsonMetadata::try_from_slice(&json_metadata_account.data.borrow())?;
let bump = assert_derivation(
&crate::ID,
json_metadata_account,
&[
PREFIX.as_bytes(),
crate::ID.as_ref(),
json_account.key.as_ref(),
],
OnchainMetadataError::MetadataDerivedKeyInvalid,
)?;
if bump != json_metadata.bump {
return Err(OnchainMetadataError::MetadataDerivedKeyInvalid.into());
}
let payer = next_account_info(account_info_iter)?;
assert_signer(payer)?;
if !json_metadata.authorities.contains(payer.key) {
return Err(OnchainMetadataError::InvalidAuthority.into());
}
let system_program = next_account_info(account_info_iter)?;
if system_program.key != &system_program::ID {
return Err(OnchainMetadataError::InvalidSystemProgram.into());
}
let mut json_data: serde_json::Value = serde_json::from_slice(&json_account.data.borrow())
.map_err(|_| OnchainMetadataError::InvalidJson)?;
let new_data: serde_json::Value =
serde_json::from_str(&args.value).map_err(|_| OnchainMetadataError::InvalidJson)?;
merge_append(&mut json_data, new_data)?;
let serialized_data =
serde_json::to_vec(&json_data).map_err(|_| OnchainMetadataError::InvalidJson)?;
resize_or_reallocate_account_raw(json_account, payer, system_program, serialized_data.len())?;
sol_memcpy(
&mut json_account.try_borrow_mut_data()?,
&serialized_data,
serialized_data.len(),
);
let json_data: serde_json::Value = serde_json::from_slice(&json_account.data.borrow())
.map_err(|_| OnchainMetadataError::InvalidJson)?;
solana_program::msg!("JSON account data: {:?}", json_data);
Ok(())
}
fn merge_append(a: &mut serde_json::Value, b: serde_json::Value) -> ProgramResult {
if let serde_json::Value::Object(a) = a {
if let serde_json::Value::Object(b) = b {
for (k, v) in b {
merge_append(a.entry(k).or_insert(serde_json::Value::Null), v)?;
}
return Ok(());
}
}
match a {
serde_json::Value::String(a) => {
if let serde_json::Value::String(b) = b {
a.push_str(&b);
Ok(())
} else {
Err(OnchainMetadataError::InvalidJson.into())
}
}
serde_json::Value::Array(a) => {
if let serde_json::Value::Array(b) = b {
a.extend(b);
Ok(())
} else {
Err(OnchainMetadataError::InvalidJson.into())
}
}
_ => Err(OnchainMetadataError::InvalidJson.into()),
}
}