Skip to content

Commit

Permalink
Add web3jsV2 examples (#610)
Browse files Browse the repository at this point in the history
* Add web3jsV2 examples

* Code review

* Update add-priority-fees.md

* Fix formatting

* Update add-priority-fees.md

---------

Co-authored-by: nickfrosty <[email protected]>
  • Loading branch information
Woody4618 and nickfrosty authored Nov 21, 2024
1 parent 5d4d64c commit 0823b6a
Show file tree
Hide file tree
Showing 5 changed files with 612 additions and 4 deletions.
87 changes: 87 additions & 0 deletions content/cookbook/development/load-keypair-from-file.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
title: Load a local json file keypair
sidebarSortOrder: 6
description: "Learn how to load a keypair from file."
---

When running your local project you probably want to use a file json keypair.
This can be very useful for all the cookbook examples as well. You can grind
yourself a keypair using `solana-keygen grind --starts-with a23:1` and then load
and use this one for your projects using the `loadKeypairFromFile` function.

```typescript filename="load-keypair-from-file.ts"
import {
airdropFactory,
createKeyPairFromBytes,
createSolanaRpc,
createSolanaRpcSubscriptions,
devnet,
generateKeyPair,
getAddressFromPublicKey,
lamports,
} from "@solana/web3.js";
import fs from "fs";
import path from "path";
import os from "os";

// The new library takes a brand-new approach to Solana key pairs and addresses,
// which will feel quite different from the classes PublicKey and Keypair from version 1.x.
// All key operations now use the native Ed25519 implementation in JavaScript’s
// Web Crypto API.
async function createKeypair() {
const newKeypair: CryptoKeyPair = await generateKeyPair();
const publicAddress = await getAddressFromPublicKey(newKeypair.publicKey);

console.log(`Public key: ${publicAddress}`);
}

export async function loadDefaultKeypair(): Promise<CryptoKeyPair> {
return await loadKeypairFromFile("~/.config/solana/id.json");
}

export async function loadDefaultKeypairWithAirdrop(
cluster: string,
): Promise<CryptoKeyPair> {
const keypair = await loadDefaultKeypair();
const rpc = createSolanaRpc(devnet(`https://api.${cluster}.solana.com`));
const rpcSubscriptions = createSolanaRpcSubscriptions(
devnet(`wss://api.${cluster}.solana.com`),
);
try {
const result = await rpc
.getBalance(await getAddressFromPublicKey(keypair.publicKey))
.send();

console.log(`Balance: ${result.value} lamports`);
if (result.value < lamports(500_000n)) {
console.log(`Balance low requesting airdrop`);
const airdrop = airdropFactory({ rpc, rpcSubscriptions });
await airdrop({
commitment: "confirmed",
lamports: lamports(1000_000n),
recipientAddress: await getAddressFromPublicKey(keypair.publicKey),
});
}
} catch (err) {
console.error("Error fetching balance:", err);
}
return keypair;
}

export async function loadKeypairFromFile(
filePath: string,
): Promise<CryptoKeyPair> {
// This is here so you can also load the default keypair from the file system.
const resolvedPath = path.resolve(
filePath.startsWith("~") ? filePath.replace("~", os.homedir()) : filePath,
);
const loadedKeyBytes = Uint8Array.from(
JSON.parse(fs.readFileSync(resolvedPath, "utf8")),
);
// Here you can also set the second parameter to true in case you need to extract your private key.
const keypair = await createKeyPairFromBytes(loadedKeyBytes);
return keypair;
}

createKeypair();
```
127 changes: 124 additions & 3 deletions content/cookbook/transactions/add-memo.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,126 @@ description:
how to add a memo to your transactions on Solana."
---

Any transaction can add a message making use of the memo program. Currently the
programID from the Memo Program has to be added manually
`MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr`.
Any transaction can add a message making use of the memo program. In web3.js@1
the programID from the Memo Program has to be added manually
`MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` in V2 you can use
`getAddMemoInstruction`.

<Tabs groupId="language" items={['web3.js v2', 'web3.js v1']}>

<Tab value="web3.js v2">

```typescript filename="add-memo.ts" {61-72}
import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
devnet,
generateKeyPairSigner,
getComputeUnitEstimateForTransactionMessageFactory,
getSignatureFromTransaction,
lamports,
pipe,
prependTransactionMessageInstructions,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners,
type Transaction,
} from "@solana/web3.js";
import {
getSetComputeUnitLimitInstruction,
getSetComputeUnitPriceInstruction,
} from "@solana-program/compute-budget";
import { getAddMemoInstruction } from "@solana-program/memo";

async function writeMemo(message: string) {
// Create an RPC.
const CLUSTER = "devnet";
const rpc = createSolanaRpc(devnet(`https://api.${CLUSTER}.solana.com`));
const rpcSubscriptions = createSolanaRpcSubscriptions(
devnet(`wss://api.${CLUSTER}.solana.com`),
);

// Create an airdrop function.
const airdrop = airdropFactory({ rpc, rpcSubscriptions });

// Create a utility that estimates a transaction message's compute consumption.
const getComputeUnitEstimate =
getComputeUnitEstimateForTransactionMessageFactory({ rpc });

// Create a transaction sending function.
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc,
rpcSubscriptions,
});

// Create and fund an account.
const keypairSigner = await generateKeyPairSigner();
console.log("Created an account with address", keypairSigner.address);
console.log("Requesting airdrop");
await airdrop({
commitment: "confirmed",
lamports: lamports(1000_000n),
recipientAddress: keypairSigner.address,
});
console.log("Airdrop confirmed");

// Create a memo transaction.
console.log("Creating a memo transaction");
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: "legacy" }),
m => setTransactionMessageFeePayerSigner(keypairSigner, m),
m => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
m =>
appendTransactionMessageInstructions(
[
getSetComputeUnitPriceInstruction({ microLamports: 5000n }),
getAddMemoInstruction({ memo: message }),
],
m,
),
);

