Add Metadata to token bridge attestations

Change-Id: Ic1a10978c25fbd916a16bd08eab0b6937c67cd59
This commit is contained in:
Reisen 2021-08-03 10:13:42 +00:00 committed by David Paryente
parent 9e16baa040
commit 05aece1f7c
17 changed files with 560 additions and 105 deletions

View File

@ -123,8 +123,6 @@ pub fn post_message(
.create(&(&*accs).into(), ctx, accs.payer.key, Exempt)?;
}
msg!("Sequence: {}", accs.sequence.sequence);
// Initialize transfer
trace!("Setting Message Details");
accs.message.submission_time = accs.clock.unix_timestamp as u32;

View File

@ -3159,6 +3159,15 @@ dependencies = [
"thiserror",
]
[[package]]
name = "spl-token-metadata"
version = "0.0.1"
dependencies = [
"borsh",
"solana-program",
"spl-token",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -3376,6 +3385,7 @@ dependencies = [
"solitaire",
"solitaire-client",
"spl-token",
"spl-token-metadata",
"wasm-bindgen",
]

View File

@ -27,6 +27,7 @@ solana-program = "*"
spl-token = { version = "=3.1.0", features = ["no-entrypoint"] }
primitive-types = { version = "0.9.0", default-features = false }
solitaire-client = { path = "../../../solitaire/client", optional = true }
spl-token-metadata = { path = "../token-metadata" }
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] }
serde = { version = "1.0", features = ["derive"] }
rand = { version = "0.7.3", optional = true }
@ -38,3 +39,4 @@ libsecp256k1 = { version = "0.3.5", features = [] }
solana-client = "1.7.0"
solana-sdk = "=1.7.0"
spl-token = { version = "=3.1.0", features = ["no-entrypoint"] }
spl-token-metadata = { path = "../token-metadata" }

View File

