Solitaire client implementation
Change-Id: I4d1f37082537cd24a4859802652a177400f6a205
This commit is contained in:
parent
2b473f1f12
commit
c700e8847b
|
@ -12,7 +12,7 @@ let
|
|||
owner = "tilt-dev";
|
||||
repo = oldAttrs.pname;
|
||||
rev = "v0.18.4";
|
||||
sha256 = "sha256-xqBgbsrVSAOqtfHbEF07i6XIdiBXMYoR7H4Kc4xK7x0=";
|
||||
sha256 = null;
|
||||
};
|
||||
buildFlagsArray = [ "-ldflags=-X main.version=0.18.4" ];
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"programs/*",
|
||||
"client/",
|
||||
]
|
||||
|
|
|
@ -5,9 +5,12 @@ authors = ["Stan Drozd <stan@nexantic.com>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anchor-client = {git = "https://github.com/drozdziak1/anchor", branch = "anchor-debug-feature", features = ["anchor-debug"]}
|
||||
anyhow = "1.0.40"
|
||||
anchor-bridge = {path = "../programs/anchor-bridge", features = ["no-idl", "no-entrypoint", "anchor-debug"]}
|
||||
bridge = {path = "../programs/bridge", features = ["no-idl", "no-entrypoint"]}
|
||||
clap = "3.0.0-beta.2"
|
||||
rand = "0.7.3"
|
||||
shellexpand = "2.1.0"
|
||||
solana-client = "1.7.0"
|
||||
solana-program = "*"
|
||||
solana-sdk = "1.7.0"
|
||||
solitaire = { path = "../programs/solitaire" }
|
||||
|
|
|
@ -1,30 +1,37 @@
|
|||
use anchor_bridge::{
|
||||
accounts::Initialize,
|
||||
instruction::state::New,
|
||||
BridgeConfig,
|
||||
InitializeData,
|
||||
MAX_LEN_GUARDIAN_KEYS,
|
||||
use bridge::{
|
||||
api,
|
||||
client,
|
||||
instruction,
|
||||
};
|
||||
use anchor_client::{
|
||||
solana_sdk::{
|
||||
account_info::AccountInfo,
|
||||
commitment_config::CommitmentConfig,
|
||||
instruction::AccountMeta,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Keypair, Signer},
|
||||
system_instruction,
|
||||
system_program,
|
||||
sysvar,
|
||||
},
|
||||
Client,
|
||||
Cluster,
|
||||
EventContext,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use clap::Clap;
|
||||
use rand::rngs::OsRng;
|
||||
use solana_client::{
|
||||
rpc_client::RpcClient,
|
||||
rpc_config::RpcSendTransactionConfig,
|
||||
};
|
||||
use solana_program::{
|
||||
pubkey::Pubkey,
|
||||
system_instruction,
|
||||
system_program,
|
||||
sysvar,
|
||||
};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
signature::{
|
||||
read_keypair_file,
|
||||
Signer as SolSigner,
|
||||
},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solitaire::{
|
||||
AccEntry,
|
||||
Signer,
|
||||
ToInstruction,
|
||||
};
|
||||
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
error,
|
||||
mem::size_of,
|
||||
};
|
||||
|
||||
#[derive(Clap)]
|
||||
pub struct Opts {
|
||||
|
@ -32,65 +39,95 @@ pub struct Opts {
|
|||
bridge_address: Pubkey,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
pub type ErrBox = Box<dyn error::Error>;
|
||||
|
||||
fn main() -> Result<(), ErrBox> {
|
||||
let opts = Opts::parse();
|
||||
|
||||
// Wallet and cluster params.
|
||||
let payer = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json"))
|
||||
.expect("Example requires a keypair file");
|
||||
let url = Cluster::Custom(
|
||||
"http://localhost:8899".to_owned(),
|
||||
"ws://localhost:8900".to_owned(),
|
||||
);
|
||||
|
||||
let client = Client::new_with_options(url, payer, CommitmentConfig::processed());
|
||||
// Keypair is not Clone
|
||||
let payer_for_tx = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json"))
|
||||
.expect("Example requires a keypair file");
|
||||
let url = "http://localhost:8899".to_owned();
|
||||
|
||||
initialize_bridge(&client, opts.bridge_address)?;
|
||||
Ok(())
|
||||
}
|
||||
let client = RpcClient::new(url);
|
||||
|
||||
fn initialize_bridge(client: &Client, bridge_address: Pubkey) -> Result<()> {
|
||||
let program = client.program(bridge_address);
|
||||
let program_id = opts.bridge_address;
|
||||
|
||||
let guardian_set_key = Keypair::generate(&mut OsRng);
|
||||
let state_key = Keypair::generate(&mut OsRng);
|
||||
use AccEntry::*;
|
||||
let init = api::InitializeAccounts {
|
||||
bridge: Derived(program_id.clone()),
|
||||
guardian_set: Derived(program_id.clone()),
|
||||
payer: Signer(payer),
|
||||
};
|
||||
|
||||
program
|
||||
.state_request()
|
||||
.instruction(system_instruction::create_account(
|
||||
&program.payer(),
|
||||
&guardian_set_key.pubkey(),
|
||||
program.rpc().get_minimum_balance_for_rent_exemption(500)?,
|
||||
500,
|
||||
&program.id(),
|
||||
))
|
||||
.instruction(system_instruction::create_account(
|
||||
&program.payer(),
|
||||
&state_key.pubkey(),
|
||||
program.rpc().get_minimum_balance_for_rent_exemption(500)?,
|
||||
500,
|
||||
&program.id(),
|
||||
))
|
||||
.signer(&guardian_set_key)
|
||||
// .signer(&state_key)
|
||||
.accounts(Initialize {
|
||||
payer: program.payer(),
|
||||
guardian_set: guardian_set_key.pubkey(),
|
||||
state: state_key.pubkey(),
|
||||
system_program: system_program::id(),
|
||||
clock: sysvar::clock::id(),
|
||||
rent: sysvar::rent::id(),
|
||||
})
|
||||
.new(New {
|
||||
data: InitializeData {
|
||||
len_guardians: 0,
|
||||
initial_guardian_keys: [[0u8; 20]; MAX_LEN_GUARDIAN_KEYS],
|
||||
config: BridgeConfig {
|
||||
guardian_set_expiration_time: 0u32,
|
||||
},
|
||||
},
|
||||
})
|
||||
.send()?;
|
||||
let ix_data = vec![];
|
||||
|
||||
let (ix, signers) = init.to_ix(program_id, ix_data.as_slice())?;
|
||||
let (recent_blockhash, _) = client.get_recent_blockhash()?;
|
||||
|
||||
let mut tx = Transaction::new_with_payer(&[ix], Some(&payer_for_tx.pubkey()));
|
||||
|
||||
tx.sign(&signers.iter().collect::<Vec<_>>(), recent_blockhash);
|
||||
|
||||
let signature = client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
CommitmentConfig::processed(),
|
||||
RpcSendTransactionConfig {
|
||||
skip_preflight: false,
|
||||
preflight_commitment: None,
|
||||
encoding: None,
|
||||
},
|
||||
)?;
|
||||
println!("Signature: {}", signature);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn initialize_bridge(client: &Client, bridge_address: Pubkey) -> Result<()> {
|
||||
// let program = client.program(bridge_address);
|
||||
|
||||
// let guardian_set_key = Keypair::generate(&mut OsRng);
|
||||
// let state_key = Keypair::generate(&mut OsRng);
|
||||
|
||||
// program
|
||||
// .state_request()
|
||||
// .instruction(system_instruction::create_account(
|
||||
// &program.payer(),
|
||||
// &guardian_set_key.pubkey(),
|
||||
// program.rpc().get_minimum_balance_for_rent_exemption(500)?,
|
||||
// 500,
|
||||
// &program.id(),
|
||||
// ))
|
||||
// .instruction(system_instruction::create_account(
|
||||
// &program.payer(),
|
||||
// &state_key.pubkey(),
|
||||
// program.rpc().get_minimum_balance_for_rent_exemption(500)?,
|
||||
// 500,
|
||||
// &program.id(),
|
||||
// ))
|
||||
// .signer(&guardian_set_key)
|
||||
// // .signer(&state_key)
|
||||
// .accounts(Initialize {
|
||||
// payer: program.payer(),
|
||||
// guardian_set: guardian_set_key.pubkey(),
|
||||
// state: state_key.pubkey(),
|
||||
// system_program: system_program::id(),
|
||||
// clock: sysvar::clock::id(),
|
||||
// rent: sysvar::rent::id(),
|
||||
// })
|
||||
// .new(New {
|
||||
// data: InitializeData {
|
||||
// len_guardians: 0,
|
||||
// initial_guardian_keys: [[0u8; 20]; MAX_LEN_GUARDIAN_KEYS],
|
||||
// config: BridgeConfig {
|
||||
// guardian_set_expiration_time: 0u32,
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// .send()?;
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
|
|
@ -21,3 +21,4 @@ solitaire = { path = "../solitaire" }
|
|||
sha3 = "0.9.1"
|
||||
solana-program = "*"
|
||||
primitive-types = { version = "0.9.0", default-features = false }
|
||||
solana-sdk = "1.7.0"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
mod governance;
|
||||
mod initialize;
|
||||
mod post_message;
|
||||
mod post_vaa;
|
||||
mod verify_signature;
|
||||
pub mod governance;
|
||||
pub mod initialize;
|
||||
pub mod post_message;
|
||||
pub mod post_vaa;
|
||||
pub mod verify_signature;
|
||||
|
||||
pub use governance::*;
|
||||
pub use initialize::*;
|
||||
|
|
|
@ -9,7 +9,7 @@ type GuardianSet<'a> =
|
|||
Derive<Data<'a, GuardianSetData, { AccountState::Uninitialized }>, "GuardianSet">;
|
||||
type Bridge<'a> = Derive<Data<'a, BridgeData, { AccountState::Uninitialized }>, "Bridge">;
|
||||
|
||||
#[derive(FromAccounts, ToAccounts)]
|
||||
#[derive(FromAccounts, ToInstruction)]
|
||||
pub struct Initialize<'b> {
|
||||
pub bridge: Bridge<'b>,
|
||||
pub guardian_set: GuardianSet<'b>,
|
||||
|
|
|
@ -20,3 +20,4 @@ byteorder = "1.4.3"
|
|||
rocksalt = { path = "../../rocksalt" }
|
||||
sha3 = "0.9.1"
|
||||
solana-program = "*"
|
||||
solana-sdk = "1.7.0"
|
||||
|
|
|
@ -16,7 +16,10 @@ use solana_program::{
|
|||
},
|
||||
entrypoint,
|
||||
entrypoint::ProgramResult,
|
||||
instruction::AccountMeta,
|
||||
instruction::{
|
||||
AccountMeta,
|
||||
Instruction,
|
||||
},
|
||||
program::invoke_signed,
|
||||
program_error::ProgramError,
|
||||
program_pack::Pack,
|
||||
|
@ -29,6 +32,10 @@ use solana_program::{
|
|||
SysvarId,
|
||||
},
|
||||
};
|
||||
use solana_sdk::signature::{
|
||||
Keypair,
|
||||
Signer as SolSigner,
|
||||
};
|
||||
|
||||
use std::{
|
||||
io::{
|
||||
|
@ -74,6 +81,8 @@ pub use crate::{
|
|||
types::*,
|
||||
};
|
||||
|
||||
type StdResult<T, E> = std::result::Result<T, E>;
|
||||
|
||||
pub struct ExecutionContext<'a, 'b: 'a> {
|
||||
/// A reference to the program_id of the current program.
|
||||
pub program_id: &'a Pubkey,
|
||||
|
@ -98,36 +107,88 @@ impl CreationLamports {
|
|||
}
|
||||
}
|
||||
|
||||
/// The sum type for clearly specifying the accounts required on client side.
|
||||
pub enum AccEntry {
|
||||
/// Least privileged account.
|
||||
Unprivileged(Pubkey),
|
||||
|
||||
/// Accounts that need to sign a Solana call
|
||||
Signer(Keypair),
|
||||
SignerRO(Keypair),
|
||||
|
||||
/// Program addresses for privileged/unprivileged cross calls
|
||||
CPIProgram(Pubkey),
|
||||
CPIProgramSigner(Keypair),
|
||||
|
||||
/// Key decided by Wrap implementation
|
||||
Sysvar,
|
||||
Derived(Pubkey),
|
||||
DerivedRO(Pubkey),
|
||||
}
|
||||
|
||||
/// Types implementing Wrap are those that can be turned into a
|
||||
/// partial account vector tha
|
||||
/// payload.
|
||||
pub trait Wrap {
|
||||
fn wrap(&self) -> Vec<AccountMeta>;
|
||||
fn wrap(_: &AccEntry) -> StdResult<Vec<AccountMeta>, ErrBox>;
|
||||
|
||||
/// If the implementor wants to sign using other AccEntry
|
||||
/// variants, they should override this.
|
||||
fn keypair(a: AccEntry) -> Option<Keypair> {
|
||||
use AccEntry::*;
|
||||
match a {
|
||||
Signer(pair) => Some(pair),
|
||||
SignerRO(pair) => Some(pair),
|
||||
_other => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Wrap for T
|
||||
impl<'a, 'b: 'a, T> Wrap for Signer<T>
|
||||
where
|
||||
T: ToAccounts,
|
||||
T: Keyed<'a, 'b>,
|
||||
{
|
||||
fn wrap(&self) -> Vec<AccountMeta> {
|
||||
self.to()
|
||||
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<T> Wrap for Signer<T> {
|
||||
fn wrap(&self) -> Vec<AccountMeta> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
impl<T, const Seed: &'static str> Wrap for Derive<T, Seed> {
|
||||
fn wrap(&self) -> Vec<AccountMeta> {
|
||||
todo!()
|
||||
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: BorshSerialize + Owned + Default, const IsInitialized: AccountState> Wrap
|
||||
for Data<'a, T, IsInitialized>
|
||||
{
|
||||
fn wrap(&self) -> Vec<AccountMeta> {
|
||||
todo!()
|
||||
fn wrap(a: &AccEntry) -> StdResult<Vec<AccountMeta>, ErrBox> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,8 +202,9 @@ pub trait InstructionContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ToAccounts {
|
||||
fn to(&self) -> Vec<AccountMeta>;
|
||||
/// Trait used on client side to easily validate a program accounts + ix_data for a bare Solana call
|
||||
pub trait ToInstruction {
|
||||
fn to_ix(self, program_id: Pubkey, ix_data: &[u8]) -> StdResult<(Instruction, Vec<Keypair>), ErrBox>;
|
||||
}
|
||||
|
||||
/// Trait definition that describes types that can be constructed from a list of solana account
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::ops::{
|
|||
#[macro_export]
|
||||
macro_rules! solitaire {
|
||||
{ $($row:ident($kind:ty) => $fn:ident),+ $(,)* } => {
|
||||
mod instruction {
|
||||
pub mod instruction {
|
||||
use super::*;
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use solana_program::{
|
||||
|
@ -60,7 +60,7 @@ macro_rules! solitaire {
|
|||
|
||||
/// This module contains a 1-1 mapping for each function to an enum variant. The variants
|
||||
/// can be matched to the Instruction found above.
|
||||
mod client {
|
||||
pub mod client {
|
||||
use super::*;
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{instruction::Instruction, pubkey::Pubkey};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![allow(warnings)]
|
||||
|
||||
mod to_accounts;
|
||||
mod to_instruction;
|
||||
|
||||
use to_accounts::*;
|
||||
use to_instruction::*;
|
||||
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
|
@ -12,7 +12,7 @@ use solana_program::{
|
|||
};
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{
|
||||
quote,
|
||||
quote_spanned,
|
||||
|
@ -30,21 +30,41 @@ use syn::{
|
|||
Index,
|
||||
};
|
||||
|
||||
#[proc_macro_derive(ToAccounts)]
|
||||
pub fn derive_to_accounts(input: TokenStream) -> TokenStream {
|
||||
#[proc_macro_derive(ToInstruction)]
|
||||
pub fn derive_to_instruction(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = input.ident;
|
||||
let to_method_body = generate_to_method(&name, &input.data);
|
||||
|
||||
let expanded = quote! {
|
||||
/// Macro-generated implementation of ToAccounts by Solitaire.
|
||||
impl<'a> solitaire::ToAccounts for #name<'a> {
|
||||
fn to(&self) -> Vec<solana_program::instruction::AccountMeta> {
|
||||
#to_method_body
|
||||
}
|
||||
// Type params of the instruction context account
|
||||
let type_params: Vec<GenericParam> = input
|
||||
.generics
|
||||
.type_params()
|
||||
.map(|v| GenericParam::Type(v.clone()))
|
||||
.collect();
|
||||
|
||||
// Generics lifetimes of the peel type
|
||||
let mut peel_g = input.generics.clone();
|
||||
peel_g.params = parse_quote!('a, 'b: 'a, 'c);
|
||||
let (_, peel_type_g, _) = peel_g.split_for_impl();
|
||||
|
||||
// Params of the instruction context
|
||||
let mut type_generics = input.generics.clone();
|
||||
type_generics.params = parse_quote!('b);
|
||||
for x in &type_params {
|
||||
type_generics.params.push(x.clone());
|
||||
}
|
||||
};
|
||||
let (type_impl_g, type_g, _) = type_generics.split_for_impl();
|
||||
|
||||
// Combined lifetimes of peel and the instruction context
|
||||
let mut combined_generics = Generics::default();
|
||||
combined_generics.params = peel_g.params.clone();
|
||||
for x in &type_params {
|
||||
combined_generics.params.push(x.clone());
|
||||
}
|
||||
let (combined_impl_g, _, _) = combined_generics.split_for_impl();
|
||||
|
||||
let from_method = generate_fields(&name, &input.data);
|
||||
let expanded = generate_to_instruction(&name, &combined_impl_g, &input.data);
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
//! Derive macro logic for ToAccounts
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{
|
||||
quote,
|
||||
quote_spanned,
|
||||
};
|
||||
use syn::{
|
||||
parse_macro_input,
|
||||
parse_quote,
|
||||
spanned::Spanned,
|
||||
Data,
|
||||
DataStruct,
|
||||
DeriveInput,
|
||||
Fields,
|
||||
GenericParam,
|
||||
Generics,
|
||||
Index,
|
||||
};
|
||||
|
||||
pub fn generate_to_method(name: &syn::Ident, data: &Data) -> TokenStream2 {
|
||||
match *data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(ref fields),
|
||||
..
|
||||
}) => {
|
||||
let expanded_fields = fields.named.iter().map(|field| {
|
||||
let name = &field.ident;
|
||||
|
||||
quote! {
|
||||
v.append(&mut solitaire::Wrap::wrap(&self.#name))
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
let mut v = Vec::new();
|
||||
#(#expanded_fields;)*
|
||||
v
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
//! Derive macro logic for ToInstruction
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{
|
||||
Span,
|
||||
TokenStream as TokenStream2,
|
||||
};
|
||||
use quote::{
|
||||
quote,
|
||||
quote_spanned,
|
||||
};
|
||||
use syn::{
|
||||
parse_macro_input,
|
||||
parse_quote,
|
||||
spanned::Spanned,
|
||||
Data,
|
||||
DataStruct,
|
||||
DeriveInput,
|
||||
Fields,
|
||||
GenericParam,
|
||||
Generics,
|
||||
Index,
|
||||
};
|
||||
|
||||
pub fn generate_to_instruction(name: &syn::Ident, impl_generics: &syn::ImplGenerics, data: &Data) -> TokenStream2 {
|
||||
match *data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(ref fields),
|
||||
..
|
||||
}) => {
|
||||
let expanded_appends = fields.named.iter().map(|field| {
|
||||
let name = &field.ident;
|
||||
let ty = &field.ty;
|
||||
|
||||
quote! {
|
||||
account_metas.append(&mut <#ty as solitaire::Wrap>::wrap(&self.#name)?);
|
||||
if let Some(pair) = <#ty as solitaire::Wrap>::keypair(self.#name) {
|
||||
signers.push(pair);
|
||||
}
|
||||
}
|
||||
});
|
||||
let client_struct_name =
|
||||
syn::Ident::new(&format!("{}Accounts", name.to_string()), Span::call_site());
|
||||
|
||||
let client_struct_decl = generate_clientside_struct(&name, &client_struct_name, &data);
|
||||
|
||||
|
||||
quote! {
|
||||
/// Solitaire-generated client-side #name representation
|
||||
#client_struct_decl
|
||||
|
||||
/// Solitaire-generatied ToInstruction implementation
|
||||
impl #impl_generics solitaire::ToInstruction for #client_struct_name {
|
||||
fn to_ix(
|
||||
self,
|
||||
program_id: solana_program::pubkey::Pubkey,
|
||||
ix_data: &[u8]) -> std::result::Result<
|
||||
(solana_program::instruction::Instruction, Vec<solana_sdk::signer::keypair::Keypair>),
|
||||
solitaire::ErrBox
|
||||
> {
|
||||
use solana_program::{pubkey::Pubkey, instruction::Instruction};
|
||||
let mut account_metas = Vec::new();
|
||||
let mut signers = Vec::new();
|
||||
|
||||
#(#expanded_appends;)*
|
||||
|
||||
Ok((solana_program::instruction::Instruction::new_with_bytes(program_id,
|
||||
ix_data,
|
||||
account_metas), signers))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_clientside_struct(name: &syn::Ident, client_struct_name: &syn::Ident, data: &Data) -> TokenStream2 {
|
||||
match *data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(ref fields),
|
||||
..
|
||||
}) => {
|
||||
let expanded_fields = fields.named.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
|
||||
quote! {
|
||||
#field_name: solitaire::AccEntry
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
pub struct #client_struct_name {
|
||||
#(pub #expanded_fields,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ byteorder = "1.3.4"
|
|||
zerocopy = "0.3.0"
|
||||
sha3 = "0.9.1"
|
||||
primitive-types = { version = "0.7.2", default-features = false }
|
||||
solana-sdk = "1.7.0"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { version = "0.7.0" }
|
||||
|
|
Loading…
Reference in New Issue