// Figure out how many compute units to budget for this transaction
// so that you can right-size the compute budget to maximize the
// chance that it will be selected for inclusion into a block.
console.log("Estimating the compute consumption of the transaction");
const estimatedComputeUnits =
await getComputeUnitEstimate(transactionMessage);
console.log(
`Transaction is estimated to consume ${estimatedComputeUnits} compute units`,
);
const budgetedTransactionMessage = prependTransactionMessageInstructions(
[getSetComputeUnitLimitInstruction({ units: estimatedComputeUnits })],
transactionMessage,
);

// Sign and send the transaction.
console.log("Signing and sending the transaction");
const signedTx = await signTransactionMessageWithSigners(
budgetedTransactionMessage,
);
const signature = getSignatureFromTransaction(signedTx);
console.log(
"Sending transaction https://explorer.solana.com/tx/" +
signature +
"/?cluster=" +
CLUSTER,
);
await sendAndConfirmTransaction(signedTx, { commitment: "confirmed" });
console.log("Transaction confirmed");
}

writeMemo("Hello, Solana!");
```

</Tab>

<Tab value="web3.js v1">

```typescript filename="add-memo.ts" {38-46}
import {
Expand Down Expand Up @@ -63,3 +180,7 @@ import {
]);
})();
```

</Tab>

</Tabs>
126 changes: 126 additions & 0 deletions content/cookbook/transactions/add-priority-fees.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,128 @@ compute budget. The value provided will replace the default value. Transactions
should request the minimum amount of CU required for execution to maximize
throughput, or minimize fees.

<Tabs groupId="language" items={['web3.js v2', 'web3.js v1']}>

<Tab value="web3.js v2">

```typescript filename="add-priority-fees.ts" {61-72} {37-38} {77-87}
import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
devnet,
generateKeyPairSigner,
getComputeUnitEstimateForTransactionMessageFactory,
getSignatureFromTransaction,
lamports,
pipe,
prependTransactionMessageInstructions,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners,
} from "@solana/web3.js";
import {
getSetComputeUnitLimitInstruction,
getSetComputeUnitPriceInstruction,
} from "@solana-program/compute-budget";
import { getAddMemoInstruction } from "@solana-program/memo";

async function writeMemoWithPriorityFees(message: string) {
// Create an RPC.
const CLUSTER = "devnet";
const rpc = createSolanaRpc(devnet(`https://api.${CLUSTER}.solana.com`));
const rpcSubscriptions = createSolanaRpcSubscriptions(
devnet(`wss://api.${CLUSTER}.solana.com`),
);

// Create an airdrop function.
const airdrop = airdropFactory({ rpc, rpcSubscriptions });

// Create a utility that estimates a transaction message's compute consumption.
const getComputeUnitEstimate =
getComputeUnitEstimateForTransactionMessageFactory({ rpc });

// Create a transaction sending function.
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc,
rpcSubscriptions,
});

// Create and fund an account.
const keypairSigner = await generateKeyPairSigner();
console.log("Created an account with address", keypairSigner.address);
console.log("Requesting airdrop");
await airdrop({
commitment: "confirmed",
lamports: lamports(1000_000n),
recipientAddress: keypairSigner.address,
});
console.log("Airdrop confirmed");

// Create a memo transaction.
console.log("Creating a memo transaction");
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: "legacy" }),
m => setTransactionMessageFeePayerSigner(keypairSigner, m),
m => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
m =>
appendTransactionMessageInstructions(
[
getSetComputeUnitPriceInstruction({ microLamports: 5000n }),
getAddMemoInstruction({ memo: message }),
],
m,
),
);

// Figure out how many compute units to budget for this transaction
// so that you can right-size the compute budget to maximize the
// chance that it will be selected for inclusion into a block.
console.log("Estimating the compute consumption of the transaction");
var estimatedComputeUnits = await getComputeUnitEstimate(transactionMessage);
// While these estimates are quite accurate they are not perfect. So you may want to add a
// buffer if you expect that the transaction may consume more compute units than estimated.
// Its not possible to exactly know what the transaction will consume when
// you send it in the future. The state may change. You can add a buffer to the estimate to account for this.
// estimatedComputeUnits += 1000;
// estimatedComputeUnits *= 1.1;
// You can read more about the issue here: https://github.com/solana-labs/solana-web3.js/tree/master/packages/library#getcomputeunitestimatefortransactionmessagefactoryrpc

console.log(
`Transaction is estimated to consume ${estimatedComputeUnits} compute units`,
);
const budgetedTransactionMessage = prependTransactionMessageInstructions(
[getSetComputeUnitLimitInstruction({ units: estimatedComputeUnits })],
transactionMessage,
);

// Sign and send the transaction.
console.log("Signing and sending the transaction");
const signedTx = await signTransactionMessageWithSigners(
budgetedTransactionMessage,
);
const signature = getSignatureFromTransaction(signedTx);
console.log(
"Sending transaction https://explorer.solana.com/tx/" +
signature +
"/?cluster=" +
CLUSTER,
);
await sendAndConfirmTransaction(signedTx, { commitment: "confirmed" });
console.log("Transaction confirmed");
}

writeMemoWithPriorityFees("Hello, priority fees!");
```

</Tab>

<Tab value="web3.js v1">

```typescript filename="add-priority-fees.ts" {25-28, 30-33}
import { BN } from "@coral-xyz/anchor";
import {
Expand Down Expand Up @@ -85,3 +207,7 @@ import {
console.log(result);
})();
```

</Tab>

</Tabs>
Loading

0 comments on commit 0823b6a

Please sign in to comment.