@ -2,12 +2,18 @@ use crate::{
accounts::{
ConfigAccount,
EmitterAccount,
WrappedMetaDerivationData,
WrappedTokenMeta,
},
messages::{
PayloadAssetMeta,
PayloadTransfer,
},
types::*,
TokenBridgeError::{
self,
*,
},
};
use bridge::{
api::{
@ -33,7 +39,11 @@ use solana_program::{
sysvar::clock::Clock,
};
use solitaire::{
processors::seeded::invoke_seeded,
processors::seeded::{
invoke_seeded,
Owned,
Seeded,
},
CreationLamports::Exempt,
*,
};
@ -44,6 +54,7 @@ use spl_token::{
Mint,
},
};
use spl_token_metadata::state::Metadata;
use std::ops::{
Deref,
DerefMut,
@ -57,7 +68,10 @@ pub struct AttestToken<'b> {
/// Mint to attest
pub mint: Data<'b, SplMint, { AccountState::Initialized }>,
pub mint_meta: Data<'b, SplMint, { AccountState::MaybeInitialized }>,
pub wrapped_meta: WrappedTokenMeta<'b, { AccountState::Uninitialized }>,
/// SPL Metadata for the associated Mint
pub spl_metadata: Info<'b>,
/// CPI Context
pub bridge: Mut<Info<'b>>,
@ -80,6 +94,14 @@ pub struct AttestToken<'b> {
impl<'b> InstructionContext<'b> for AttestToken<'b> {
}
impl<'a> From<&AttestToken<'a>> for WrappedMetaDerivationData {
fn from(accs: &AttestToken<'a>) -> Self {
WrappedMetaDerivationData {
mint_key: *accs.mint.info().key,
}
}
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct AttestTokenData {
pub nonce: u32,
@ -93,18 +115,33 @@ pub fn attest_token(
// Pay fee
let transfer_ix =
solana_program::system_instruction::transfer(accs.payer.key, accs.fee_collector.key, 1000);
invoke(&transfer_ix, ctx.accounts)?;
let payload = PayloadAssetMeta {
// Enfoce wrapped meta to be uninitialized.
let derivation_data: WrappedMetaDerivationData = (&*accs).into();
accs.wrapped_meta
.verify_derivation(ctx.program_id, &derivation_data)?;
// Create Asset Metadata
let mut payload = PayloadAssetMeta {
token_address: accs.mint.info().key.to_bytes(),
token_chain: 1,
decimals: accs.mint.decimals,
symbol: "".to_string(), // TODO metadata
symbol: "".to_string(),
name: "".to_string(),
};
if accs.mint_meta.is_initialized() {
// Populate fields
// Assign metadata if an SPL Metadata account exists for the SPL token in question.
if !accs.spl_metadata.data_is_empty() {
if *accs.spl_metadata.owner != spl_token_metadata::id() {
return Err(WrongAccountOwner.into());
}
let metadata: Metadata =
Metadata::from_account_info(accs.spl_metadata.info()).ok_or(InvalidMetadata)?;
payload.name = metadata.data.name.clone();
payload.symbol = metadata.data.symbol.clone();
}
let params = (

View File

@ -464,6 +464,9 @@ pub fn attest(
mint: Pubkey,
decimals: u8,
mint_meta: Pubkey,
spl_metadata: Pubkey,
symbol: String,
name: String,
nonce: u32,
) -> solitaire::Result<Instruction> {
let config_key = ConfigAccount::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
@ -475,16 +478,16 @@ pub fn attest(
token_address: mint.to_bytes(),
token_chain: 1,
decimals,
symbol: "".to_string(), // TODO metadata
name: "".to_string(),
symbol,
name,
};
let message_key = Message::<'_, { AccountState::Uninitialized }>::key(
&MessageDerivationData {
emitter_key: emitter_key.to_bytes(),
emitter_chain: 1,
nonce,
sequence: None,
payload: payload.try_to_vec().unwrap(),
sequence: None,
},
&bridge_id,
);
@ -503,6 +506,7 @@ pub fn attest(
AccountMeta::new(config_key, false),
AccountMeta::new_readonly(mint, false),
AccountMeta::new_readonly(mint_meta, false),
AccountMeta::new_readonly(spl_metadata, false),
// Bridge accounts
AccountMeta::new(bridge_config, false),
AccountMeta::new(message_key, false),

View File

@ -53,26 +53,21 @@ use solitaire::*;
use std::error::Error;
pub enum TokenBridgeError {
InvalidPayload,
Unknown(String),
InvalidMint,
WrongAccountOwner,
InvalidUTF8String,
AlreadyExecuted,
InvalidChain,
TokenNotNative,
InvalidGovernanceKey,
InvalidMetadata,
InvalidMint,
InvalidPayload,
InvalidUTF8String,
TokenNotNative,
UninitializedMint,
WrongAccountOwner,
}
impl<T: Error> From<T> for TokenBridgeError {
fn from(t: T) -> Self {
return TokenBridgeError::Unknown(t.to_string());
}
}
impl Into<SolitaireError> for TokenBridgeError {
fn into(self) -> SolitaireError {
SolitaireError::Custom(0)
impl From<TokenBridgeError> for SolitaireError {
fn from(t: TokenBridgeError) -> SolitaireError {
SolitaireError::Custom(t as u64)
}
}

View File

@ -18,6 +18,8 @@ use spl_token::state::{
Account,
Mint,
};
use spl_token_metadata::state::Metadata;
use std::str::FromStr;
pub type Address = [u8; 32];
pub type ChainID = u16;

View File

@ -168,11 +168,11 @@ mod helpers {
}
/// Fetch account data, the loop is there to re-attempt until data is available.
pub fn get_account_data<T: BorshDeserialize>(client: &RpcClient, account: &Pubkey) -> T {
pub fn get_account_data<T: BorshDeserialize>(client: &RpcClient, account: &Pubkey) -> Option<T> {
let account = client
.get_account_with_commitment(account, CommitmentConfig::processed())
.unwrap();
T::try_from_slice(&account.value.unwrap().data).unwrap()
T::try_from_slice(&account.value.unwrap().data).ok()
}
pub fn initialize_bridge(
@ -233,7 +233,10 @@ mod helpers {
bridge: &Pubkey,
payer: &Keypair,
mint: Pubkey,
mint_meta: Pubkey,
wrapped_meta: Pubkey,
spl_metadata: Pubkey,
symbol: String,
name: String,
nonce: u32,
) -> Result<Signature, ClientError> {
let mint_data = Mint::unpack(
@ -255,7 +258,10 @@ mod helpers {
payer.pubkey(),
mint,
mint_data.decimals,
mint_meta,
wrapped_meta,
spl_metadata,
symbol,
name,
nonce,
)
.unwrap()],
@ -496,6 +502,41 @@ mod helpers {
)
}
pub fn create_spl_metadata(
client: &RpcClient,
payer: &Keypair,
metadata_account: &Pubkey,
mint_authority: &Keypair,
mint: &Keypair,
update_authority: &Pubkey,
name: String,
symbol: String,
) -> Result<Signature, ClientError> {
execute(
client,
payer,
&[payer, mint_authority],
&[
spl_token_metadata::instruction::create_metadata_accounts(
spl_token_metadata::id(),
*metadata_account,
mint.pubkey(),
mint_authority.pubkey(),
payer.pubkey(),
*update_authority,
name,
symbol,
"https://token.org".to_string(),
None,
0,
false,
false,
)
],
CommitmentConfig::processed(),
)
}
pub fn create_token_account(
client: &RpcClient,
payer: &Keypair,

View File

@ -31,6 +31,7 @@ use solana_program::{
sysvar,
};
use solana_sdk::{
commitment_config::CommitmentConfig,
signature::{
read_keypair_file,
Keypair,
@ -42,6 +43,7 @@ use solitaire::{
processors::seeded::Seeded,
AccountState,
};
use spl_token::state::Mint;
use std::{
convert::TryInto,
io::{
@ -91,6 +93,7 @@ use std::{
};
use token_bridge::{
accounts::{
EmitterAccount,
WrappedDerivationData,
WrappedMint,
},
@ -130,11 +133,12 @@ struct Context {
/// Keypairs for mint information, required in multiple tests.
mint_authority: Keypair,
mint: Keypair,
mint_meta: Keypair,
mint_meta: Pubkey,
/// Keypairs for test token accounts.
token_authority: Keypair,
token_account: Keypair,
metadata_account: Pubkey,
}
/// Small helper to track and provide sequences during tests. This is in particular needed for
@ -164,6 +168,31 @@ fn run_integration_tests() {
common::initialize_bridge(&client, &bridge, &payer);
// Context for test environment.
let mint = Keypair::new();
let mint_pubkey = mint.pubkey();
let metadata_pubkey = Pubkey::from_str("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s").unwrap();
// SPL Token Meta
let metadata_seeds = &[
"metadata".as_bytes(),
metadata_pubkey.as_ref(),
mint_pubkey.as_ref(),
];
let (metadata_key, metadata_bump_seed) = Pubkey::find_program_address(
metadata_seeds,
&Pubkey::from_str("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s").unwrap(),
);
// Token Bridge Meta
use token_bridge::accounts::WrappedTokenMeta;
let metadata_account = WrappedTokenMeta::<'_, { AccountState::Uninitialized }>::key(
&token_bridge::accounts::WrappedMetaDerivationData {
mint_key: mint_pubkey.clone(),
},
&token_bridge,
);
let mut context = Context {
seq: Sequencer {
sequences: HashMap::new(),
@ -173,10 +202,11 @@ fn run_integration_tests() {
payer,
token_bridge,
mint_authority: Keypair::new(),
mint: Keypair::new(),
mint_meta: Keypair::new(),
mint,
mint_meta: metadata_account,
token_account: Keypair::new(),
token_authority: Keypair::new(),
metadata_account: metadata_key,
};
// Create a mint for use within tests.
@ -215,6 +245,20 @@ fn run_integration_tests() {
test_attest(&mut context);
test_register_chain(&mut context);
test_transfer_native_in(&mut context);
// Create an SPL Metadata account to test attestations for wrapped tokens.
common::create_spl_metadata(
&context.client,
&context.payer,
&context.metadata_account,
&context.mint_authority,
&context.mint,
&context.payer.pubkey(),
"BTC".to_string(),
"Bitcoin".to_string(),
)
.unwrap();
let wrapped = test_create_wrapped(&mut context);
let wrapped_acc = Keypair::new();
common::create_token_account(
@ -244,6 +288,7 @@ fn test_attest(context: &mut Context) -> () {
ref mint_authority,
ref mint,
ref mint_meta,
ref metadata_account,
..
} = context;
@ -253,10 +298,42 @@ fn test_attest(context: &mut Context) -> () {
bridge,
payer,
mint.pubkey(),
mint_meta.pubkey(),
*mint_meta,
*metadata_account,
"".to_string(),
"".to_string(),
0,
)
.unwrap();
let emitter_key = EmitterAccount::key(None, &token_bridge);
let mint_data = Mint::unpack(
&client
.get_account_with_commitment(&mint.pubkey(), CommitmentConfig::processed())
.unwrap()
.value
.unwrap()
.data,
)
.unwrap();
let payload = PayloadAssetMeta {
token_address: mint.pubkey().to_bytes(),
token_chain: 1,
decimals: mint_data.decimals,
symbol: "USD".to_string(),
name: "Bitcoin".to_string(),
};
let payload = payload.try_to_vec().unwrap();
let message_key = Message::<'_, { AccountState::Uninitialized }>::key(
&MessageDerivationData {
emitter_key: emitter_key.to_bytes(),
emitter_chain: 1,
nonce: 0,
sequence: None,
payload: payload.clone(),
},
&token_bridge,
);
}
fn test_transfer_native(context: &mut Context) -> () {
@ -533,6 +610,6 @@ fn test_initialize(context: &mut Context) {
// Verify Token Bridge State
let config_key = ConfigAccount::<'_, { AccountState::Uninitialized }>::key(None, &token_bridge);
let config: Config = common::get_account_data(client, &config_key);
let config: Config = common::get_account_data(client, &config_key).unwrap();
assert_eq!(config.wormhole_bridge, *bridge);
}

View File

@ -0,0 +1,20 @@
[package]
name = "spl-token-metadata"
version = "0.0.1"
description = "Metaplex Metadata"
authors = ["Metaplex Maintainers <maintainers@metaplex.com>"]
repository = "https://github.com/metaplex-foundation/metaplex"
license = "Apache-2.0"
edition = "2018"
[lib]
crate-type = ["cdylib", "lib"]
[features]
no-entrypoint = []
test-bpf = []
[dependencies]
borsh = "0.8.1"
solana-program = "1.7.0"
spl-token = { version = "=3.1.0", features = ["no-entrypoint"] }

View File

@ -0,0 +1,8 @@
---
title: Token Metadata Program
---
Fork of the SPL Token Metadata program from the Metaplex repository, this is
temporary until there are versioned releases we can compile against. Currently
the upstream version depends on a version of solana with conflicting versions
of borsh.

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -0,0 +1,130 @@
use {
crate::{
state::{Creator, Data, EDITION, EDITION_MARKER_BIT_SIZE, PREFIX},
},
borsh::{BorshDeserialize, BorshSerialize},
solana_program::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
sysvar,
},
};
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
/// Args for update call
pub struct UpdateMetadataAccountArgs {
pub data: Option<Data>,
pub update_authority: Option<Pubkey>,
pub primary_sale_happened: Option<bool>,
}
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
/// Args for create call
pub struct CreateMetadataAccountArgs {
/// Note that unique metadatas are disabled for now.
pub data: Data,
/// Whether you want your metadata to be updateable in the future.
pub is_mutable: bool,
}
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
pub struct CreateMasterEditionArgs {
/// If set, means that no more than this number of editions can ever be minted. This is immutable.
pub max_supply: Option<u64>,
}
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
pub struct MintNewEditionFromMasterEditionViaTokenArgs {
pub edition: u64,
}
/// Instructions supported by the Metadata program.
#[derive(BorshSerialize, BorshDeserialize, Clone)]
pub enum MetadataInstruction {
/// Create Metadata object.
/// 0. `[writable]` Metadata key (pda of ['metadata', program id, mint id])
/// 1. `[]` Mint of token asset
/// 2. `[signer]` Mint authority
/// 3. `[signer]` payer
/// 4. `[]` update authority info
/// 5. `[]` System program
/// 6. `[]` Rent info
CreateMetadataAccount(CreateMetadataAccountArgs),
/// Update a Metadata
/// 0. `[writable]` Metadata account
/// 1. `[signer]` Update authority key
UpdateMetadataAccount(UpdateMetadataAccountArgs),
}
/// Creates an CreateMetadataAccounts instruction
#[allow(clippy::too_many_arguments)]
pub fn create_metadata_accounts(
program_id: Pubkey,
metadata_account: Pubkey,
mint: Pubkey,
mint_authority: Pubkey,
payer: Pubkey,
update_authority: Pubkey,
name: String,
symbol: String,
uri: String,
creators: Option<Vec<Creator>>,
seller_fee_basis_points: u16,
update_authority_is_signer: bool,
is_mutable: bool,
) -> Instruction {
Instruction {
program_id,
accounts: vec![
AccountMeta::new(metadata_account, false),
AccountMeta::new_readonly(mint, false),
AccountMeta::new_readonly(mint_authority, true),
AccountMeta::new_readonly(payer, true),
AccountMeta::new_readonly(update_authority, update_authority_is_signer),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
],
data: MetadataInstruction::CreateMetadataAccount(CreateMetadataAccountArgs {
data: Data {
name,
symbol,
uri,
seller_fee_basis_points,
creators,
},
is_mutable,
})
.try_to_vec()
.unwrap(),
}
}
/// update metadata account instruction
pub fn update_metadata_accounts(
program_id: Pubkey,
metadata_account: Pubkey,
update_authority: Pubkey,
new_update_authority: Option<Pubkey>,
data: Option<Data>,
primary_sale_happened: Option<bool>,
) -> Instruction {
Instruction {
program_id,
accounts: vec![
AccountMeta::new(metadata_account, false),
AccountMeta::new_readonly(update_authority, true),
],
data: MetadataInstruction::UpdateMetadataAccount(UpdateMetadataAccountArgs {
data,
update_authority: new_update_authority,
primary_sale_happened,
})
.try_to_vec()
.unwrap(),
}
}

View File

@ -0,0 +1,7 @@
#![allow(warnings)]
pub mod instruction;
pub mod state;
pub mod utils;
solana_program::declare_id!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");

View File

@ -0,0 +1,124 @@
use crate::{
utils::try_from_slice_checked,
};
use borsh::{
BorshDeserialize,
BorshSerialize,
};
use solana_program::{
account_info::AccountInfo,
entrypoint::ProgramResult,
program_error::ProgramError,
pubkey::Pubkey,
};
/// prefix used for PDAs to avoid certain collision attacks (https://en.wikipedia.org/wiki/Collision_attack#Chosen-prefix_collision_attack)
pub const PREFIX: &str = "metadata";
/// Used in seeds to make Edition model pda address
pub const EDITION: &str = "edition";
pub const RESERVATION: &str = "reservation";
pub const MAX_NAME_LENGTH: usize = 32;
pub const MAX_SYMBOL_LENGTH: usize = 10;
pub const MAX_URI_LENGTH: usize = 200;
pub const MAX_METADATA_LEN: usize = 1
+ 32
+ 32
+ MAX_NAME_LENGTH
+ MAX_SYMBOL_LENGTH
+ MAX_URI_LENGTH
+ MAX_CREATOR_LIMIT * MAX_CREATOR_LEN
+ 2
+ 1
+ 1
+ 198;
pub const MAX_EDITION_LEN: usize = 1 + 32 + 8 + 200;
// Large buffer because the older master editions have two pubkeys in them,
// need to keep two versions same size because the conversion process actually changes the same account
// by rewriting it.
pub const MAX_MASTER_EDITION_LEN: usize = 1 + 9 + 8 + 264;
pub const MAX_CREATOR_LIMIT: usize = 5;
pub const MAX_CREATOR_LEN: usize = 32 + 1 + 1;
pub const MAX_RESERVATIONS: usize = 200;
// can hold up to 200 keys per reservation, note: the extra 8 is for number of elements in the vec
pub const MAX_RESERVATION_LIST_V1_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 34 + 100;
// can hold up to 200 keys per reservation, note: the extra 8 is for number of elements in the vec
pub const MAX_RESERVATION_LIST_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 48 + 8 + 8 + 84;
pub const MAX_EDITION_MARKER_SIZE: usize = 32;
pub const EDITION_MARKER_BIT_SIZE: u64 = 248;
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, Copy)]
pub enum Key {
Uninitialized,
EditionV1,
MasterEditionV1,
ReservationListV1,
MetadataV1,
ReservationListV2,
MasterEditionV2,
EditionMarker,
}
impl Default for Key {
fn default() -> Self {
Key::Uninitialized
}
}
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, Default, PartialEq, Debug, Clone)]
pub struct Data {
/// The name of the asset
pub name: String,
/// The symbol for the asset
pub symbol: String,
/// URI pointing to JSON representing the asset
pub uri: String,
/// Royalty basis points that goes to creators in secondary sales (0-10000)
pub seller_fee_basis_points: u16,
/// Array of creators, optional
pub creators: Option<Vec<Creator>>,
}
#[repr(C)]
#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Default)]
pub struct Metadata {
pub key: Key,
pub update_authority: Pubkey,
pub mint: Pubkey,
pub data: Data,
// Immutable, once flipped, all sales of this metadata are considered secondary.
pub primary_sale_happened: bool,
// Whether or not the data struct is mutable, default is not
pub is_mutable: bool,
}
impl Metadata {
pub fn from_account_info(a: &AccountInfo) -> Option<Metadata> {
try_from_slice_checked(&a.data.borrow_mut(), Key::MetadataV1, MAX_METADATA_LEN)
}
}
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
pub struct Creator {
pub address: Pubkey,
pub verified: bool,
// In percentages, NOT basis points ;) Watch out!
pub share: u8,
}

View File

@ -0,0 +1,68 @@
use crate::{
state::{
Data,
Key,
Metadata,
EDITION,
EDITION_MARKER_BIT_SIZE,
MAX_CREATOR_LIMIT,
MAX_EDITION_LEN,
MAX_EDITION_MARKER_SIZE,
MAX_MASTER_EDITION_LEN,
MAX_METADATA_LEN,
MAX_NAME_LENGTH,
MAX_SYMBOL_LENGTH,
MAX_URI_LENGTH,
PREFIX,
},
};
use borsh::{
BorshDeserialize,
BorshSerialize,
};
use solana_program::{
account_info::AccountInfo,
borsh::try_from_slice_unchecked,
entrypoint::ProgramResult,
msg,
program::{
invoke,
invoke_signed,
},
program_error::ProgramError,
program_option::COption,
program_pack::{
IsInitialized,
Pack,
},
pubkey::Pubkey,
system_instruction,
sysvar::{
rent::Rent,
Sysvar,
},
};
use spl_token::{
instruction::{
set_authority,
AuthorityType,
},
state::{
Account,
Mint,
},
};
use std::convert::TryInto;
pub fn try_from_slice_checked<T: BorshDeserialize>(
data: &[u8],
data_type: Key,
data_size: usize,
) -> Option<T> {
if (data[0] != data_type as u8 && data[0] != Key::Uninitialized as u8)
|| data.len() != data_size
{
return None;
}
try_from_slice_unchecked(data).ok()
}

View File

@ -102,76 +102,6 @@ macro_rules! solitaire {
}
}
#[macro_export]
macro_rules! data_wrapper {
($name:ident, $embed:ty, $state:expr) => {
#[repr(transparent)]
pub struct $name<'b>(solitaire::Data<'b, $embed, { $state }>);
impl<'b> std::ops::Deref for $name<'b> {
type Target = solitaire::Data<'b, $embed, { $state }>;
fn deref(&self) -> &Self::Target {
return &self.0;
}
}
impl<'b> std::ops::DerefMut for $name<'b> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::mem::transmute(&mut self.0) }
}
}
impl<'a, 'b: 'a> solitaire::processors::keyed::Keyed<'a, 'b> for $name<'b> {
fn info(&'a self) -> &'a solitaire::Info<'b> {
self.0.info()
}
}
impl<'b> solitaire::AccountSize for $name<'b> {
fn size(&self) -> usize {
return self.0.size();
}
}
impl<'a, 'b: 'a, 'c> solitaire::Peel<'a, 'b, 'c> for $name<'b> {
fn peel<T>(ctx: &'c mut solitaire::Context<'a, 'b, 'c, T>) -> solitaire::Result<Self>
where
Self: Sized,
{
solitaire::Data::peel(ctx).map(|v| $name(v))
}
fn deps() -> Vec<solana_program::pubkey::Pubkey> {
solitaire::Data::<'_, $embed, { $state }>::deps()
}
fn persist(
&self,
program_id: &solana_program::pubkey::Pubkey,
) -> solitaire::Result<()> {
solitaire::Data::<'_, $embed, { $state }>::persist(self, program_id)
}
}
impl<'b> solitaire::Owned for $name<'b> {
fn owner(&self) -> solitaire::AccountOwner {
return self.1.owner();
}
}
#[cfg(feature = "client")]
impl<'b> solitaire_client::Wrap for $name<'b> {
fn wrap(
a: &solitaire_client::AccEntry,
) -> std::result::Result<Vec<solitaire_client::AccountMeta>, solitaire_client::ErrBox>
{
solitaire::Data::<'b, $embed, { $state }>::wrap(a)
}
}
};
}
#[macro_export]
macro_rules! pack_type {
($name:ident, $embed:ty, $owner:expr) => {