coral-xyz / anchor Goto Github PK
View Code? Open in Web Editor NEW⚓ Solana Sealevel Framework
Home Page: https://anchor-lang.com
License: Apache License 2.0
⚓ Solana Sealevel Framework
Home Page: https://anchor-lang.com
License: Apache License 2.0
Constructing transactions is a bit bulky right now due to having to specify multiple accounts and instructions all in one function call. We should consider offering a builder api, similar to what the rust client does.
Currently the deploy happens once for the entire test suite.
As discussed with @sconybeare, we should explore adding an account deserialization trait that looks something like
impl TRAIT for STRUCT {
fn deserialize(deserializer: D, accounts: &mut &[AccountInfo]) -> Self {
...
}
}
Requires the solana bpf sdk to be on Rust 1.45 or above, so that we can remove #![feature(proc_macro_hygiene)]
everywhere.
Until we do this, CI will sometimes fail, because rustfmt
is not always available on the latest nightly, so travis should also probably pin the nightly version.
Currently, a CPI interface is implemented manually. See the "opaque relay" here https://github.com/project-serum/anchor/blob/master/examples/lockup/programs/lockup/src/lib.rs#L232.
Instead, we should be able to define a trait for CPI and allow any program to implement it, so that we can have cleaner, typed APIs instead of using the raw solana_program::program::invoke
apis.
Consider implementing the Accounts
trait for Vec
, where all accounts in the &[AccountInfo]
slice are consumed and deserialized into the vec.
We should add an anchor account <program>.<account> <address>
command that allows one to fetch any account in the workspace via CLI (by reading all IDLs).
Currently, any implementation of a program #[interface]
trait can only be used via CPI. We should add these handlers to the IDL so that clients can call them directly, potentially. In the mean time, if one wants to invoke these methods from clients, they can should expose methods outside the trait, and have the trait invoke these methods.
Provide an EventEmitter
API to listen to any logs emitted via the solana msg!
macro.
Currently all the tests are run on every invocation of anchor test
.
We should automatically generate validation code for common types, e.g. all sysvars.
For example,
#[derive(Accounts)]
pub struct MyStruct {
pub clock: Clock,
pub rent: Rent,
}
Should work without specifying additional attributes.
Signing with a program is pretty confusing for people using solana for the first time. Not really specific to Anchor but a useful example nonetheless.
The DSL is currently very lazy with error codes. We should do this properly, generating an error for each constraint, adding those to the IDL, and having all clients report typed, informative errors, when the runtime responds with a particular error code.
There's also some boilierplate we should eliminate with a macro when creating custom errors for programs.
Provide the ability to generate a global state struct and methods on it. This is useful when you want Solana programs to behave as if they have state (even though they do not). Because account sizes are finite, this can't be the dominate programming pattern. But it can be complementary to what we have now (i.e. defining instructions and accounts separately).
#[program]
mod registry {
#[state]
pub struct Foo {
data: Pubkey,
}
impl Foo {
pub fn new(data: Pubkey) -> Result<Self, Error> {
Ok(Registry {
data,
})
}
pub fn some_instruction(&mut self, data: Pubkey) -> Result<(), Error> {
self.data = data;
Ok(())
}
}
}
Add an #[account(rent_exempt)]
attribute for checking if an account is rent exempt.
Similar to what the dex does with AccountFlags, we should transparently add a discriminator field, which uniquely identifies the account type, in the first 8 bytes of every program-owned account.
If the account discriminator doesn't match when the Accounts struct being deserialized, then the instruction should error out.
We achieve this with a new attribute #[account]
, which implements four traits:
Where the implementations of AccountSerialize
and AccountDeserialize
do the discriminator check on the first 8 bytes and Borsh ser/deser of the rest of the account data.
Concretely, this looks like.
#[account]
pub struct Bar {
...
}
When checking the account discriminator, the one exception would be if the account is marked with a newly proposed attribute, #[account(init)]
, in which case, the discriminator should be zero, because an account is being initialized for the first time (and the discriminator will be set on the instruction execution).
#[derive(Accounts)]
pub struct MyInstruction<'info> {
#[account(init)]
pub foo: ProgramAccount<'info, Bar>,
}
Using a 1-indexed discriminator means all accounts need to be defined inside the #[program]
decorated mod
, so that the macro knows what index to assign a given Account. Alternatively, instead of incrementing a 1-indexed discriminator, we could just hash (SHA256) the name of the account and take the first 8 bytes to create the discriminator, in which case, the Accounts can be defined outside (or inside) of the #[program]
mod
.
Both for aesthetic, flexibility, and immediate implementation reasons, I prefer hashing the account name. Note that we can define the hashed discriminator at compile time (in the macro), so there is no runtime cost vs a 1-indexed counter. Aesthetically, it means only instruction handlers need to be inside the #[program]
module. In terms of implementation, it allows all account related serialization to be defined completely within the derive
macro. Otherwise, the #[program]
macro needs to handle this logic.
(A third option would be to have the programmer manually specify the discriminator. The developer could do this by just manually implementing the AccountSerialize
and AccountDeserialize
traits instead of using the #[account]
macro.)
CpiAccount<'info, TokenAccount>
has one too many Account
s for me. Lets rename TokenAccount
-> Token
. I'm not a fan of calling it Account
as the spl program does, since it overloads the word, which is confusing--though Token
is potentially confusing as well (when coming from Ethereum's programming model).
Currently methods are dispatched based upon a generated rust enum
, where each variant corresponds to one of the instruction handlers. The problem with this is that a method in one program will have a different "discriminator" tag than the same method in another program if the order of the function defined in the file is different. This makes defining interfaces inconvenient. One solution to this is to switch to something like Ethereum's sighash instead of using an enum
.
Sometimes sysvars are irrelevant to the application logic. For example, when initializing a program owned account, a rent exemption check is done, requiring the Rent sysvar to be included into the accounts set as follows.
#[derive(Accounts)]
pub struct MyAccounts {
#[account(init)]
my_account: ProgramAccount<'info, Foo>,
rent: Sysvar<'info, Rent>,
}
It would be nice for the application to omit the sysvar here
#[derive(Accounts)]
pub struct MyAccounts {
#[account(init)]
my_account: ProgramAccount<'info, Foo>,,
}
And have it automatically added to another field of the program's Context
object, when needed. In doing this, we would be setting the convention to not have sysvars in the accounts struct, so we would have to have a separate way of explicitly adding them (again, to a separate field on the Context object) when needed. One solution, would be to add a new inert attribute to mark the sysvars needed. For example,
#[derive(Accounts)]
#[sysvar(clock, rent)]
pub struct MyAccounts {
#[account(init)]
my_account: ProgramAccount<'info, Foo>,,
}
We can do what we do for the program state struct, i.e., store the IDL JSON at a determistic account address as a function of nothing but the program_id
.
This will allow clients to easily interact with any program written in Anchor using nothing but the program_id
.
Tasks:
anchor.Program.at(<PROGRAM-ID>)
which generates a client from the program addressThe codegen uses these names before invoking the handler. Instead, we should add a __
prefix to all variables used by the codegen so that there's no collision.
Currently the generated clients are freewheeling javascript. It would be nice to emit an index.d.ts
file for apps to use.
Unlike the solana program deploy
command, the anchor deploy
command deploys to a new address everytime. To upgrade, we should implement an explicit anchor upgrade
command.
Would be nice to use the @project-serum/anchor package directly in the browser without a build step, i.e., via <script>
.
For SPL programs, namely the token program, we should create clients for CPI that can be created from the accounts array. I.e. a manual version of #7, since of course the SPL programs aren't written in this framework.
Currently the IDL is created by parsing the src/lib.rs
file. Instead, we should walk the entire crate to allow for larger programs to be more organized.
For example, it would be nice to only have nothing but the #[program]
mod in src/lib.rs
and all the separate handlers
be in separate files alongside the associated instruction's Accounts
struct. Similarly, an accounts
module.
Similar to the ts
typescript package, we should generate rust clients from IDL.
Generate type Result<T> = std::result::Result<T, Error>;
, since there's no other error types that should be used in programs.
Create a dex
module analogous to https://github.com/project-serum/anchor/blob/master/spl/src/token.rs in the anchor_spl
crate.
Currently we cut off the bottom of the file to emphasize the part of the docs that actually matters. However, this can be confusing when people copy and paste the code locally, since it won't compile.
We should provide a getProgramAccounts
method that filters on the account discriminator, which has the effect of getting all accounts of a given type associated with a program.
Generate CPI methods for the state struct instructions, similar to what we do for non-state-struct instructions.
Filing a tracking issue, though I don't love the idea at the moment and am inclined to throw this idea away.
An additional feature the #[account(init)]
attribute enables is the ability for clients to know when they should create accounts in the same transaction as executing a program instruction.
Currently, when executing this type of instruction, clients must manually add the account creation instruction as follows.
// The Account to create.
const myAccount = new anchor.web3.Account();
// Atomically create the new account and initialize it with the program.
await program.rpc.myInstruction(...args, {
accounts: {
myAccount: myAccount.publicKey,
},
signers: [myAccount],
instructions: [
await program.account.MyAccount.createInstruction(myAccount),
],
});
With the #[anchor(init)]
attribute, this example can be transformed into
await program.rpc.myInstruction(...args);
Since the client generator can read a new isInit
field in the IDL
// idl.json
{
...,
instructions: {
...,
{
"name:" "myInstruction",
"accounts": [...],
"isInit": true,
"isMut": true,
"isSigner": true,
...
}
}
We should generate clients for performing CPI. So that programs can import a crate, auto-deserialize a client from all the accounts, and make a high level calls to other programs without manually dealing with all the AccountInfo
structs and instruction serialization.
We should add an example with complexity analogous to the lockup
example, where we don't use borsh and manually serialize/deserialize everything by implementing the various Anchor
traits--and fix any issues that pop up in the process.
Currently one must use Vec
.
#[state]
structs are required to implement program #[interface]
traits. However, if one only wants to implement nothing but the trait, one still has to define a new
constructor. We should remove this requirement.
Had to switch to pascal case to support the upgradeable loader. https://github.com/project-serum/anchor/blob/master/cli/src/main.rs#L676
Currently we use ProgramAccount
to refer to accounts owned by the current program. This type boxes it's inner account by default (needed to sometimes get around BPF stack size limits). Instead, we should provide a non-boxed version with type AccountBox = Box<NonBoxedVersion>;
as the boxed variant.
Suppose I had a field foo
of type crate_a::MyAccountsA
that is embedded into crate_b::MyAccountsB
as follows.
// crate_a/src/lib.rs
#[derive(Accounts)]
pub struct MyAccountsA {
...
}
// crate_b/src/lib.rs
#[derive(Accounts)]
pub struct MyAccountsB {
foo: crate_a::MyAccountsA,
}
I would want the IDL emitted for crate_b
to have all the type i+ account nformation for foo
.
Currently this information is only available to crate_a
, and so crate_b
would either have to re-define crate_a::MyAccountsA
in its own src/lib.rs
or use all the accounts in flattened form, instead of composing crate_a::MyAccountsA
directly.
To achieve this, we can have the various macros generate trait implementations providing IDL/type information. Additionally, we probably need to add some type of build.rs
file that calls these generated traits to output the IDL, instead of just parsing the program's file directly, as we do now.
The build steps might be organized into
For example
#[derive(Accounts)]
pub struct MyStruct<'info> {
#[account(signer)]
pub authority: Vec<AccountInfo<'info>>,
}
Would check that all accounts in the vec are signers.
Currently the #[access_control]
attribute only supports a single function modifier, e.g.
#[access_control(my_modifier())]
We should support multiple modifiers
#[access_control(
my_modifier_1(),
my_modifier_2(),
)]
Provide an EventEmitter
API to subscribe to (deserialized) account changes.
For example, see here.
The CPI client should transparently use "retbuf" accounts to allow returning values from CPI.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.