Solitaire: Add an explicit Many<T> type for nested structs
Change-Id: I6a721e8a88652f807f2317cfe4d82be9d629a849
This commit is contained in:
parent
ee96b6b793
commit
6868cc7177
|
@ -26,6 +26,7 @@ use solitaire::{
|
|||
CPICall,
|
||||
FromAccounts,
|
||||
Info,
|
||||
Many,
|
||||
Mut,
|
||||
Sysvar,
|
||||
};
|
||||
|
@ -59,6 +60,9 @@ pub enum AccEntry {
|
|||
/// Program addresses for privileged cross calls
|
||||
CPIProgramSigner(Keypair, Box<dyn ToInstruction>),
|
||||
|
||||
/// General case of nested structs not representing a CPI call
|
||||
Many(Box<dyn ToInstruction>),
|
||||
|
||||
/// Key decided from SPL constants
|
||||
Sysvar(Pubkey),
|
||||
|
||||
|
@ -68,8 +72,8 @@ pub enum AccEntry {
|
|||
DerivedRO(Pubkey),
|
||||
}
|
||||
|
||||
/// Types implementing Wrap are those that can be turned into a
|
||||
/// partial account vector for a program call.
|
||||
/// Types implementing Wrap can be used in top-level Solana calls as
|
||||
/// the accepted, implementation-dependent AccEntry variants.
|
||||
pub trait Wrap {
|
||||
fn wrap(_: &AccEntry) -> StdResult<Vec<AccountMeta>, ErrBox>;
|
||||
|
||||
|
@ -143,15 +147,10 @@ where
|
|||
SignerRO(pair) => Ok(vec![AccountMeta::new_readonly(pair.pubkey(), true)]),
|
||||
_other => Err(format!("{} with IsInitialized = {:?} must be passed as Unprivileged, Signer or the respective read-only variant", std::any::type_name::<Self>(), a).into())
|
||||
},
|
||||
Uninitialized => match a {
|
||||
Uninitialized | MaybeInitialized => match a {
|
||||
Unprivileged(k) => Ok(vec![AccountMeta::new(*k, false)]),
|
||||
Signer(pair) => Ok(vec![AccountMeta::new(pair.pubkey(), true)]),
|
||||
_other => Err(format!("{} with IsInitialized = {:?} must be passed as Unprivileged or Signer (write access required for initialization)", std::any::type_name::<Self>(), a).into())
|
||||
}
|
||||
MaybeInitialized => match a {
|
||||
Unprivileged(k) => Ok(vec![AccountMeta::new(*k, false)]),
|
||||
Signer(pair) => Ok(vec![AccountMeta::new(pair.pubkey(), true)]),
|
||||
_other => Err(format!("{} with IsInitialized = {:?} must be passed as Unprivileged or Signer (write access required in case of initialization)", std::any::type_name::<Self>(), a).into())
|
||||
_other => Err(format!("{} with IsInitialized = {:?} must be passed as Unprivileged or Signer (write access required for initialization)", std::any::type_name::<Self>(), IsInitialized).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +212,7 @@ impl<'a, 'b: 'a, 'c, T> Wrap for CPICall<T> {
|
|||
match a {
|
||||
AccEntry::CPIProgram(xprog_k, xprog_accs) => {
|
||||
let mut v = vec![AccountMeta::new_readonly(xprog_k.clone(), false)];
|
||||
v.append(&mut xprog_accs.acc_metas(&xprog_k)?);
|
||||
v.append(&mut xprog_accs.gen_client_metas()?);
|
||||
Ok(v)
|
||||
}
|
||||
AccEntry::CPIProgramSigner(xprog_pair, xprog_accs) => {
|
||||
|
@ -221,7 +220,7 @@ impl<'a, 'b: 'a, 'c, T> Wrap for CPICall<T> {
|
|||
xprog_pair.pubkey().clone(),
|
||||
false,
|
||||
)];
|
||||
v.append(&mut xprog_accs.acc_metas(&xprog_pair.pubkey())?);
|
||||
v.append(&mut xprog_accs.gen_client_metas()?);
|
||||
Ok(v)
|
||||
}
|
||||
_other => Err(format!(
|
||||
|
@ -234,10 +233,10 @@ impl<'a, 'b: 'a, 'c, T> Wrap for CPICall<T> {
|
|||
|
||||
fn partial_signer_keypairs(a: &AccEntry) -> Vec<Keypair> {
|
||||
match a {
|
||||
AccEntry::CPIProgram(xprog_k, xprog_accs) => xprog_accs.signer_keypairs(),
|
||||
AccEntry::CPIProgram(xprog_k, xprog_accs) => xprog_accs.gen_client_signers(),
|
||||
AccEntry::CPIProgramSigner(xprog_pair, xprog_accs) => {
|
||||
let mut v = vec![Keypair::from_bytes(&xprog_pair.to_bytes()[..]).unwrap()];
|
||||
v.append(&mut xprog_accs.signer_keypairs());
|
||||
v.append(&mut xprog_accs.gen_client_signers());
|
||||
v
|
||||
}
|
||||
_other => vec![],
|
||||
|
@ -245,15 +244,30 @@ impl<'a, 'b: 'a, 'c, T> Wrap for CPICall<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Trait used on client side to easily validate a program account struct + ix_data for a bare Solana call
|
||||
impl<'a, 'b: 'a, 'c, T: FromAccounts<'a, 'b, 'c>> Wrap for Many<T> {
|
||||
fn wrap(a: &AccEntry) -> StdResult<Vec<AccountMeta>, ErrBox> {
|
||||
if let AccEntry::Many(nested_accs) = a {
|
||||
Ok(nested_accs.gen_client_metas()?)
|
||||
} else {
|
||||
Err(format!("{} must be passed as Many", std::any::type_name::<Self>()).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait used on top-level client side to easily validate a program account struct + ix_data for a bare Solana call
|
||||
pub trait ToInstruction: std::fmt::Debug {
|
||||
fn to_ix(
|
||||
/// Convenience method that will create an Instruction struct and
|
||||
/// a vector of signer keypairs for use in a Solana RPC contract
|
||||
/// call.
|
||||
fn gen_client_ix(
|
||||
&self,
|
||||
program_id: Pubkey,
|
||||
ix_data: &[u8],
|
||||
) -> StdResult<(Instruction, Vec<Keypair>), ErrBox>;
|
||||
|
||||
fn acc_metas(&self, program_id: &Pubkey) -> StdResult<Vec<AccountMeta>, ErrBox>;
|
||||
/// Serialize the implementor into a vec of appropriate AccountMetas
|
||||
fn gen_client_metas(&self) -> StdResult<Vec<AccountMeta>, ErrBox>;
|
||||
|
||||
fn signer_keypairs(&self) -> Vec<Keypair>;
|
||||
/// Gather all keypairs required by the implementor's Solana program call
|
||||
fn gen_client_signers(&self) -> Vec<Keypair>;
|
||||
}
|
||||
|
|
|
@ -290,7 +290,7 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c> + FromAccounts<'a, 'b, 'c>> Peel<'a, 'b, 'c>
|
||||
impl<'a, 'b: 'a, 'c, T: FromAccounts<'a, 'b, 'c>> Peel<'a, 'b, 'c>
|
||||
for CPICall<T>
|
||||
{
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self>
|
||||
|
@ -317,7 +317,7 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c> + FromAccounts<'a, 'b, 'c>> Peel<'a, 'b
|
|||
}
|
||||
|
||||
fn deps() -> Vec<Pubkey> {
|
||||
todo!()
|
||||
vec![system_program::id()]
|
||||
}
|
||||
|
||||
fn persist(&self, program_id: &Pubkey) -> Result<()> {
|
||||
|
@ -333,3 +333,29 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c> + FromAccounts<'a, 'b, 'c>> Peel<'a, 'b
|
|||
T::size_in_accounts() + 1 // Nested type size + 1 for cross program ID
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c, T: FromAccounts<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Many<T> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok(Self(T::from(ctx.this, ctx.iter, ctx.data)?))
|
||||
}
|
||||
|
||||
fn deps() -> Vec<Pubkey> {
|
||||
// deps handled in peel() using <T as FromAccounts>::from()
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn persist(&self, program_id: &Pubkey) -> Result<()> {
|
||||
self.persist(program_id)
|
||||
}
|
||||
|
||||
fn to_partial_cpi_metas(infos: &'c mut Iter<Info<'b>>) -> Result<Vec<AccountMeta>> {
|
||||
T::to_cpi_metas(infos)
|
||||
}
|
||||
|
||||
fn partial_size_in_accounts() -> usize {
|
||||
T::size_in_accounts()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,6 +121,7 @@ impl<const Seed: &'static str, T: BorshSerialize + Owned + Default>
|
|||
}
|
||||
}
|
||||
|
||||
/// Model a cross-program invocation with its program ID and AccountMetas
|
||||
#[derive(Debug)]
|
||||
pub struct CPICall<T> {
|
||||
pub xprog_id: Pubkey,
|
||||
|
@ -128,3 +129,21 @@ pub struct CPICall<T> {
|
|||
/// Helps preserve information about the type of cross program's arguments
|
||||
pub callee_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
/// Model a nested FromAccounts struct
|
||||
|
||||
pub struct Many<T>(pub T);
|
||||
|
||||
|
||||
impl<T> Deref for Many<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Many<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,34 +126,6 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// Macro generated implementation of Peel by Solitaire.
|
||||
impl #combined_impl_g solitaire::Peel<'a, 'b, 'c> for #name #type_g {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> solitaire::Result<Self> where Self: Sized {
|
||||
let v: #name #type_g = FromAccounts::from(ctx.this, ctx.iter, ctx.data)?;
|
||||
|
||||
// Verify the instruction constraints
|
||||
solitaire::InstructionContext::verify(&v, ctx.this)?;
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn deps() -> Vec<solana_program::pubkey::Pubkey> {
|
||||
#deps_method
|
||||
}
|
||||
|
||||
fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> {
|
||||
solitaire::Persist::persist(self, program_id)
|
||||
}
|
||||
|
||||
fn partial_size_in_accounts() -> usize {
|
||||
Self::size_in_accounts()
|
||||
}
|
||||
|
||||
fn to_partial_cpi_metas(infos: &'c mut std::slice::Iter<Info<'b>>) -> solitaire::Result<Vec<solana_program::instruction::AccountMeta>> {
|
||||
Self::to_cpi_metas(infos)
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro generated implementation of Persist by Solitaire.
|
||||
impl #type_impl_g solitaire::Persist for #name #type_g {
|
||||
fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> {
|
||||
|
|
|
@ -59,7 +59,7 @@ pub fn generate_to_instruction(
|
|||
#[cfg(feature = "client")]
|
||||
impl #impl_generics solitaire_client::ToInstruction for #client_struct_ident {
|
||||
|
||||
fn to_ix(
|
||||
fn gen_client_ix(
|
||||
&self,
|
||||
program_id: solana_program::pubkey::Pubkey,
|
||||
ix_data: &[u8]) -> std::result::Result<
|
||||
|
@ -86,7 +86,7 @@ pub fn generate_to_instruction(
|
|||
|
||||
}
|
||||
|
||||
fn acc_metas(&self, aprogram_id: &solana_program::pubkey::Pubkey) -> std::result::Result <Vec<solana_program::instruction::AccountMeta>, solitaire::ErrBox> {
|
||||
fn gen_client_metas(&self) -> std::result::Result <Vec<solana_program::instruction::AccountMeta>, solitaire::ErrBox> {
|
||||
let mut #acc_metas_ident = Vec::new();
|
||||
|
||||
#acc_metas_appends
|
||||
|
@ -94,7 +94,7 @@ pub fn generate_to_instruction(
|
|||
Ok(#acc_metas_ident)
|
||||
}
|
||||
|
||||
fn signer_keypairs(&self) -> Vec<solitaire_client::solana_sdk::signature::Keypair> {
|
||||
fn gen_client_signers(&self) -> Vec<solitaire_client::solana_sdk::signature::Keypair> {
|
||||
let mut #signers_ident = Vec::new();
|
||||
|
||||
#signers_appends
|
||||
|
|
Loading…
Reference in New Issue