Skip to content

Commit

Permalink
d
Browse files Browse the repository at this point in the history
  • Loading branch information
mikemaccana committed Jun 21, 2024
1 parent 3a26949 commit 8278a3e
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 23 deletions.
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Eventually, most of these will end up in `@solana/web3.js`.

[Make multiple keypairs at once](#make-multiple-keypairs-at-once)

[Create multiple accounts with balances of different tokens in a single step](#fixme)

[Resolve a custom error message](#resolve-a-custom-error-message)

[Get an airdrop if your balance is below some amount](#get-an-airdrop-if-your-balance-is-below-some-amount)
Expand Down Expand Up @@ -48,19 +50,54 @@ Usage:
makeKeypairs(amount);
```

In some situations - like making tests for your on-chain programs - you might need to make lots of keypairs at once. You can use `makeKeypairs()` combined with JS destructuring to quickly create multiple variables with distinct keypairs.
In some situations - like making tests for your onchain programs - you might need to make lots of keypairs at once. You can use `makeKeypairs()` combined with JS destructuring to quickly create multiple variables with distinct keypairs.

```typescript
const [sender, recipient] = makeKeypairs(2);
```

### Create users, mints and token accounts in a single step

Frequently, tests for onchain programs need to make not just users with SOL, but also token mints and give each user some balance of each token. To save this boilerlate, `createAccountsMintsAndTokenAccounts()` handles making user keypairs, giving hem SOL, making mints, creating associated token accounts, and minting tokens directly to the associated token accounts.

Eg, to make two new users, and two tokens:

- the first user with million of the first token, none of the second token, and 1 SOL
- the second user with 1none of the first token, 1 million of the second token, and 1 SOL

Just run:

````typescript
const usersMintsAndTokenAccounts = await createAccountsMintsAndTokenAccounts(
[
[1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B
[0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B
],
1 * LAMPORTS_PER_SOL,
connection,
payer,
);
```

The returned `usersMintsAndTokenAccounts` will be an object of the form:

```
{
users: <Array<Keypair>>
mints: <Array<Keypair>>,
tokenAccounts: <Array<Array><PublicKey>>>
}
```
tokenAccounts are indexed by the user, then the mint. Eg, so the ATA of user[0] for mint[0] is tokenAccounts[0][0].


### Resolve a custom error message

Usage:

```typescript
getCustomErrorMessage(programErrors, errorMessage);
```
````
Sometimes Solana transactions throw an error with a message like:
Expand Down
39 changes: 20 additions & 19 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { exec as execNoPromises } from "child_process";
import { promisify } from "util";
import { writeFile, unlink as deleteFile } from "node:fs/promises";
import dotenv from "dotenv";
import { createUsersMintsAndTokenAccounts } from "./index.js";
import { createAccountsMintsAndTokenAccounts } from "./index.js";

const exec = promisify(execNoPromises);

Expand Down Expand Up @@ -501,8 +501,8 @@ describe("getSimulationComputeUnits", () => {
});
});

describe("createUsersMintsAndTokenAccounts", () => {
test("createUsersMintsAndTokenAccounts works", async () => {
describe("createAccountsMintsAndTokenAccounts", () => {
test("createAccountsMintsAndTokenAccounts works", async () => {
const payer = Keypair.generate();
const connection = new Connection(LOCALHOST);
await airdropIfRequired(
Expand All @@ -514,25 +514,26 @@ describe("createUsersMintsAndTokenAccounts", () => {

const SOL_BALANCE = 10 * LAMPORTS_PER_SOL;

const usersMintsAndTokenAccounts = await createUsersMintsAndTokenAccounts(
[
[1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B
[0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B
],
SOL_BALANCE,
connection,
payer,
);

// Check the users and their balances
const usersMintsAndTokenAccounts =
await createAccountsMintsAndTokenAccounts(
[
[1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B
[0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B
],
SOL_BALANCE,
connection,
payer,
);

// Check all users have been created and have some SOL
const users = usersMintsAndTokenAccounts.users;
assert.equal(users.length, 2);
const firstUserSOLBalance = await connection.getBalance(users[0].publicKey);
assert(firstUserSOLBalance === SOL_BALANCE);
const secondUserSOLBalance = await connection.getBalance(
users[1].publicKey,
await Promise.all(
users.map(async (user) => {
const balance = await connection.getBalance(user.publicKey);
assert(balance === SOL_BALANCE);
}),
);
assert(secondUserSOLBalance === SOL_BALANCE);

// Check the mints
assert.equal(usersMintsAndTokenAccounts.mints.length, 2);
Expand Down
8 changes: 6 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,12 @@ const makeAndSendAndConfirmTransaction = async (
await confirmTransaction(connection, signature);
};

// Create users, mints, mint tokens and create ATAs
export const createUsersMintsAndTokenAccounts = async (
// Create users, mints, create ATAs and mint tokens.
// TODO: we may actually want to split this into multiple transactions
// to avoid the transaction size limit (or use lookup tables)
// in the future. However it works for two transactions of the size
// used in our unit tests.
export const createAccountsMintsAndTokenAccounts = async (
usersAndTokenBalances: Array<Array<number>>,
lamports: number,
connection: Connection,
Expand Down

0 comments on commit 8278a3e

Please sign in to comment.