2021-06-08 07:50:43 -07:00
#![ feature(const_generics) ]
#![ feature(const_generics_defaults) ]
#![ allow(warnings) ]
//! Client-specific code
pub use solana_program ::pubkey ::Pubkey ;
2021-06-16 03:09:06 -07:00
use solana_program ::sysvar ::Sysvar as SolSysvar ;
2021-06-08 07:50:43 -07:00
pub use solana_sdk ;
pub use solana_sdk ::{
instruction ::{
AccountMeta ,
Instruction ,
} ,
signature ::{
Keypair ,
Signer as SolSigner ,
} ,
} ;
use borsh ::BorshSerialize ;
2021-06-16 03:09:06 -07:00
use solitaire ::{
AccountState ,
2021-07-05 03:16:27 -07:00
CPICall ,
FromAccounts ,
2021-06-25 07:05:03 -07:00
Info ,
2021-07-12 04:53:46 -07:00
Many ,
2021-07-05 03:16:27 -07:00
Mut ,
2021-06-16 03:09:06 -07:00
Sysvar ,
} ;
2021-06-08 07:50:43 -07:00
pub use solitaire ::{
Data ,
Derive ,
Keyed ,
Owned ,
Signer ,
} ;
type StdResult < T , E > = std ::result ::Result < T , E > ;
pub type ErrBox = Box < dyn std ::error ::Error > ;
/// The sum type for clearly specifying the accounts required on client side.
2021-06-11 05:29:43 -07:00
#[ derive(Debug) ]
2021-06-08 07:50:43 -07:00
pub enum AccEntry {
/// Least privileged account.
Unprivileged ( Pubkey ) ,
2021-06-11 05:29:43 -07:00
/// Least privileged account, read-only.
UnprivilegedRO ( Pubkey ) ,
2021-06-08 07:50:43 -07:00
/// Accounts that need to sign a Solana call
Signer ( Keypair ) ,
2021-06-11 05:29:43 -07:00
/// Accounts that need to sign a Solana call, read-only.
2021-06-08 07:50:43 -07:00
SignerRO ( Keypair ) ,
2021-06-11 05:29:43 -07:00
/// Program addresses for unprivileged cross calls
2021-07-05 03:16:27 -07:00
CPIProgram ( Pubkey , Box < dyn ToInstruction > ) ,
2021-06-11 05:29:43 -07:00
/// Program addresses for privileged cross calls
2021-07-05 03:16:27 -07:00
CPIProgramSigner ( Keypair , Box < dyn ToInstruction > ) ,
2021-06-08 07:50:43 -07:00
2021-07-12 04:53:46 -07:00
/// General case of nested structs not representing a CPI call
Many ( Box < dyn ToInstruction > ) ,
2021-06-11 05:29:43 -07:00
/// Key decided from SPL constants
2021-06-16 03:09:06 -07:00
Sysvar ( Pubkey ) ,
2021-06-11 05:29:43 -07:00
/// Key derived from constants and/or program address
2021-06-08 07:50:43 -07:00
Derived ( Pubkey ) ,
2021-06-11 05:29:43 -07:00
/// Key derived from constants and/or program address, read-only.
2021-06-08 07:50:43 -07:00
DerivedRO ( Pubkey ) ,
}
2021-07-12 04:53:46 -07:00
/// Types implementing Wrap can be used in top-level Solana calls as
/// the accepted, implementation-dependent AccEntry variants.
2021-06-08 07:50:43 -07:00
pub trait Wrap {
fn wrap ( _ : & AccEntry ) -> StdResult < Vec < AccountMeta > , ErrBox > ;
/// If the implementor wants to sign using other AccEntry
2021-07-05 03:16:27 -07:00
/// variants, they should override this. Multiple keypairs may be
/// used in multi-account AccEntry variants.
fn partial_signer_keypairs ( a : & AccEntry ) -> Vec < Keypair > {
2021-06-08 07:50:43 -07:00
use AccEntry ::* ;
match a {
2021-07-05 03:16:27 -07:00
// A panic on unwrap below here would imply solana keypair code does not understand its own bytes
Signer ( pair ) | SignerRO ( pair ) = > {
vec! [ Keypair ::from_bytes ( pair . to_bytes ( ) . to_vec ( ) . as_slice ( ) ) . unwrap ( ) ]
}
_other = > vec! [ ] ,
2021-06-08 07:50:43 -07:00
}
}
}
impl < ' a , ' b : ' a , T > Wrap for Signer < T >
where
T : Keyed < ' a , ' b > ,
{
fn wrap ( a : & AccEntry ) -> StdResult < Vec < AccountMeta > , ErrBox > {
use AccEntry ::* ;
match a {
Signer ( pair ) = > Ok ( vec! [ AccountMeta ::new ( pair . pubkey ( ) , true ) ] ) ,
SignerRO ( pair ) = > Ok ( vec! [ AccountMeta ::new_readonly ( pair . pubkey ( ) , true ) ] ) ,
other = > Err ( format! (
" {} must be passed as Signer or SignerRO " ,
std ::any ::type_name ::< Self > ( )
)
. into ( ) ) ,
}
}
}
impl < ' a , ' b : ' a , T , const Seed : & 'static str > Wrap for Derive < T , Seed > {
fn wrap ( a : & AccEntry ) -> StdResult < Vec < AccountMeta > , ErrBox > {
match a {
AccEntry ::Derived ( program_id ) = > {
let ( k , extra_seed ) = Pubkey ::find_program_address ( & [ Seed . as_bytes ( ) ] , & program_id ) ;
Ok ( vec! [ AccountMeta ::new ( k , false ) ] )
}
AccEntry ::DerivedRO ( program_id ) = > {
let ( k , extra_seed ) = Pubkey ::find_program_address ( & [ Seed . as_bytes ( ) ] , & program_id ) ;
Ok ( vec! [ AccountMeta ::new_readonly ( k , false ) ] )
}
other = > Err ( format! (
" {} must be passed as Derived or DerivedRO " ,
std ::any ::type_name ::< Self > ( )
)
. into ( ) ) ,
}
}
}
impl < ' a , T , const IsInitialized : AccountState > Wrap for Data < ' a , T , IsInitialized >
where
T : BorshSerialize + Owned + Default ,
{
fn wrap ( a : & AccEntry ) -> StdResult < Vec < AccountMeta > , ErrBox > {
2021-06-11 05:29:43 -07:00
use AccEntry ::* ;
use AccountState ::* ;
match IsInitialized {
Initialized = > match a {
Unprivileged ( k ) = > Ok ( vec! [ AccountMeta ::new ( * k , false ) ] ) ,
UnprivilegedRO ( k ) = > Ok ( vec! [ AccountMeta ::new_readonly ( * k , false ) ] ) ,
Signer ( pair ) = > Ok ( vec! [ AccountMeta ::new ( pair . pubkey ( ) , true ) ] ) ,
SignerRO ( pair ) = > Ok ( vec! [ AccountMeta ::new_readonly ( pair . pubkey ( ) , true ) ] ) ,
2021-06-14 09:58:12 -07:00
_other = > Err ( format! ( " {} with IsInitialized = {:?} must be passed as Unprivileged, Signer or the respective read-only variant " , std ::any ::type_name ::< Self > ( ) , a ) . into ( ) )
2021-06-11 05:29:43 -07:00
} ,
2021-07-12 04:53:46 -07:00
Uninitialized | MaybeInitialized = > match a {
2021-06-11 05:29:43 -07:00
Unprivileged ( k ) = > Ok ( vec! [ AccountMeta ::new ( * k , false ) ] ) ,
Signer ( pair ) = > Ok ( vec! [ AccountMeta ::new ( pair . pubkey ( ) , true ) ] ) ,
2021-07-12 04:53:46 -07:00
_other = > Err ( format! ( " {} with IsInitialized = {:?} must be passed as Unprivileged or Signer (write access required for initialization) " , std ::any ::type_name ::< Self > ( ) , IsInitialized ) . into ( ) )
2021-06-14 09:58:12 -07:00
}
2021-06-11 05:29:43 -07:00
}
2021-06-08 07:50:43 -07:00
}
}
2021-06-16 03:09:06 -07:00
impl < ' b , Var > Wrap for Sysvar < ' b , Var >
where
Var : SolSysvar ,
{
fn wrap ( a : & AccEntry ) -> StdResult < Vec < AccountMeta > , ErrBox > {
if let AccEntry ::Sysvar ( k ) = a {
2021-06-18 05:34:31 -07:00
if Var ::check_id ( k ) {
Ok ( vec! [ AccountMeta ::new_readonly ( k . clone ( ) , false ) ] )
} else {
Err ( format! (
" {} does not point at sysvar {} " ,
k ,
std ::any ::type_name ::< Var > ( )
)
. into ( ) )
}
2021-06-16 03:09:06 -07:00
} else {
2021-06-18 05:34:31 -07:00
Err ( format! ( " {} must be passed as Sysvar " , std ::any ::type_name ::< Self > ( ) ) . into ( ) )
2021-06-16 03:09:06 -07:00
}
}
}
2021-06-25 07:05:03 -07:00
impl < ' b > Wrap for Info < ' b > {
fn wrap ( a : & AccEntry ) -> StdResult < Vec < AccountMeta > , ErrBox > {
match a {
AccEntry ::UnprivilegedRO ( k ) = > Ok ( vec! [ AccountMeta ::new_readonly ( k . clone ( ) , false ) ] ) ,
AccEntry ::Unprivileged ( k ) = > Ok ( vec! [ AccountMeta ::new ( k . clone ( ) , false ) ] ) ,
_other = > Err ( format! (
" {} must be passed as Unprivileged or UnprivilegedRO " ,
std ::any ::type_name ::< Self > ( )
)
. into ( ) ) ,
}
}
}
2021-07-05 03:16:27 -07:00
impl < ' b , T : Wrap > Wrap for Mut < T > {
fn wrap ( a : & AccEntry ) -> StdResult < Vec < AccountMeta > , ErrBox > {
match a {
AccEntry ::Unprivileged ( _ ) | AccEntry ::Signer ( _ ) | AccEntry ::Derived ( _ ) = > {
Ok ( T ::wrap ( a ) ? )
}
_other = > Err ( format! (
" {} must be passed as a mutable AccEntry, such as Unprivileged, Signer or Derived " ,
std ::any ::type_name ::< Self > ( )
)
. into ( ) ) ,
}
}
}
impl < ' a , ' b : ' a , ' c , T > Wrap for CPICall < T > {
fn wrap ( a : & AccEntry ) -> StdResult < Vec < AccountMeta > , ErrBox > {
match a {
AccEntry ::CPIProgram ( xprog_k , xprog_accs ) = > {
let mut v = vec! [ AccountMeta ::new_readonly ( xprog_k . clone ( ) , false ) ] ;
2021-07-12 04:53:46 -07:00
v . append ( & mut xprog_accs . gen_client_metas ( ) ? ) ;
2021-07-05 03:16:27 -07:00
Ok ( v )
}
AccEntry ::CPIProgramSigner ( xprog_pair , xprog_accs ) = > {
let mut v = vec! [ AccountMeta ::new_readonly (
xprog_pair . pubkey ( ) . clone ( ) ,
false ,
) ] ;
2021-07-12 04:53:46 -07:00
v . append ( & mut xprog_accs . gen_client_metas ( ) ? ) ;
2021-07-05 03:16:27 -07:00
Ok ( v )
}
_other = > Err ( format! (
" {} must be passed as CPIProgram or CPIProgramSigner " ,
std ::any ::type_name ::< Self > ( )
)
. into ( ) ) ,
}
}
fn partial_signer_keypairs ( a : & AccEntry ) -> Vec < Keypair > {
match a {
2021-07-12 04:53:46 -07:00
AccEntry ::CPIProgram ( xprog_k , xprog_accs ) = > xprog_accs . gen_client_signers ( ) ,
2021-07-05 03:16:27 -07:00
AccEntry ::CPIProgramSigner ( xprog_pair , xprog_accs ) = > {
let mut v = vec! [ Keypair ::from_bytes ( & xprog_pair . to_bytes ( ) [ .. ] ) . unwrap ( ) ] ;
2021-07-12 04:53:46 -07:00
v . append ( & mut xprog_accs . gen_client_signers ( ) ) ;
2021-07-05 03:16:27 -07:00
v
}
_other = > vec! [ ] ,
}
}
}
2021-07-12 04:53:46 -07:00
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
2021-07-05 03:16:27 -07:00
pub trait ToInstruction : std ::fmt ::Debug {
2021-07-12 04:53:46 -07:00
/// 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 (
2021-07-05 03:16:27 -07:00
& self ,
2021-06-08 07:50:43 -07:00
program_id : Pubkey ,
ix_data : & [ u8 ] ,
) -> StdResult < ( Instruction , Vec < Keypair > ) , ErrBox > ;
2021-07-05 03:16:27 -07:00
2021-07-12 04:53:46 -07:00
/// Serialize the implementor into a vec of appropriate AccountMetas
fn gen_client_metas ( & self ) -> StdResult < Vec < AccountMeta > , ErrBox > ;
2021-07-05 03:16:27 -07:00
2021-07-12 04:53:46 -07:00
/// Gather all keypairs required by the implementor's Solana program call
fn gen_client_signers ( & self ) -> Vec < Keypair > ;
2021-06-08 07:50:43 -07:00
}