Merge pull request #77 from project-serum/armani/fns
This commit is contained in:
commit
d20363fb2c
|
@ -11,8 +11,13 @@ incremented for features.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Embed workspace programs into local validator genesis when testing.
|
||||
* cli: Stream program logs to `.anchor/program-logs` directory when testing.
|
||||
* spl: Add shared memory api.
|
||||
* lang/attribute/access-control: Allow specifying multiple modifier functions.
|
||||
* lang/syn: Allow state structs that don't have a ctor or impl block (just trait implementations).
|
||||
|
||||
## [0.2.0] - 2021-02-08
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.8",
|
||||
"regex",
|
||||
"syn 1.0.57",
|
||||
]
|
||||
|
||||
|
@ -181,6 +182,7 @@ name = "anchor-spl"
|
|||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"solana-program",
|
||||
"spl-token 3.0.1",
|
||||
]
|
||||
|
||||
|
|
|
@ -13,15 +13,7 @@ pub mod counter_auth {
|
|||
use super::*;
|
||||
|
||||
#[state]
|
||||
pub struct CounterAuth {}
|
||||
|
||||
// TODO: remove this impl block after addressing
|
||||
// https://github.com/project-serum/anchor/issues/71.
|
||||
impl CounterAuth {
|
||||
pub fn new(_ctx: Context<Empty>) -> Result<Self, ProgramError> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
pub struct CounterAuth;
|
||||
|
||||
impl<'info> Auth<'info, Empty> for CounterAuth {
|
||||
fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> ProgramResult {
|
||||
|
|
|
@ -7,7 +7,7 @@ pub fn available_for_withdrawal(vesting: &Vesting, current_ts: i64) -> u64 {
|
|||
}
|
||||
|
||||
// The amount of funds currently in the vault.
|
||||
pub fn balance(vesting: &Vesting) -> u64 {
|
||||
fn balance(vesting: &Vesting) -> u64 {
|
||||
vesting
|
||||
.outstanding
|
||||
.checked_sub(vesting.whitelist_owned)
|
||||
|
@ -33,12 +33,13 @@ fn withdrawn_amount(vesting: &Vesting) -> u64 {
|
|||
// Returns the total vested amount up to the given ts, assuming zero
|
||||
// withdrawals and zero funds sent to other programs.
|
||||
fn total_vested(vesting: &Vesting, current_ts: i64) -> u64 {
|
||||
assert!(current_ts >= vesting.start_ts);
|
||||
|
||||
if current_ts >= vesting.end_ts {
|
||||
return vesting.start_balance;
|
||||
if current_ts < vesting.start_ts {
|
||||
0
|
||||
} else if current_ts >= vesting.end_ts {
|
||||
vesting.start_balance
|
||||
} else {
|
||||
linear_unlock(vesting, current_ts).unwrap()
|
||||
}
|
||||
linear_unlock(vesting, current_ts).unwrap()
|
||||
}
|
||||
|
||||
fn linear_unlock(vesting: &Vesting, current_ts: i64) -> Option<u64> {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#![feature(proc_macro_hygiene)]
|
||||
|
||||
use anchor_lang::prelude::*;
|
||||
use anchor_lang::solana_program;
|
||||
use anchor_lang::solana_program::instruction::Instruction;
|
||||
use anchor_lang::solana_program::program;
|
||||
use anchor_spl::token::{self, TokenAccount, Transfer};
|
||||
|
||||
mod calculator;
|
||||
|
@ -18,7 +18,8 @@ pub mod lockup {
|
|||
pub struct Lockup {
|
||||
/// The key with the ability to change the whitelist.
|
||||
pub authority: Pubkey,
|
||||
/// Valid programs the program can relay transactions to.
|
||||
/// List of programs locked tokens can be sent to. These programs
|
||||
/// are completely trusted to maintain the locked property.
|
||||
pub whitelist: Vec<WhitelistEntry>,
|
||||
}
|
||||
|
||||
|
@ -70,25 +71,19 @@ pub mod lockup {
|
|||
pub fn create_vesting(
|
||||
ctx: Context<CreateVesting>,
|
||||
beneficiary: Pubkey,
|
||||
end_ts: i64,
|
||||
period_count: u64,
|
||||
deposit_amount: u64,
|
||||
nonce: u8,
|
||||
start_ts: i64,
|
||||
end_ts: i64,
|
||||
period_count: u64,
|
||||
realizor: Option<Realizor>,
|
||||
) -> Result<()> {
|
||||
if end_ts <= ctx.accounts.clock.unix_timestamp {
|
||||
return Err(ErrorCode::InvalidTimestamp.into());
|
||||
}
|
||||
if period_count > (end_ts - ctx.accounts.clock.unix_timestamp) as u64 {
|
||||
return Err(ErrorCode::InvalidPeriod.into());
|
||||
}
|
||||
if period_count == 0 {
|
||||
return Err(ErrorCode::InvalidPeriod.into());
|
||||
}
|
||||
if deposit_amount == 0 {
|
||||
return Err(ErrorCode::InvalidDepositAmount.into());
|
||||
}
|
||||
|
||||
if !is_valid_schedule(start_ts, end_ts, period_count) {
|
||||
return Err(ErrorCode::InvalidSchedule.into());
|
||||
}
|
||||
let vesting = &mut ctx.accounts.vesting;
|
||||
vesting.beneficiary = beneficiary;
|
||||
vesting.mint = ctx.accounts.vault.mint;
|
||||
|
@ -96,7 +91,8 @@ pub mod lockup {
|
|||
vesting.period_count = period_count;
|
||||
vesting.start_balance = deposit_amount;
|
||||
vesting.end_ts = end_ts;
|
||||
vesting.start_ts = ctx.accounts.clock.unix_timestamp;
|
||||
vesting.start_ts = start_ts;
|
||||
vesting.created_ts = ctx.accounts.clock.unix_timestamp;
|
||||
vesting.outstanding = deposit_amount;
|
||||
vesting.whitelist_owned = 0;
|
||||
vesting.grantor = *ctx.accounts.depositor_authority.key;
|
||||
|
@ -321,9 +317,10 @@ pub struct Vesting {
|
|||
/// originally deposited.
|
||||
pub start_balance: u64,
|
||||
/// The unix timestamp at which this vesting account was created.
|
||||
pub created_ts: i64,
|
||||
/// The time at which vesting begins.
|
||||
pub start_ts: i64,
|
||||
/// The ts at which all the tokens associated with this account
|
||||
/// should be vested.
|
||||
/// The time at which all tokens are vested.
|
||||
pub end_ts: i64,
|
||||
/// The number of times vesting will occur. For example, if vesting
|
||||
/// is once a year over seven years, this will be 7.
|
||||
|
@ -400,6 +397,8 @@ pub enum ErrorCode {
|
|||
InvalidLockRealizor,
|
||||
#[msg("You have not realized this vesting account.")]
|
||||
UnrealizedVesting,
|
||||
#[msg("Invalid vesting schedule given.")]
|
||||
InvalidSchedule,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, 'info> From<&mut CreateVesting<'info>>
|
||||
|
@ -471,8 +470,7 @@ pub fn whitelist_relay_cpi<'info>(
|
|||
let signer = &[&seeds[..]];
|
||||
let mut accounts = transfer.to_account_infos();
|
||||
accounts.extend_from_slice(&remaining_accounts);
|
||||
solana_program::program::invoke_signed(&relay_instruction, &accounts, signer)
|
||||
.map_err(Into::into)
|
||||
program::invoke_signed(&relay_instruction, &accounts, signer).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn is_whitelisted<'info>(transfer: &WhitelistTransfer<'info>) -> Result<()> {
|
||||
|
@ -491,10 +489,23 @@ fn whitelist_auth(lockup: &Lockup, ctx: &Context<Auth>) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_valid_schedule(start_ts: i64, end_ts: i64, period_count: u64) -> bool {
|
||||
if end_ts <= start_ts {
|
||||
return false;
|
||||
}
|
||||
if period_count > (end_ts - start_ts) as u64 {
|
||||
return false;
|
||||
}
|
||||
if period_count == 0 {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// Returns Ok if the locked vesting account has been "realized". Realization
|
||||
// is application dependent. For example, in the case of staking, one must first
|
||||
// unstake before being able to earn locked tokens.
|
||||
fn is_realized<'info>(ctx: &Context<Withdraw>) -> Result<()> {
|
||||
fn is_realized(ctx: &Context<Withdraw>) -> Result<()> {
|
||||
if let Some(realizor) = &ctx.accounts.vesting.realizor {
|
||||
let cpi_program = {
|
||||
let p = ctx.remaining_accounts[0].clone();
|
||||
|
|
|
@ -358,6 +358,16 @@ mod registry {
|
|||
if ctx.accounts.clock.unix_timestamp >= expiry_ts {
|
||||
return Err(ErrorCode::InvalidExpiry.into());
|
||||
}
|
||||
if let RewardVendorKind::Locked {
|
||||
start_ts,
|
||||
end_ts,
|
||||
period_count,
|
||||
} = kind
|
||||
{
|
||||
if !lockup::is_valid_schedule(start_ts, end_ts, period_count) {
|
||||
return Err(ErrorCode::InvalidVestingSchedule.into());
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer funds into the vendor's vault.
|
||||
token::transfer(ctx.accounts.into(), total)?;
|
||||
|
@ -384,7 +394,7 @@ mod registry {
|
|||
vendor.from = *ctx.accounts.depositor_authority.key;
|
||||
vendor.total = total;
|
||||
vendor.expired = false;
|
||||
vendor.kind = kind.clone();
|
||||
vendor.kind = kind;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -434,12 +444,13 @@ mod registry {
|
|||
ctx: Context<'a, 'b, 'c, 'info, ClaimRewardLocked<'info>>,
|
||||
nonce: u8,
|
||||
) -> Result<()> {
|
||||
let (end_ts, period_count) = match ctx.accounts.cmn.vendor.kind {
|
||||
let (start_ts, end_ts, period_count) = match ctx.accounts.cmn.vendor.kind {
|
||||
RewardVendorKind::Unlocked => return Err(ErrorCode::ExpectedLockedVendor.into()),
|
||||
RewardVendorKind::Locked {
|
||||
start_ts,
|
||||
end_ts,
|
||||
period_count,
|
||||
} => (end_ts, period_count),
|
||||
} => (start_ts, end_ts, period_count),
|
||||
};
|
||||
|
||||
// Reward distribution.
|
||||
|
@ -452,19 +463,6 @@ mod registry {
|
|||
.unwrap();
|
||||
assert!(reward_amount > 0);
|
||||
|
||||
// The lockup program requires the timestamp to be >= clock's timestamp.
|
||||
// So update if the time has already passed.
|
||||
//
|
||||
// If the reward is within `period_count` seconds of fully vesting, then
|
||||
// we bump the `end_ts` because, otherwise, the vesting account would
|
||||
// fail to be created. Vesting must have no more frequently than the
|
||||
// smallest unit of time, once per second, expressed as
|
||||
// `period_count <= end_ts - start_ts`.
|
||||
let end_ts = match end_ts < ctx.accounts.cmn.clock.unix_timestamp + period_count as i64 {
|
||||
true => ctx.accounts.cmn.clock.unix_timestamp + period_count as i64,
|
||||
false => end_ts,
|
||||
};
|
||||
|
||||
// Specify the vesting account's realizor, so that unlocks can only
|
||||
// execute once completely unstaked.
|
||||
let realizor = Some(Realizor {
|
||||
|
@ -487,10 +485,11 @@ mod registry {
|
|||
lockup::cpi::create_vesting(
|
||||
cpi_ctx,
|
||||
ctx.accounts.cmn.member.beneficiary,
|
||||
end_ts,
|
||||
period_count,
|
||||
reward_amount,
|
||||
nonce,
|
||||
start_ts,
|
||||
end_ts,
|
||||
period_count,
|
||||
realizor,
|
||||
)?;
|
||||
|
||||
|
@ -1165,7 +1164,11 @@ pub struct RewardVendor {
|
|||
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
|
||||
pub enum RewardVendorKind {
|
||||
Unlocked,
|
||||
Locked { end_ts: i64, period_count: u64 },
|
||||
Locked {
|
||||
start_ts: i64,
|
||||
end_ts: i64,
|
||||
period_count: u64,
|
||||
},
|
||||
}
|
||||
|
||||
#[error]
|
||||
|
@ -1216,6 +1219,8 @@ pub enum ErrorCode {
|
|||
InvalidBeneficiary,
|
||||
#[msg("The given member account does not match the realizor metadata.")]
|
||||
InvalidRealizorMetadata,
|
||||
#[msg("Invalid vesting schedule for the locked reward.")]
|
||||
InvalidVestingSchedule,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, 'info> From<&mut Deposit<'info>>
|
||||
|
|
|
@ -12,6 +12,7 @@ describe("Lockup and Registry", () => {
|
|||
anchor.setProvider(provider);
|
||||
|
||||
const lockup = anchor.workspace.Lockup;
|
||||
const linear = anchor.workspace.Linear;
|
||||
const registry = anchor.workspace.Registry;
|
||||
|
||||
let lockupAddress = null;
|
||||
|
@ -138,9 +139,10 @@ describe("Lockup and Registry", () => {
|
|||
let vestingSigner = null;
|
||||
|
||||
it("Creates a vesting account", async () => {
|
||||
const beneficiary = provider.wallet.publicKey;
|
||||
const endTs = new anchor.BN(Date.now() / 1000 + 5);
|
||||
const startTs = new anchor.BN(Date.now() / 1000);
|
||||
const endTs = new anchor.BN(startTs.toNumber() + 5);
|
||||
const periodCount = new anchor.BN(2);
|
||||
const beneficiary = provider.wallet.publicKey;
|
||||
const depositAmount = new anchor.BN(100);
|
||||
|
||||
const vault = new anchor.web3.Account();
|
||||
|
@ -155,10 +157,11 @@ describe("Lockup and Registry", () => {
|
|||
|
||||
await lockup.rpc.createVesting(
|
||||
beneficiary,
|
||||
endTs,
|
||||
periodCount,
|
||||
depositAmount,
|
||||
nonce,
|
||||
startTs,
|
||||
endTs,
|
||||
periodCount,
|
||||
null, // Lock realizor is None.
|
||||
{
|
||||
accounts: {
|
||||
|
@ -190,11 +193,11 @@ describe("Lockup and Registry", () => {
|
|||
assert.ok(vestingAccount.grantor.equals(provider.wallet.publicKey));
|
||||
assert.ok(vestingAccount.outstanding.eq(depositAmount));
|
||||
assert.ok(vestingAccount.startBalance.eq(depositAmount));
|
||||
assert.ok(vestingAccount.endTs.eq(endTs));
|
||||
assert.ok(vestingAccount.periodCount.eq(periodCount));
|
||||
assert.ok(vestingAccount.whitelistOwned.eq(new anchor.BN(0)));
|
||||
assert.equal(vestingAccount.nonce, nonce);
|
||||
assert.ok(endTs.gt(vestingAccount.startTs));
|
||||
assert.ok(vestingAccount.createdTs.gt(new anchor.BN(0)));
|
||||
assert.ok(vestingAccount.startTs.eq(startTs));
|
||||
assert.ok(vestingAccount.endTs.eq(endTs));
|
||||
assert.ok(vestingAccount.realizor === null);
|
||||
});
|
||||
|
||||
|
@ -582,6 +585,7 @@ describe("Lockup and Registry", () => {
|
|||
it("Drops a locked reward", async () => {
|
||||
lockedRewardKind = {
|
||||
locked: {
|
||||
startTs: new anchor.BN(Date.now() / 1000),
|
||||
endTs: new anchor.BN(Date.now() / 1000 + 6),
|
||||
periodCount: new anchor.BN(2),
|
||||
},
|
||||
|
|
|
@ -15,4 +15,5 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "=1.0.57", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.2.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.2.0" }
|
||||
regex = "1.0"
|
|
@ -50,7 +50,18 @@ pub fn access_control(
|
|||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let access_control: proc_macro2::TokenStream = args.to_string().parse().unwrap();
|
||||
let mut args = args.to_string();
|
||||
args.retain(|c| !c.is_whitespace());
|
||||
let access_control: Vec<proc_macro2::TokenStream> = args
|
||||
.split(')')
|
||||
.filter_map(|ac| match ac {
|
||||
"" => None,
|
||||
_ => Some(ac),
|
||||
})
|
||||
.map(|ac| format!("{})", ac)) // Put back on the split char.
|
||||
.map(|ac| format!("{}?;", ac)) // Add `?;` syntax.
|
||||
.map(|ac| ac.parse().unwrap())
|
||||
.collect();
|
||||
|
||||
let item_fn = parse_macro_input!(input as syn::ItemFn);
|
||||
|
||||
|
@ -63,7 +74,7 @@ pub fn access_control(
|
|||
proc_macro::TokenStream::from(quote! {
|
||||
#fn_vis #fn_sig {
|
||||
|
||||
#access_control?;
|
||||
#(#access_control)*
|
||||
|
||||
#(#fn_stmts)*
|
||||
}
|
||||
|
|
|
@ -23,6 +23,15 @@ impl<'info, T: solana_program::sysvar::Sysvar> Sysvar<'info, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'info, T: solana_program::sysvar::Sysvar> Clone for Sysvar<'info, T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
info: self.info.clone(),
|
||||
account: T::from_account_info(&self.info).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info, T: solana_program::sysvar::Sysvar> Accounts<'info> for Sysvar<'info, T> {
|
||||
fn try_accounts(
|
||||
_program_id: &Pubkey,
|
||||
|
|
|
@ -69,39 +69,14 @@ pub fn generate_dispatch(program: &Program) -> proc_macro2::TokenStream {
|
|||
// Dispatch the state constructor.
|
||||
let ctor_state_dispatch_arm = match &program.state {
|
||||
None => quote! { /* no-op */ },
|
||||
Some(state) => {
|
||||
let variant_arm = generate_ctor_variant(state);
|
||||
let ctor_args = generate_ctor_args(state);
|
||||
let ix_name: proc_macro2::TokenStream = generate_ctor_variant_name().parse().unwrap();
|
||||
let sighash_arr = sighash_ctor();
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
#sighash_tts => {
|
||||
let ix = instruction::#ix_name::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let instruction::#variant_arm = ix;
|
||||
__private::__ctor(program_id, accounts, #(#ctor_args),*)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Dispatch the state impl instructions.
|
||||
let state_dispatch_arms: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(s) => s
|
||||
.methods
|
||||
.iter()
|
||||
.map(|rpc: &crate::StateRpc| {
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let name = &rpc.raw_method.sig.ident.to_string();
|
||||
let rpc_name: proc_macro2::TokenStream = { format!("__{}", name).parse().unwrap() };
|
||||
let variant_arm =
|
||||
generate_ix_variant(rpc.raw_method.sig.ident.to_string(), &rpc.args, true);
|
||||
let ix_name = generate_ix_variant_name(rpc.raw_method.sig.ident.to_string(), true);
|
||||
let sighash_arr = sighash(SIGHASH_STATE_NAMESPACE, &name);
|
||||
Some(state) => match state.ctor_and_anchor.is_some() {
|
||||
false => quote! {},
|
||||
true => {
|
||||
let variant_arm = generate_ctor_variant(state);
|
||||
let ctor_args = generate_ctor_args(state);
|
||||
let ix_name: proc_macro2::TokenStream =
|
||||
generate_ctor_variant_name().parse().unwrap();
|
||||
let sighash_arr = sighash_ctor();
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
|
@ -109,11 +84,50 @@ pub fn generate_dispatch(program: &Program) -> proc_macro2::TokenStream {
|
|||
let ix = instruction::#ix_name::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let instruction::#variant_arm = ix;
|
||||
__private::#rpc_name(program_id, accounts, #(#rpc_arg_names),*)
|
||||
__private::__ctor(program_id, accounts, #(#ctor_args),*)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Dispatch the state impl instructions.
|
||||
let state_dispatch_arms: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(s) => s
|
||||
.impl_block_and_methods
|
||||
.as_ref()
|
||||
.map(|(_impl_block, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|rpc: &crate::StateRpc| {
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let name = &rpc.raw_method.sig.ident.to_string();
|
||||
let rpc_name: proc_macro2::TokenStream =
|
||||
{ format!("__{}", name).parse().unwrap() };
|
||||
let variant_arm = generate_ix_variant(
|
||||
rpc.raw_method.sig.ident.to_string(),
|
||||
&rpc.args,
|
||||
true,
|
||||
);
|
||||
let ix_name =
|
||||
generate_ix_variant_name(rpc.raw_method.sig.ident.to_string(), true);
|
||||
let sighash_arr = sighash(SIGHASH_STATE_NAMESPACE, &name);
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
#sighash_tts => {
|
||||
let ix = instruction::#ix_name::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let instruction::#variant_arm = ix;
|
||||
__private::#rpc_name(program_id, accounts, #(#rpc_arg_names),*)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
.unwrap_or(vec![]),
|
||||
};
|
||||
|
||||
// Dispatch all trait interface implementations.
|
||||
|
@ -121,54 +135,59 @@ pub fn generate_dispatch(program: &Program) -> proc_macro2::TokenStream {
|
|||
None => vec![],
|
||||
Some(s) => s
|
||||
.interfaces
|
||||
.iter()
|
||||
.flat_map(|iface: &crate::StateInterface| {
|
||||
iface
|
||||
.methods
|
||||
.as_ref()
|
||||
.map(|interfaces| {
|
||||
interfaces
|
||||
.iter()
|
||||
.map(|m: &crate::StateRpc| {
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
m.args.iter().map(|arg| &arg.name).collect();
|
||||
let name = &m.raw_method.sig.ident.to_string();
|
||||
let rpc_name: proc_macro2::TokenStream = format!("__{}_{}", iface.trait_name, name).parse().unwrap();
|
||||
let raw_args: Vec<&syn::PatType> = m
|
||||
.args
|
||||
.flat_map(|iface: &crate::StateInterface| {
|
||||
iface
|
||||
.methods
|
||||
.iter()
|
||||
.map(|arg: &crate::RpcArg| &arg.raw_arg)
|
||||
.collect();
|
||||
let sighash_arr = sighash(&iface.trait_name, &m.ident.to_string());
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
let args_struct = {
|
||||
if m.args.len() == 0 {
|
||||
.map(|m: &crate::StateRpc| {
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
m.args.iter().map(|arg| &arg.name).collect();
|
||||
let name = &m.raw_method.sig.ident.to_string();
|
||||
let rpc_name: proc_macro2::TokenStream = format!("__{}_{}", iface.trait_name, name).parse().unwrap();
|
||||
let raw_args: Vec<&syn::PatType> = m
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg: &crate::RpcArg| &arg.raw_arg)
|
||||
.collect();
|
||||
let sighash_arr = sighash(&iface.trait_name, &m.ident.to_string());
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
let args_struct = {
|
||||
if m.args.len() == 0 {
|
||||
quote! {
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args {
|
||||
#(#raw_args),*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args {
|
||||
#(#raw_args),*
|
||||
#sighash_tts => {
|
||||
#args_struct
|
||||
let ix = Args::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let Args {
|
||||
#(#rpc_arg_names),*
|
||||
} = ix;
|
||||
__private::#rpc_name(program_id, accounts, #(#rpc_arg_names),*)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
#sighash_tts => {
|
||||
#args_struct
|
||||
let ix = Args::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let Args {
|
||||
#(#rpc_arg_names),*
|
||||
} = ix;
|
||||
__private::#rpc_name(program_id, accounts, #(#rpc_arg_names),*)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<proc_macro2::TokenStream>>()
|
||||
})
|
||||
.collect::<Vec<proc_macro2::TokenStream>>()
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
.unwrap_or(vec![])
|
||||
};
|
||||
|
||||
// Dispatch all global instructions.
|
||||
|
@ -254,6 +273,12 @@ pub fn generate_non_inlined_handlers(program: &Program) -> proc_macro2::TokenStr
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[cfg(feature = "no-idl")]
|
||||
pub fn __idl(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> ProgramResult {
|
||||
Err(anchor_lang::solana_program::program_error::ProgramError::Custom(99))
|
||||
}
|
||||
|
||||
// One time IDL account initializer. Will faill on subsequent
|
||||
// invocations.
|
||||
#[inline(never)]
|
||||
|
@ -346,234 +371,246 @@ pub fn generate_non_inlined_handlers(program: &Program) -> proc_macro2::TokenStr
|
|||
};
|
||||
let non_inlined_ctor: proc_macro2::TokenStream = match &program.state {
|
||||
None => quote! {},
|
||||
Some(state) => {
|
||||
let ctor_typed_args = generate_ctor_typed_args(state);
|
||||
let ctor_untyped_args = generate_ctor_args(state);
|
||||
let name = &state.strct.ident;
|
||||
let mod_name = &program.name;
|
||||
let anchor_ident = &state.ctor_anchor;
|
||||
quote! {
|
||||
// One time state account initializer. Will faill on subsequent
|
||||
// invocations.
|
||||
#[inline(never)]
|
||||
pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], #(#ctor_typed_args),*) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
|
||||
// Deserialize accounts.
|
||||
let ctor_accounts = anchor_lang::Ctor::try_accounts(program_id, &mut remaining_accounts)?;
|
||||
let mut ctor_user_def_accounts = #anchor_ident::try_accounts(program_id, &mut remaining_accounts)?;
|
||||
|
||||
// Invoke the ctor.
|
||||
let instance = #mod_name::#name::new(
|
||||
anchor_lang::Context::new(
|
||||
program_id,
|
||||
&mut ctor_user_def_accounts,
|
||||
remaining_accounts,
|
||||
),
|
||||
#(#ctor_untyped_args),*
|
||||
)?;
|
||||
|
||||
// Create the solana account for the ctor data.
|
||||
let from = ctor_accounts.from.key;
|
||||
let (base, nonce) = Pubkey::find_program_address(&[], ctor_accounts.program.key);
|
||||
let seed = anchor_lang::ProgramState::<#name>::seed();
|
||||
let owner = ctor_accounts.program.key;
|
||||
let to = Pubkey::create_with_seed(&base, seed, owner).unwrap();
|
||||
// Add 8 for the account discriminator.
|
||||
let space = 8 + instance.try_to_vec().map_err(|_| ProgramError::Custom(1))?.len();
|
||||
let lamports = ctor_accounts.rent.minimum_balance(space);
|
||||
let seeds = &[&[nonce][..]];
|
||||
let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed(
|
||||
from,
|
||||
&to,
|
||||
&base,
|
||||
seed,
|
||||
lamports,
|
||||
space as u64,
|
||||
owner,
|
||||
);
|
||||
anchor_lang::solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&[
|
||||
ctor_accounts.from.clone(),
|
||||
ctor_accounts.to.clone(),
|
||||
ctor_accounts.base.clone(),
|
||||
ctor_accounts.system_program.clone(),
|
||||
],
|
||||
&[seeds],
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
ctor_user_def_accounts.exit(program_id)?;
|
||||
let mut data = ctor_accounts.to.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
instance.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let non_inlined_state_handlers: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(state) => state
|
||||
.methods
|
||||
.iter()
|
||||
.map(|rpc| {
|
||||
let rpc_params: Vec<_> = rpc.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let private_rpc_name: proc_macro2::TokenStream = {
|
||||
let n = format!("__{}", &rpc.raw_method.sig.ident.to_string());
|
||||
n.parse().unwrap()
|
||||
};
|
||||
let rpc_name = &rpc.raw_method.sig.ident;
|
||||
let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
Some(state) => match state.ctor_and_anchor.as_ref() {
|
||||
None => quote! {},
|
||||
Some((_ctor, anchor_ident)) => {
|
||||
let ctor_typed_args = generate_ctor_typed_args(state);
|
||||
let ctor_untyped_args = generate_ctor_args(state);
|
||||
let name = &state.strct.ident;
|
||||
let mod_name = &program.name;
|
||||
quote! {
|
||||
// One time state account initializer. Will faill on subsequent
|
||||
// invocations.
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
|
||||
pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], #(#ctor_typed_args),*) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.len() == 0 {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
let state_account = &remaining_accounts[0];
|
||||
let mut state: #state_ty = {
|
||||
let data = state_account.try_borrow_data()?;
|
||||
let mut sliced: &[u8] = &data;
|
||||
anchor_lang::AccountDeserialize::try_deserialize(&mut sliced)?
|
||||
};
|
||||
// Deserialize accounts.
|
||||
let ctor_accounts = anchor_lang::Ctor::try_accounts(program_id, &mut remaining_accounts)?;
|
||||
let mut ctor_user_def_accounts = #anchor_ident::try_accounts(program_id, &mut remaining_accounts)?;
|
||||
|
||||
remaining_accounts = &remaining_accounts[1..];
|
||||
|
||||
// Deserialize the program's execution context.
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
// Invoke the ctor.
|
||||
let instance = #mod_name::#name::new(
|
||||
anchor_lang::Context::new(
|
||||
program_id,
|
||||
&mut ctor_user_def_accounts,
|
||||
remaining_accounts,
|
||||
),
|
||||
#(#ctor_untyped_args),*
|
||||
)?;
|
||||
let ctx = Context::new(program_id, &mut accounts, remaining_accounts);
|
||||
|
||||
// Execute user defined function.
|
||||
state.#rpc_name(
|
||||
ctx,
|
||||
#(#rpc_arg_names),*
|
||||
// Create the solana account for the ctor data.
|
||||
let from = ctor_accounts.from.key;
|
||||
let (base, nonce) = Pubkey::find_program_address(&[], ctor_accounts.program.key);
|
||||
let seed = anchor_lang::ProgramState::<#name>::seed();
|
||||
let owner = ctor_accounts.program.key;
|
||||
let to = Pubkey::create_with_seed(&base, seed, owner).unwrap();
|
||||
// Add 8 for the account discriminator.
|
||||
let space = 8 + instance.try_to_vec().map_err(|_| ProgramError::Custom(1))?.len();
|
||||
let lamports = ctor_accounts.rent.minimum_balance(space);
|
||||
let seeds = &[&[nonce][..]];
|
||||
let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed(
|
||||
from,
|
||||
&to,
|
||||
&base,
|
||||
seed,
|
||||
lamports,
|
||||
space as u64,
|
||||
owner,
|
||||
);
|
||||
anchor_lang::solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&[
|
||||
ctor_accounts.from.clone(),
|
||||
ctor_accounts.to.clone(),
|
||||
ctor_accounts.base.clone(),
|
||||
ctor_accounts.system_program.clone(),
|
||||
],
|
||||
&[seeds],
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
accounts.exit(program_id)?;
|
||||
let mut data = state_account.try_borrow_mut_data()?;
|
||||
ctor_user_def_accounts.exit(program_id)?;
|
||||
let mut data = ctor_accounts.to.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
state.try_serialize(&mut cursor)?;
|
||||
instance.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
},
|
||||
};
|
||||
let non_inlined_state_trait_handlers: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => Vec::new(),
|
||||
let non_inlined_state_handlers: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(state) => state
|
||||
.interfaces
|
||||
.iter()
|
||||
.flat_map(|iface: &crate::StateInterface| {
|
||||
iface
|
||||
.methods
|
||||
.impl_block_and_methods
|
||||
.as_ref()
|
||||
.map(|(_impl_block, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|rpc| {
|
||||
let rpc_params: Vec<_> = rpc.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let private_rpc_name: proc_macro2::TokenStream = {
|
||||
let n = format!("__{}_{}", iface.trait_name, &rpc.raw_method.sig.ident.to_string());
|
||||
let n = format!("__{}", &rpc.raw_method.sig.ident.to_string());
|
||||
n.parse().unwrap()
|
||||
};
|
||||
let rpc_name = &rpc.raw_method.sig.ident;
|
||||
let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
|
||||
if rpc.has_receiver {
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.len() == 0 {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
let state_account = &remaining_accounts[0];
|
||||
let mut state: #state_ty = {
|
||||
let data = state_account.try_borrow_data()?;
|
||||
let mut sliced: &[u8] = &data;
|
||||
anchor_lang::AccountDeserialize::try_deserialize(&mut sliced)?
|
||||
};
|
||||
|
||||
remaining_accounts = &remaining_accounts[1..];
|
||||
|
||||
// Deserialize the program's execution context.
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
let ctx = Context::new(program_id, &mut accounts, remaining_accounts);
|
||||
|
||||
// Execute user defined function.
|
||||
state.#rpc_name(
|
||||
ctx,
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
accounts.exit(program_id)?;
|
||||
let mut data = state_account.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
state.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let state_name: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
#state_name::#rpc_name(
|
||||
Context::new(program_id, &mut accounts, remaining_accounts),
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
accounts.exit(program_id)
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.len() == 0 {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
let state_account = &remaining_accounts[0];
|
||||
let mut state: #state_ty = {
|
||||
let data = state_account.try_borrow_data()?;
|
||||
let mut sliced: &[u8] = &data;
|
||||
anchor_lang::AccountDeserialize::try_deserialize(&mut sliced)?
|
||||
};
|
||||
|
||||
remaining_accounts = &remaining_accounts[1..];
|
||||
|
||||
// Deserialize the program's execution context.
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
let ctx = Context::new(program_id, &mut accounts, remaining_accounts);
|
||||
|
||||
// Execute user defined function.
|
||||
state.#rpc_name(
|
||||
ctx,
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
accounts.exit(program_id)?;
|
||||
let mut data = state_account.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
state.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<proc_macro2::TokenStream>>()
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
.unwrap_or(vec![]),
|
||||
};
|
||||
let non_inlined_state_trait_handlers: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => Vec::new(),
|
||||
Some(state) => state
|
||||
.interfaces
|
||||
.as_ref()
|
||||
.map(|interfaces| {
|
||||
interfaces
|
||||
.iter()
|
||||
.flat_map(|iface: &crate::StateInterface| {
|
||||
iface
|
||||
.methods
|
||||
.iter()
|
||||
.map(|rpc| {
|
||||
let rpc_params: Vec<_> = rpc.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let private_rpc_name: proc_macro2::TokenStream = {
|
||||
let n = format!("__{}_{}", iface.trait_name, &rpc.raw_method.sig.ident.to_string());
|
||||
n.parse().unwrap()
|
||||
};
|
||||
let rpc_name = &rpc.raw_method.sig.ident;
|
||||
let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
|
||||
if rpc.has_receiver {
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.len() == 0 {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
let state_account = &remaining_accounts[0];
|
||||
let mut state: #state_ty = {
|
||||
let data = state_account.try_borrow_data()?;
|
||||
let mut sliced: &[u8] = &data;
|
||||
anchor_lang::AccountDeserialize::try_deserialize(&mut sliced)?
|
||||
};
|
||||
|
||||
remaining_accounts = &remaining_accounts[1..];
|
||||
|
||||
// Deserialize the program's execution context.
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
let ctx = Context::new(program_id, &mut accounts, remaining_accounts);
|
||||
|
||||
// Execute user defined function.
|
||||
state.#rpc_name(
|
||||
ctx,
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
accounts.exit(program_id)?;
|
||||
let mut data = state_account.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
state.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let state_name: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
#state_name::#rpc_name(
|
||||
Context::new(program_id, &mut accounts, remaining_accounts),
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
accounts.exit(program_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<proc_macro2::TokenStream>>()
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or(Vec::new()),
|
||||
};
|
||||
let non_inlined_handlers: Vec<proc_macro2::TokenStream> = program
|
||||
.rpcs
|
||||
|
@ -663,42 +700,50 @@ pub fn generate_ctor_typed_variant_with_semi(program: &Program) -> proc_macro2::
|
|||
|
||||
fn generate_ctor_typed_args(state: &State) -> Vec<syn::PatType> {
|
||||
state
|
||||
.ctor
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.clone())
|
||||
}
|
||||
_ => panic!("Invalid syntaxe,"),
|
||||
.ctor_and_anchor
|
||||
.as_ref()
|
||||
.map(|(ctor, _anchor_ident)| {
|
||||
ctor.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.clone())
|
||||
}
|
||||
_ => panic!("Invalid syntaxe,"),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
.unwrap_or(Vec::new())
|
||||
}
|
||||
|
||||
fn generate_ctor_args(state: &State) -> Vec<Box<syn::Pat>> {
|
||||
state
|
||||
.ctor
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.pat.clone())
|
||||
}
|
||||
_ => panic!(""),
|
||||
.ctor_and_anchor
|
||||
.as_ref()
|
||||
.map(|(ctor, _anchor_ident)| {
|
||||
ctor.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.pat.clone())
|
||||
}
|
||||
_ => panic!(""),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
.unwrap_or(Vec::new())
|
||||
}
|
||||
|
||||
pub fn generate_ix_variant(
|
||||
|
@ -750,62 +795,67 @@ pub fn generate_instructions(program: &Program) -> proc_macro2::TokenStream {
|
|||
let state_method_variants: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(state) => state
|
||||
.methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
let rpc_name_camel: proc_macro2::TokenStream = {
|
||||
let name = format!(
|
||||
"__{}",
|
||||
&method.raw_method.sig.ident.to_string().to_camel_case(),
|
||||
);
|
||||
name.parse().unwrap()
|
||||
};
|
||||
let raw_args: Vec<proc_macro2::TokenStream> = method
|
||||
.args
|
||||
.impl_block_and_methods
|
||||
.as_ref()
|
||||
.map(|(_impl_block, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
format!("pub {}", parser::tts_to_string(&arg.raw_arg))
|
||||
.parse()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
.map(|method| {
|
||||
let rpc_name_camel: proc_macro2::TokenStream = {
|
||||
let name = format!(
|
||||
"__{}",
|
||||
&method.raw_method.sig.ident.to_string().to_camel_case(),
|
||||
);
|
||||
name.parse().unwrap()
|
||||
};
|
||||
let raw_args: Vec<proc_macro2::TokenStream> = method
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
format!("pub {}", parser::tts_to_string(&arg.raw_arg))
|
||||
.parse()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ix_data_trait = {
|
||||
let name = method.raw_method.sig.ident.to_string();
|
||||
let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, &name);
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
impl anchor_lang::InstructionData for #rpc_name_camel {
|
||||
fn data(&self) -> Vec<u8> {
|
||||
let mut d = #sighash_tts.to_vec();
|
||||
d.append(&mut self.try_to_vec().expect("Should always serialize"));
|
||||
d
|
||||
let ix_data_trait = {
|
||||
let name = method.raw_method.sig.ident.to_string();
|
||||
let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, &name);
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
impl anchor_lang::InstructionData for #rpc_name_camel {
|
||||
fn data(&self) -> Vec<u8> {
|
||||
let mut d = #sighash_tts.to_vec();
|
||||
d.append(&mut self.try_to_vec().expect("Should always serialize"));
|
||||
d
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If no args, output a "unit" variant instead of a struct variant.
|
||||
if method.args.len() == 0 {
|
||||
quote! {
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #rpc_name_camel;
|
||||
|
||||
#ix_data_trait
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #rpc_name_camel {
|
||||
#(#raw_args),*
|
||||
}
|
||||
|
||||
#ix_data_trait
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If no args, output a "unit" variant instead of a struct variant.
|
||||
if method.args.len() == 0 {
|
||||
quote! {
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #rpc_name_camel;
|
||||
|
||||
#ix_data_trait
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #rpc_name_camel {
|
||||
#(#raw_args),*
|
||||
}
|
||||
|
||||
#ix_data_trait
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
.unwrap_or(Vec::new()),
|
||||
};
|
||||
let variants: Vec<proc_macro2::TokenStream> = program
|
||||
.rpcs
|
||||
|
@ -876,16 +926,18 @@ pub fn generate_instructions(program: &Program) -> proc_macro2::TokenStream {
|
|||
fn generate_accounts(program: &Program) -> proc_macro2::TokenStream {
|
||||
let mut accounts = std::collections::HashSet::new();
|
||||
|
||||
// Got through state accounts.
|
||||
// Go through state accounts.
|
||||
if let Some(state) = &program.state {
|
||||
for rpc in &state.methods {
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
// TODO: move to fn and share with accounts.rs.
|
||||
let macro_name = format!(
|
||||
"__client_accounts_{}",
|
||||
anchor_ident.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
if let Some((_impl_block, methods)) = &state.impl_block_and_methods {
|
||||
for rpc in methods {
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
// TODO: move to fn and share with accounts.rs.
|
||||
let macro_name = format!(
|
||||
"__client_accounts_{}",
|
||||
anchor_ident.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,11 +30,9 @@ pub struct Program {
|
|||
pub struct State {
|
||||
pub name: String,
|
||||
pub strct: syn::ItemStruct,
|
||||
pub impl_block: syn::ItemImpl,
|
||||
pub methods: Vec<StateRpc>,
|
||||
pub interfaces: Vec<StateInterface>,
|
||||
pub ctor: syn::ImplItemMethod,
|
||||
pub ctor_anchor: syn::Ident, // TODO: consolidate this with ctor above.
|
||||
pub ctor_and_anchor: Option<(syn::ImplItemMethod, syn::Ident)>,
|
||||
pub impl_block_and_methods: Option<(syn::ItemImpl, Vec<StateRpc>)>,
|
||||
pub interfaces: Option<Vec<StateInterface>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -33,102 +33,112 @@ pub fn parse(filename: impl AsRef<Path>) -> Result<Idl> {
|
|||
acc_names
|
||||
};
|
||||
|
||||
let state = p.state.map(|state| {
|
||||
let mut methods = state
|
||||
.methods
|
||||
.iter()
|
||||
.map(|method: &StateRpc| {
|
||||
let name = method.ident.to_string().to_mixed_case();
|
||||
let args = method
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
arg.raw_arg.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: arg.name.to_string().to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
let state = match p.state {
|
||||
None => None,
|
||||
Some(state) => match state.ctor_and_anchor {
|
||||
None => None, // State struct defined but no implementation
|
||||
Some((ctor, anchor_ident)) => {
|
||||
let mut methods = state
|
||||
.impl_block_and_methods
|
||||
.map(|(_impl_block, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|method: &StateRpc| {
|
||||
let name = method.ident.to_string().to_mixed_case();
|
||||
let args = method
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
arg.raw_arg.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: arg.name.to_string().to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let accounts_strct =
|
||||
accs.get(&method.anchor_ident.to_string()).unwrap();
|
||||
let accounts = accounts_strct.idl_accounts(&accs);
|
||||
IdlStateMethod {
|
||||
name,
|
||||
args,
|
||||
accounts,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let accounts_strct = accs.get(&method.anchor_ident.to_string()).unwrap();
|
||||
let accounts = accounts_strct.idl_accounts(&accs);
|
||||
IdlStateMethod {
|
||||
name,
|
||||
args,
|
||||
accounts,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let ctor = {
|
||||
let name = "new".to_string();
|
||||
let args = state
|
||||
.ctor
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
// TODO: this filtering should be donein the parser.
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(arg)
|
||||
.unwrap_or(Vec::new());
|
||||
let ctor = {
|
||||
let name = "new".to_string();
|
||||
let args = ctor
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
// TODO: this filtering should be donein the parser.
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(arg)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(arg_typed) => {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
arg_typed.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: parser::tts_to_string(&arg_typed.pat).to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
}
|
||||
_ => panic!("Invalid syntax"),
|
||||
})
|
||||
.collect();
|
||||
let accounts_strct = accs.get(&anchor_ident.to_string()).unwrap();
|
||||
let accounts = accounts_strct.idl_accounts(&accs);
|
||||
IdlStateMethod {
|
||||
name,
|
||||
args,
|
||||
accounts,
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(arg_typed) => {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
arg_typed.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: parser::tts_to_string(&arg_typed.pat).to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
};
|
||||
|
||||
methods.insert(0, ctor);
|
||||
|
||||
let strct = {
|
||||
let fields = match state.strct.fields {
|
||||
syn::Fields::Named(f_named) => f_named
|
||||
.named
|
||||
.iter()
|
||||
.map(|f: &syn::Field| {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
f.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<IdlField>>(),
|
||||
_ => panic!("State must be a struct"),
|
||||
};
|
||||
IdlTypeDef {
|
||||
name: state.name,
|
||||
ty: IdlTypeDefTy::Struct { fields },
|
||||
}
|
||||
_ => panic!("Invalid syntax"),
|
||||
})
|
||||
.collect();
|
||||
let accounts_strct = accs.get(&state.ctor_anchor.to_string()).unwrap();
|
||||
let accounts = accounts_strct.idl_accounts(&accs);
|
||||
IdlStateMethod {
|
||||
name,
|
||||
args,
|
||||
accounts,
|
||||
};
|
||||
|
||||
Some(IdlState { strct, methods })
|
||||
}
|
||||
};
|
||||
|
||||
methods.insert(0, ctor);
|
||||
|
||||
let strct = {
|
||||
let fields = match state.strct.fields {
|
||||
syn::Fields::Named(f_named) => f_named
|
||||
.named
|
||||
.iter()
|
||||
.map(|f: &syn::Field| {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
f.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<IdlField>>(),
|
||||
_ => panic!("State must be a struct"),
|
||||
};
|
||||
IdlTypeDef {
|
||||
name: state.name,
|
||||
ty: IdlTypeDefTy::Struct { fields },
|
||||
}
|
||||
};
|
||||
|
||||
IdlState { strct, methods }
|
||||
});
|
||||
},
|
||||
};
|
||||
let error = parse_error_enum(&f).map(|mut e| error::parse(&mut e));
|
||||
let error_codes = error.as_ref().map(|e| {
|
||||
e.codes
|
||||
|
|
|
@ -27,8 +27,9 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
})
|
||||
.next();
|
||||
|
||||
let impl_block: Option<&syn::ItemImpl> = strct.map(|strct| {
|
||||
let item_impls = mod_content
|
||||
let impl_block: Option<syn::ItemImpl> = match strct {
|
||||
None => None,
|
||||
Some(strct) => mod_content
|
||||
.iter()
|
||||
.filter_map(|item| match item {
|
||||
syn::Item::Impl(item_impl) => {
|
||||
|
@ -40,13 +41,12 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
if strct_name != impl_ty_str {
|
||||
return None;
|
||||
}
|
||||
Some(item_impl)
|
||||
Some(item_impl.clone())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<&syn::ItemImpl>>();
|
||||
item_impls[0]
|
||||
});
|
||||
.next(),
|
||||
};
|
||||
|
||||
// All program interface implementations.
|
||||
let trait_impls: Option<Vec<StateInterface>> = strct.map(|_strct| {
|
||||
|
@ -84,83 +84,87 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
let mut strct = strct.clone();
|
||||
strct.attrs = vec![];
|
||||
|
||||
let impl_block = impl_block.expect("Must exist if struct exists").clone();
|
||||
let (ctor, ctor_anchor) = impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => {
|
||||
if m.sig.ident.to_string() == "new" {
|
||||
let ctx_arg = m.sig.inputs.first().unwrap(); // todo: unwrap.
|
||||
match ctx_arg {
|
||||
syn::FnArg::Receiver(_) => panic!("invalid syntax"),
|
||||
syn::FnArg::Typed(arg) => {
|
||||
Some((m.clone(), extract_ident(&arg).clone()))
|
||||
let ctor_and_anchor = match &impl_block {
|
||||
None => None,
|
||||
Some(impl_block) => {
|
||||
impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => {
|
||||
if m.sig.ident.to_string() == "new" {
|
||||
let ctx_arg = m.sig.inputs.first().unwrap(); // todo: unwrap.
|
||||
match ctx_arg {
|
||||
syn::FnArg::Receiver(_) => panic!("invalid syntax"),
|
||||
syn::FnArg::Typed(arg) => {
|
||||
Some((m.clone(), extract_ident(&arg).clone()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.expect("Must exist if struct exists")
|
||||
.clone();
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.clone()
|
||||
}
|
||||
};
|
||||
|
||||
let methods: Vec<StateRpc> = impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => match m.sig.inputs.first() {
|
||||
None => None,
|
||||
Some(arg) => match arg {
|
||||
syn::FnArg::Typed(_) => None,
|
||||
syn::FnArg::Receiver(_) => {
|
||||
let mut args = m
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
syn::FnArg::Receiver(_) => None,
|
||||
syn::FnArg::Typed(arg) => Some(arg),
|
||||
})
|
||||
.map(|raw_arg| {
|
||||
let ident = match &*raw_arg.pat {
|
||||
syn::Pat::Ident(ident) => &ident.ident,
|
||||
_ => panic!("invalid syntax"),
|
||||
};
|
||||
RpcArg {
|
||||
name: ident.clone(),
|
||||
raw_arg: raw_arg.clone(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<RpcArg>>();
|
||||
// Remove the Anchor accounts argument
|
||||
let anchor = args.remove(0);
|
||||
let anchor_ident = extract_ident(&anchor.raw_arg).clone();
|
||||
let impl_block_and_methods = impl_block.map(|impl_block| {
|
||||
let methods: Vec<StateRpc> = impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => match m.sig.inputs.first() {
|
||||
None => None,
|
||||
Some(arg) => match arg {
|
||||
syn::FnArg::Typed(_) => None,
|
||||
syn::FnArg::Receiver(_) => {
|
||||
let mut args = m
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
syn::FnArg::Receiver(_) => None,
|
||||
syn::FnArg::Typed(arg) => Some(arg),
|
||||
})
|
||||
.map(|raw_arg| {
|
||||
let ident = match &*raw_arg.pat {
|
||||
syn::Pat::Ident(ident) => &ident.ident,
|
||||
_ => panic!("invalid syntax"),
|
||||
};
|
||||
RpcArg {
|
||||
name: ident.clone(),
|
||||
raw_arg: raw_arg.clone(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<RpcArg>>();
|
||||
// Remove the Anchor accounts argument
|
||||
let anchor = args.remove(0);
|
||||
let anchor_ident = extract_ident(&anchor.raw_arg).clone();
|
||||
|
||||
Some(StateRpc {
|
||||
raw_method: m.clone(),
|
||||
ident: m.sig.ident.clone(),
|
||||
args,
|
||||
anchor_ident,
|
||||
has_receiver: true,
|
||||
})
|
||||
}
|
||||
Some(StateRpc {
|
||||
raw_method: m.clone(),
|
||||
ident: m.sig.ident.clone(),
|
||||
args,
|
||||
anchor_ident,
|
||||
has_receiver: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
(impl_block.clone(), methods)
|
||||
});
|
||||
State {
|
||||
name: strct.ident.to_string(),
|
||||
strct: strct.clone(),
|
||||
interfaces: trait_impls.expect("Some if state exists"),
|
||||
impl_block,
|
||||
ctor,
|
||||
ctor_anchor,
|
||||
methods,
|
||||
interfaces: trait_impls,
|
||||
impl_block_and_methods,
|
||||
ctor_and_anchor,
|
||||
}
|
||||
})
|
||||
};
|
||||
|
|
|
@ -9,3 +9,4 @@ description = "CPI clients for SPL programs"
|
|||
[dependencies]
|
||||
anchor-lang = { path = "../lang", version = "0.2.0", features = ["derive"] }
|
||||
spl-token = { version = "3.0.1", features = ["no-entrypoint"] }
|
||||
solana-program = "=1.5.0"
|
|
@ -1 +1,2 @@
|
|||
pub mod shmem;
|
||||
pub mod token;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
//! CPI API for interacting with the SPL shared memory
|
||||
//! [program](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory).
|
||||
|
||||
use anchor_lang::{Accounts, CpiContext};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::declare_id;
|
||||
use solana_program::entrypoint::ProgramResult;
|
||||
use solana_program::instruction::{AccountMeta, Instruction};
|
||||
use solana_program::program;
|
||||
|
||||
// TODO: update this once the final shared memory program gets released.
|
||||
// shmem4EWT2sPdVGvTZCzXXRAURL9G5vpPxNwSeKhHUL.
|
||||
declare_id!("DynWy94wrWp5RimU49creYMQ5py3Up8BBNS4VA73VCpi");
|
||||
|
||||
/// `ret` writes the given `data` field to the shared memory account
|
||||
/// acting as a return value that can be used across CPI.
|
||||
/// The caleee should use this to write data into the shared memory account.
|
||||
/// The caler should use the account directly to pull out and interpret the
|
||||
/// bytes. Shared memory serialization is not specified and is up to the
|
||||
/// caller and callee.
|
||||
pub fn ret<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, Ret<'info>>,
|
||||
data: Vec<u8>,
|
||||
) -> ProgramResult {
|
||||
let instruction = Instruction {
|
||||
program_id: *ctx.program.key,
|
||||
accounts: vec![AccountMeta::new(*ctx.accounts.buffer.key, false)],
|
||||
data,
|
||||
};
|
||||
let mut accounts = vec![ctx.accounts.buffer];
|
||||
accounts.push(ctx.program.clone());
|
||||
program::invoke(&instruction, &accounts)
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Ret<'info> {
|
||||
#[account(mut)]
|
||||
pub buffer: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
// A set of accounts that can be used with shared memory.
|
||||
#[derive(Accounts)]
|
||||
pub struct Shmem<'info> {
|
||||
// Shared memory account to write the return value into.
|
||||
#[account(mut, "shmem.owner == shmem_program.key")]
|
||||
pub shmem: AccountInfo<'info>,
|
||||
#[account("shmem_program.key == &ID")]
|
||||
pub shmem_program: AccountInfo<'info>,
|
||||
}
|
Loading…
Reference in New Issue