Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support legacy IDL + Versioned Transactions #77

Merged
merged 10 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 2.8

- Added `sendVersionedTransaction()` to send a versioned transaction with lookup tables. Also adds priority fee support.
- Added `createLookupTable()` to easily create a lookup table and extend it with additional addresses
- Added `getIdlByProgramId()` to fetch an IDL from a program on-chain
- Added `getIdlByPath()` to parse an IDL from a local file path
- Added `getIdlParsedAccountData()` to parse account data using an IDL
- Added `parseAnchorTransactionEvents()` to parse anchor transaction events using an IDL
- Added `decodeAnchorTransaction()` to decode a transaction completely using an IDL
- Fixed account data parsing in `decodeAnchorTransaction()`

## 2.7

- Added `sendTransaction()` to send transactions with compute unit optimization and automatic retries.
Expand Down
140 changes: 115 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,33 @@ const units = await getSimulationComputeUnits(

You can then use `ComputeBudgetProgram.setComputeUnitLimit({ units })` as the first instruction in your transaction. See [How to Request Optimal Compute Budget](https://solana.com/developers/guides/advanced/how-to-request-optimal-compute) for more information on compute units.

### `addComputeInstructions`

Adds compute unit instructions for a transaction if they don't already exist:

```typescript
const updatedInstructions = await addComputeInstructions(
connection,
instructions,
lookupTables,
payer.publicKey,
10000, // priority fee default 10000 microLamports
{ multiplier: 1.1 }, // compute unit buffer default adds 10%
);

// Returns instructions array with:
// 1. setComputeUnitPrice instruction (if not present)
// 2. setComputeUnitLimit instruction based on simulation (if not present)
// The limit is calculated by simulating the transaction and adding the specified buffer
```

This function:

1. Adds priority fee instruction if not present
2. Simulates transaction to determine required compute units
3. Adds compute unit limit instruction with buffer
4. Returns the updated instructions array

## node.js specific helpers

### Get a keypair from a keypair file
Expand Down Expand Up @@ -541,38 +568,107 @@ For RPC providers that support priority fees:
- Triton: see their [priority fee API](https://docs.triton.one/chains/solana/improved-priority-fees-api)
- Quicknode: see their [priority fee estimation](https://www.quicknode.com/docs/solana/qn_estimatePriorityFees)

#### `prepareTransactionWithCompute`
#### `sendVersionedTransaction`

If you need more control, you can prepare compute units separately:
Sends a versioned transaction with compute unit optimization and automatic retries.

```typescript
await prepareTransactionWithCompute(
async function sendVersionedTransaction(
connection: Connection,
instructions: Array<TransactionInstruction>,
signers: Keypair[],
priorityFee: number = 10000,
lookupTables?: Array<AddressLookupTableAccount> | [],
options?: SendTransactionOptions & {
computeUnitBuffer?: ComputeUnitBuffer;
},
): Promise<string>;
```

Example:

```typescript
const signature = await sendVersionedTransaction(
connection,
transaction,
payer.publicKey,
10000, // priority fee
instructions,
[payer],
10000,
lookupTables,
{
multiplier: 1.1, // add 10% buffer
fixed: 100, // add fixed amount of CUs
computeUnitBuffer: { multiplier: 1.1 },
onStatusUpdate: (status) => console.log(status),
},
);
```

This will:
#### `createLookupTable`

1. Simulate the transaction to determine required compute units
2. Add compute budget instructions for both price and unit limit
3. Apply any specified compute unit buffers
Creates a new address lookup table and extends it with additional addresses.

```typescript
async function createLookupTable(
connection: Connection,
sender: Keypair,
additionalAddresses: PublicKey[],
priorityFee: number = 10000,
): Promise<[PublicKey, AddressLookupTableAccount]>;
```

Example:

```typescript
const [lookupTableAddress, lookupTableAccount] = await createLookupTable(
connection,
payer,
[account1.publicKey, account2.publicKey, account3.publicKey],
);
// Can either cache the lookup table address and lookup table account for reuse, or use them directly
const signature = await sendVersionedTransaction(
connection,
instructions,
[payer],
10000,
[lookupTableAccount],
{
onStatusUpdate: (status) => console.log(status),
},
);
```

These utilities help with:

- Creating and sending versioned transactions
- Managing compute units and priority fees
- Using address lookup tables to fit more accounts in a single transaction
- Automatic transaction retries and status updates

## Anchor IDL Utilities

### Parse Account Data with IDL
### Loading IDLs

Get an IDL from a local file:

```typescript
const idl = await getIdlByPath("./idl/program.json");
```

Or fetch it from the chain:

```typescript
const idl = await getIdlByProgramId(
new PublicKey("verifycLy8mB96wd9wqq3WDXQwM4oU6r42Th37Db9fC"),
connection,
);
```

### Parse Account Data

Usage:

```typescript
const accountData = await getIdlParsedAccountData(
"./idl/program.json",
const idl = await getIdlByProgramId(programId, connection);
const data = await getIdlParsedAccountData(
idl,
"counter",
accountAddress,
connection,
Expand All @@ -588,11 +684,8 @@ Fetches and parses an account's data using an Anchor IDL file. This is useful wh
Usage:

```typescript
const events = await parseAnchorTransactionEvents(
"./idl/program.json",
signature,
connection,
);
const idl = await getIdlByPath("./idl/program.json");
const events = await parseAnchorTransactionEvents(idl, signature, connection);

// Events will be an array of:
// {
Expand All @@ -608,11 +701,8 @@ Parses all Anchor events emitted in a transaction. This helps you track and veri
Usage:

```typescript
const decoded = await decodeAnchorTransaction(
"./idl/program.json",
signature,
connection,
);
const idl = await getIdlByProgramId(programId, connection);
const decoded = await decodeAnchorTransaction(idl, signature, connection);

// Print human-readable format
console.log(decoded.toString());
Expand Down
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,13 @@
},
"license": "MIT",
"dependencies": {
"@coral-xyz/anchor": "^0.30.1",
"@solana/spl-token": "^0.4.8",
"@solana/spl-token-metadata": "^0.1.4",
"@solana/web3.js": "^1.98.0",
"bn.js": "^5.2.1",
"bs58": "^6.0.0",
"dotenv": "^16.4.5",
"@coral-xyz/anchor": "^0.30.1"
"dotenv": "^16.4.5"
},
"devDependencies": {
"@types/bn.js": "^5.1.6",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export * from './lib/explorer';
export * from './lib/airdrop';
export * from './lib/token';
export * from './lib/logs';
export * from './lib/idl';
export * from './types';
Loading