kklas / anchor-client-gen Goto Github PK
View Code? Open in Web Editor NEWA tool for generating solana web3 clients from anchor IDLs.
License: MIT License
A tool for generating solana web3 clients from anchor IDLs.
License: MIT License
#[derive(Debug, TryFromPrimitive, PartialEq, Eq, Clone, Copy)]
#[repr(u16)]
pub enum GlobalConfigOption {
EmergencyMode = 0,
...
// 100
ScopeProgramId = 100,
ScopePriceId = 101,
}
Codegen:
export interface ScopePriceIdJSON {
kind: "ScopePriceId"
}
export class ScopePriceId {
static readonly discriminator = 17
static readonly kind = "ScopePriceId"
readonly discriminator = 17
readonly kind = "ScopePriceId"
}
This should be 101, not 17..
It would be great to have fetchMultiple
support to allow for efficient fetching of multiple accounts. This functionality is currently supported by the existing dynamic client library within anchor.
error An unexpected error occurred: "https://github.com/kklas/anchor-nightly/releases/download/anchor-cli-latest/anchor-cli-0.25.0-7eb8ca8.tgz: Request failed "404 Not Found"".
Hi @kklas. I'd love to chat. Please reach me at [email protected] ๐ .
Would be nice to have
Anchor allows accessing accounts not defined on the Accounts struct through ctx.remaining_accounts
. I currently don't see a way to pass these additional accounts in the generated client code cleanly.
Proposal:
Add optional arg to pass additional accounts
export function redeem(args: RedeemArgs, accounts: RedeemAccounts, additionalAccounts?: AccountMeta[]) {...}
Haven't tried it yet, but I believe there's a workaround where the returned TransactionInstruction can be mutated, but this is non-ideal
I noticed if I run this tool against an IDL with no "types" defined, the resultant /types/index.ts
file will empty, which causes import errors in the other files.
I was able to fix the errors by putting export default {};
in /types/index.ts
.
Return errors with more information from the client. It would be a good idea to have custom classes for each error types but we would have to have them in a separate lib (maybe anchor-client-gen-common
) in order to avoid code duplication when there multiple generated clients in the same codebase.
Originally posted by @kklas in #4 (comment)
Duplicate of #29
Hey @kklas , thank you for your help with the bytes type. I am currently working on a project, where I would like to use this for more data types, specifically:
Vec<u8>
[u8; 32]
I had a look at the code already and was able to get it to work on a local branch: https://github.com/mschneider/anchor-client-gen/tree/max/uint8array2 but curious to hear your thoughts as this somewhat breaks with the current abstraction of the library on a few levels.
My ideal outcome would be:
Let me know what you think about these changes.
Apologies if this is a dumb question, let's say I have a project with a few programs:
programs/main
programs/admin
programs/pools
where the accounts
, structs
, enums
, etc are defined in the main
program, and referenced as a dependency in the other two. Anchor creates three IDL's. Is it possible to generate a client (or three different clients?) from this structure? Right now I don't think it's possible to expose the structs cross-program.
I'd like to branch on variant kind like below, but kind is an instance variable, not static, so the following doesn't work
switch (myEnumValue.kind) {
case MyEnum.Swap.kind:
...
case MyEnum.LP.kind:
...
}
export class Swap {
static readonly discriminator = 0 // <--- make these 2 fields static
static readonly kind = "Swap"
readonly value: SwapValue
constructor(value: SwapFields) {
this.value = [new types.SwapInfo({ ...value[0] })]
}
toJSON(): SwapJSON {
return {
kind: "Swap",
value: [this.value[0].toJSON()],
}
}
toEncodable() {
return {
Swap: {
_0: types.SwapInfo.toEncodable(this.value[0]),
},
}
}
}
If there's another more idiomatic way of doing this please let me know instead
Event parsing in anchor-ts relies on IDL magic: https://github.com/project-serum/anchor/blob/master/ts/src/program/event.ts
So it would be nice if the generated client came with an event parser.
fn do_something(arg: Box<MyType>) -> Result<()> {
...
}
Currently the above fails with the following error:
generating programId.ts...
generating errors.ts...
generating instructions...
Defined type not found: {"defined":"Box<MyType>"}
Disregard the Box
and just look for MyType
Interested in getting thoughts on dropping the connection parameter, or making it optional. I'm not clear on the use case on being able to target multiple or different connections.
Anchor already provides a global straight-forward way to get the currently configure connection that could be used within the generated clients.
getProvider().connection
This would make the interface of e.g., fetch
, cleaner IMO, passing just the address of the account to fetch, and save client authors having to pass around the connection.
static async fetch(address: PublicKey): Promise<State | null> {
const c = getProvider().connection
const info = await c.getAccountInfo(address)
if (info === null) {
return null
}
if (!info.owner.equals(PROGRAM_ID)) {
throw new Error("account doesn't belong to this program")
}
return this.decode(info.data)
}
...
const res = await State.fetch(state.publicKey)
import { PublicKey, Connection } from "@solana/web3.js"
import BN from "bn.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import * as borsh from "@project-serum/borsh" // eslint-disable-line @typescript-eslint/no-unused-vars
import * as types from "../types" // eslint-disable-line @typescript-eslint/no-unused-vars
import { PROGRAM_ID } from "../programId"
export interface OrderbookInfoFields {
admin: PublicKey
length: number
applesMint: PublicKey
orangesMint: PublicKey
bump: number
closed: boolean
id: PublicKey
tradeLog: Array<types.TradeRecordFields>
}
export interface OrderbookInfoJSON {
admin: string
length: number
applesMint: string
orangesMint: string
bump: number
closed: boolean
id: string
tradeLog: Array<types.TradeRecordJSON>
}
export class OrderbookInfo {
readonly admin: PublicKey
readonly length: number
readonly applesMint: PublicKey
readonly orangesMint: PublicKey
readonly bump: number
readonly closed: boolean
readonly id: PublicKey
readonly tradeLog: Array<types.TradeRecord>
static readonly discriminator = Buffer.from([
126, 118, 193, 78, 125, 233, 132, 90,
])
static readonly layout = borsh.struct([
borsh.publicKey("admin"),
borsh.u32("length"),
borsh.publicKey("applesMint"),
borsh.publicKey("orangesMint"),
borsh.u8("bump"),
borsh.bool("closed"),
borsh.publicKey("id"),
borsh.vec(types.TradeRecord.layout(), "tradeLog"),
])
constructor(fields: OrderbookInfoFields) {
this.admin = fields.admin
this.length = fields.length
this.applesMint = fields.applesMint
this.orangesMint = fields.orangesMint
this.bump = fields.bump
this.closed = fields.closed
this.id = fields.id
this.tradeLog = fields.tradeLog.map(
(item) => new types.TradeRecord({ ...item })
)
}
static async fetch(
c: Connection,
address: PublicKey
): Promise<OrderbookInfo | null> {
const info = await c.getAccountInfo(address)
if (info === null) {
return null
}
if (!info.owner.equals(PROGRAM_ID)) {
throw new Error("account doesn't belong to this program")
}
return this.decode(info.data)
}
static async fetchMultiple(
c: Connection,
addresses: PublicKey[]
): Promise<Array<OrderbookInfo | null>> {
const infos = await c.getMultipleAccountsInfo(addresses)
return infos.map((info) => {
if (info === null) {
return null
}
if (!info.owner.equals(PROGRAM_ID)) {
throw new Error("account doesn't belong to this program")
}
return this.decode(info.data)
})
}
static decode(data: Buffer): OrderbookInfo {
if (!data.slice(0, 8).equals(OrderbookInfo.discriminator)) {
throw new Error("invalid account discriminator")
}
const dec = OrderbookInfo.layout.decode(data.slice(8))
return new OrderbookInfo({
admin: dec.admin,
length: dec.length,
applesMint: dec.applesMint,
orangesMint: dec.orangesMint,
bump: dec.bump,
closed: dec.closed,
id: dec.id,
tradeLog: dec.tradeLog.map((item) => types.TradeRecord.fromDecoded(item)),
})
}
toJSON(): OrderbookInfoJSON {
return {
admin: this.admin.toString(),
length: this.length,
applesMint: this.applesMint.toString(),
orangesMint: this.orangesMint.toString(),
bump: this.bump,
closed: this.closed,
id: this.id.toString(),
tradeLog: this.tradeLog.map((item) => item.toJSON()),
}
}
static fromJSON(obj: OrderbookInfoJSON): OrderbookInfo {
return new OrderbookInfo({
admin: new PublicKey(obj.admin),
length: obj.length,
applesMint: new PublicKey(obj.applesMint),
orangesMint: new PublicKey(obj.orangesMint),
bump: obj.bump,
closed: obj.closed,
id: new PublicKey(obj.id),
tradeLog: obj.tradeLog.map((item) => types.TradeRecord.fromJSON(item)),
})
}
}
Line 116 contains error (pictured)
Not sure if the "real" error is that dec
should be typed, or what.
some workflows require regenerating diff program ids depending on cluster etc, would be useful to not require it to be hardcoded in a file
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.