Skip to content

Commit

Permalink
Migrate codegen to @solana/web3.js:2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
elliotkennedy committed Dec 2, 2024
1 parent 8106a2a commit d81a2c4
Show file tree
Hide file tree
Showing 45 changed files with 2,048 additions and 1,185 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ node_modules/
**/*.rs.bk
.test-ledger
dist/
.idea/
58 changes: 46 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,38 +72,72 @@ The following packages are required for the generated client to work:
- `@solana/web3.js`
- `bn.js`
- `@coral-xyz/borsh`
- `buffer-layout`

Install them in your project with:

```sh
// npm
$ npm install @solana/web3.js bn.js @coral-xyz/borsh
$ npm install @solana/web3.js bn.js @coral-xyz/borsh buffer-layout

// yarn
$ yarn add @solana/web3.js bn.js @coral-xyz/borsh
$ yarn add @solana/web3.js bn.js @coral-xyz/borsh buffer-layout
```

For typescript, the `buffer-layout` types can be found in the `@coral-xyz/borsh` package.

`tsconfig.json`:

```json
{
"compilerOptions": {
"types": ["<existing types>", "../node_modules/@coral-xyz/anchor/types"]
}
}
```

### Instructions

```ts
import { someInstruction } from "./output/directory/instructions"

// call an instruction
const tx = new Transaction()
const fooAccount = new Keypar()
const fooAccount = generateKeyPairSigner()

// call an instruction
const ix = someInstruction({
fooParam: "...",
barParam: "...",
...
}, {
fooAccount: fooAccount.publicKey, // signer
barAccount: new PublicKey("..."),
fooAccount: fooAccount, // signer
barAccount: address("..."),
...
})
tx.add(ix)

sendAndConfirmTransaction(connection, tx, [payer, fooAccount])
const blockhash = await rpc
.getLatestBlockhash({ commitment: "finalized" })
.send()

const tx = await pipe(
createTransactionMessage({ version: 0 }),
(tx) => appendTransactionMessageInstruction(ix, tx),
(tx) => setTransactionMessageFeePayerSigner(payer, tx),
(tx) =>
setTransactionMessageLifetimeUsingBlockhash(
{
blockhash: blockhash.value.blockhash,
lastValidBlockHeight: blockhash.value.lastValidBlockHeight,
},
tx
),
(tx) => signTransactionMessageWithSigners(tx)
)

const sendAndConfirmFn = sendAndConfirmTransactionFactory({
rpc,
rpcSubscriptions,
})
await sendAndConfirmFn(tx)
```

### Accounts
Expand All @@ -112,9 +146,9 @@ sendAndConfirmTransaction(connection, tx, [payer, fooAccount])
import { FooAccount } from "./output/directory/accounts"

// fetch an account
const addr = new PublicKey("...")
const addr = address("...")

const acc = FooAccount.fetch(connection, addr)
const acc = FooAccount.fetch(rpc, addr)
if (acc === null) {
// the fetch method returns null when the account is uninitialized
console.log("account not found")
Expand Down Expand Up @@ -190,7 +224,7 @@ import { fromTxError } from "./output/directory/errors"
import { SomeCustomError } from "./output/directory/errors/custom"

try {
await sendAndConfirmTransaction(c, tx, [payer])
await sendAndConfirmFn(tx)
} catch (e) {
const parsed = fromTxError(e)
if (parsed !== null && parsed instanceof SomeCustomError) {
Expand Down
51 changes: 30 additions & 21 deletions examples/basic-2/generated-client/accounts/Counter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { PublicKey, Connection } from "@solana/web3.js"
import {
address,
Address,
fetchEncodedAccount,
fetchEncodedAccounts,
GetAccountInfoApi,
GetMultipleAccountsApi,
Rpc,
} from "@solana/web3.js"
import BN from "bn.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import * as borsh from "@coral-xyz/borsh" // eslint-disable-line @typescript-eslint/no-unused-vars
import { borshAddress } from "../utils" // eslint-disable-line @typescript-eslint/no-unused-vars
import { PROGRAM_ID } from "../programId"

export interface CounterFields {
authority: PublicKey
authority: Address
count: BN
}

Expand All @@ -14,15 +23,15 @@ export interface CounterJSON {
}

export class Counter {
readonly authority: PublicKey
readonly authority: Address
readonly count: BN

static readonly discriminator = Buffer.from([
255, 176, 4, 245, 188, 253, 124, 25,
])

static readonly layout = borsh.struct([
borsh.publicKey("authority"),
static readonly layout = borsh.struct<Counter>([
borshAddress("authority"),
borsh.u64("count"),
])

Expand All @@ -32,38 +41,38 @@ export class Counter {
}

static async fetch(
c: Connection,
address: PublicKey,
programId: PublicKey = PROGRAM_ID
rpc: Rpc<GetAccountInfoApi>,
address: Address,
programId: Address = PROGRAM_ID
): Promise<Counter | null> {
const info = await c.getAccountInfo(address)
const info = await fetchEncodedAccount(rpc, address)

if (info === null) {
if (!info.exists) {
return null
}
if (!info.owner.equals(programId)) {
if (info.programAddress !== programId) {
throw new Error("account doesn't belong to this program")
}

return this.decode(info.data)
return this.decode(Buffer.from(info.data))
}

static async fetchMultiple(
c: Connection,
addresses: PublicKey[],
programId: PublicKey = PROGRAM_ID
rpc: Rpc<GetMultipleAccountsApi>,
addresses: Address[],
programId: Address = PROGRAM_ID
): Promise<Array<Counter | null>> {
const infos = await c.getMultipleAccountsInfo(addresses)
const infos = await fetchEncodedAccounts(rpc, addresses)

return infos.map((info) => {
if (info === null) {
if (!info.exists) {
return null
}
if (!info.owner.equals(programId)) {
if (info.programAddress !== programId) {
throw new Error("account doesn't belong to this program")
}

return this.decode(info.data)
return this.decode(Buffer.from(info.data))
})
}

Expand All @@ -82,14 +91,14 @@ export class Counter {

toJSON(): CounterJSON {
return {
authority: this.authority.toString(),
authority: this.authority,
count: this.count.toString(),
}
}

static fromJSON(obj: CounterJSON): Counter {
return new Counter({
authority: new PublicKey(obj.authority),
authority: address(obj.authority),
count: new BN(obj.count),
})
}
Expand Down
18 changes: 12 additions & 6 deletions examples/basic-2/generated-client/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PublicKey } from "@solana/web3.js"
import { Address } from "@solana/web3.js"
import { PROGRAM_ID } from "../programId"
import * as anchor from "./anchor"

Expand All @@ -20,19 +20,25 @@ const errorRe = /Program (\w+) failed: custom program error: (\w+)/

export function fromTxError(
err: unknown,
programId: PublicKey = PROGRAM_ID
programId: Address = PROGRAM_ID
): anchor.AnchorError | null {
if (
typeof err !== "object" ||
err === null ||
!hasOwnProperty(err, "logs") ||
!Array.isArray(err.logs)
!hasOwnProperty(err, "context")
) {
return null
}

const context = err.context as { code?: number; logs?: string[] }
if (hasOwnProperty(context, "code") && context.code) {
return fromCode(context.code, context.logs)
}
if (!hasOwnProperty(context, "logs") || !context.logs) {
return null
}
let firstMatch: RegExpExecArray | null = null
for (const logLine of err.logs) {
for (const logLine of context.logs) {
firstMatch = errorRe.exec(logLine)
if (firstMatch !== null) {
break
Expand All @@ -55,5 +61,5 @@ export function fromTxError(
return null
}

return fromCode(errorCode, err.logs)
return fromCode(errorCode, context.logs)
}
31 changes: 19 additions & 12 deletions examples/basic-2/generated-client/instructions/create.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import { TransactionInstruction, PublicKey, AccountMeta } from "@solana/web3.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import {
Address,
IAccountMeta,
IAccountSignerMeta,
IInstruction,
TransactionSigner,
} from "@solana/web3.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import BN from "bn.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import * as borsh from "@coral-xyz/borsh" // eslint-disable-line @typescript-eslint/no-unused-vars
import { borshAddress } from "../utils" // eslint-disable-line @typescript-eslint/no-unused-vars
import { PROGRAM_ID } from "../programId"

export interface CreateArgs {
authority: PublicKey
authority: Address
}

export interface CreateAccounts {
counter: PublicKey
user: PublicKey
systemProgram: PublicKey
counter: TransactionSigner
user: TransactionSigner
systemProgram: Address
}

export const layout = borsh.struct([borsh.publicKey("authority")])
export const layout = borsh.struct([borshAddress("authority")])

export function create(
args: CreateArgs,
accounts: CreateAccounts,
programId: PublicKey = PROGRAM_ID
programAddress: Address = PROGRAM_ID
) {
const keys: Array<AccountMeta> = [
{ pubkey: accounts.counter, isSigner: true, isWritable: true },
{ pubkey: accounts.user, isSigner: true, isWritable: true },
{ pubkey: accounts.systemProgram, isSigner: false, isWritable: false },
const keys: Array<IAccountMeta | IAccountSignerMeta> = [
{ address: accounts.counter.address, role: 3, signer: accounts.counter },
{ address: accounts.user.address, role: 3, signer: accounts.user },
{ address: accounts.systemProgram, role: 0 },
]
const identifier = Buffer.from([24, 30, 200, 40, 5, 28, 7, 119])
const buffer = Buffer.alloc(1000)
Expand All @@ -34,6 +41,6 @@ export function create(
buffer
)
const data = Buffer.concat([identifier, buffer]).slice(0, 8 + len)
const ix = new TransactionInstruction({ keys, programId, data })
const ix: IInstruction = { accounts: keys, programAddress, data }
return ix
}
27 changes: 19 additions & 8 deletions examples/basic-2/generated-client/instructions/increment.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
import { TransactionInstruction, PublicKey, AccountMeta } from "@solana/web3.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import {
Address,
IAccountMeta,
IAccountSignerMeta,
IInstruction,
TransactionSigner,
} from "@solana/web3.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import BN from "bn.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import * as borsh from "@coral-xyz/borsh" // eslint-disable-line @typescript-eslint/no-unused-vars
import { borshAddress } from "../utils" // eslint-disable-line @typescript-eslint/no-unused-vars
import { PROGRAM_ID } from "../programId"

export interface IncrementAccounts {
counter: PublicKey
authority: PublicKey
counter: Address
authority: TransactionSigner
}

export function increment(
accounts: IncrementAccounts,
programId: PublicKey = PROGRAM_ID
programAddress: Address = PROGRAM_ID
) {
const keys: Array<AccountMeta> = [
{ pubkey: accounts.counter, isSigner: false, isWritable: true },
{ pubkey: accounts.authority, isSigner: true, isWritable: false },
const keys: Array<IAccountMeta | IAccountSignerMeta> = [
{ address: accounts.counter, role: 1 },
{
address: accounts.authority.address,
role: 2,
signer: accounts.authority,
},
]
const identifier = Buffer.from([11, 18, 104, 9, 104, 174, 59, 33])
const data = identifier
const ix = new TransactionInstruction({ keys, programId, data })
const ix: IInstruction = { accounts: keys, programAddress, data }
return ix
}
6 changes: 3 additions & 3 deletions examples/basic-2/generated-client/programId.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { PublicKey } from "@solana/web3.js"
import { address, Address } from "@solana/web3.js"

// Program ID passed with the cli --program-id flag when running the code generator. Do not edit, it will get overwritten.
export const PROGRAM_ID_CLI = new PublicKey(
export const PROGRAM_ID_CLI = address(
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
)

// This constant will not get overwritten on subsequent code generations and it's safe to modify it's value.
export const PROGRAM_ID: PublicKey = PROGRAM_ID_CLI
export const PROGRAM_ID: Address = PROGRAM_ID_CLI
Loading

0 comments on commit d81a2c4

Please sign in to comment.