use ts builder api (#54)

Co-authored-by: Paul Schaaf <paulsimonschaaf@gmail.com>
This commit is contained in:
lucas 2022-04-13 15:09:04 -03:00 committed by GitHub
parent 0099a11a65
commit e6603e3e2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 97 deletions

View File

@ -1,6 +1,6 @@
{ {
"dependencies": { "dependencies": {
"@project-serum/anchor": "^0.23.0" "@project-serum/anchor": "0.23.0"
}, },
"devDependencies": { "devDependencies": {
"@types/mocha": "^9.0.0", "@types/mocha": "^9.0.0",

View File

@ -2,7 +2,6 @@ use crate::errors::TicTacToeError;
use anchor_lang::prelude::*; use anchor_lang::prelude::*;
use num_derive::*; use num_derive::*;
use num_traits::*; use num_traits::*;
use std::mem;
#[account] #[account]
pub struct Game { pub struct Game {

View File

@ -6,14 +6,15 @@ import chaiAsPromised from 'chai-as-promised';
import { expect } from 'chai'; import { expect } from 'chai';
chai.use(chaiAsPromised); chai.use(chaiAsPromised);
async function play(program, game, player, tile, expectedTurn, expectedGameState, expectedBoard) { async function play(program: Program<TicTacToe>, game, player, tile, expectedTurn, expectedGameState, expectedBoard) {
await program.rpc.play(tile, { await program.methods
accounts: { .play(tile)
.accounts({
player: player.publicKey, player: player.publicKey,
game game
}, })
signers: player instanceof (anchor.Wallet as any) ? [] : [player] .signers(player instanceof (anchor.Wallet as any) ? [] : [player])
}); .rpc();
const gameState = await program.account.game.fetch(game); const gameState = await program.account.game.fetch(game);
expect(gameState.turn).to.equal(expectedTurn); expect(gameState.turn).to.equal(expectedTurn);
@ -33,14 +34,14 @@ describe('tic-tac-toe', () => {
const gameKeypair = anchor.web3.Keypair.generate(); const gameKeypair = anchor.web3.Keypair.generate();
const playerOne = program.provider.wallet; const playerOne = program.provider.wallet;
const playerTwo = anchor.web3.Keypair.generate(); const playerTwo = anchor.web3.Keypair.generate();
await program.rpc.setupGame(playerTwo.publicKey, { await program.methods
accounts: { .setupGame(playerTwo.publicKey)
.accounts({
game: gameKeypair.publicKey, game: gameKeypair.publicKey,
playerOne: playerOne.publicKey, playerOne: playerOne.publicKey,
systemProgram: anchor.web3.SystemProgram.programId })
}, .signers([gameKeypair])
signers: [gameKeypair] .rpc();
});
let gameState = await program.account.game.fetch(gameKeypair.publicKey); let gameState = await program.account.game.fetch(gameKeypair.publicKey);
expect(gameState.turn).to.equal(1); expect(gameState.turn).to.equal(1);
@ -57,14 +58,14 @@ describe('tic-tac-toe', () => {
const gameKeypair = anchor.web3.Keypair.generate(); const gameKeypair = anchor.web3.Keypair.generate();
const playerOne = program.provider.wallet; const playerOne = program.provider.wallet;
const playerTwo = anchor.web3.Keypair.generate(); const playerTwo = anchor.web3.Keypair.generate();
await program.rpc.setupGame(playerTwo.publicKey, { await program.methods
accounts: { .setupGame(playerTwo.publicKey)
.accounts({
game: gameKeypair.publicKey, game: gameKeypair.publicKey,
playerOne: playerOne.publicKey, playerOne: playerOne.publicKey,
systemProgram: anchor.web3.SystemProgram.programId })
}, .signers([gameKeypair])
signers: [gameKeypair] .rpc();
});
let gameState = await program.account.game.fetch(gameKeypair.publicKey); let gameState = await program.account.game.fetch(gameKeypair.publicKey);
expect(gameState.turn).to.equal(1); expect(gameState.turn).to.equal(1);
@ -243,14 +244,14 @@ describe('tic-tac-toe', () => {
const gameKeypair = anchor.web3.Keypair.generate(); const gameKeypair = anchor.web3.Keypair.generate();
const playerOne = program.provider.wallet; const playerOne = program.provider.wallet;
const playerTwo = anchor.web3.Keypair.generate(); const playerTwo = anchor.web3.Keypair.generate();
await program.rpc.setupGame(playerTwo.publicKey, { await program.methods
accounts: { .setupGame(playerTwo.publicKey)
.accounts({
game: gameKeypair.publicKey, game: gameKeypair.publicKey,
playerOne: playerOne.publicKey, playerOne: playerOne.publicKey,
systemProgram: anchor.web3.SystemProgram.programId })
}, .signers([gameKeypair])
signers: [gameKeypair] .rpc();
});
let gameState = await program.account.game.fetch(gameKeypair.publicKey); let gameState = await program.account.game.fetch(gameKeypair.publicKey);
expect(gameState.turn).to.equal(1); expect(gameState.turn).to.equal(1);

View File

@ -30,7 +30,7 @@
"@ethersproject/logger" "^5.5.0" "@ethersproject/logger" "^5.5.0"
hash.js "1.1.7" hash.js "1.1.7"
"@project-serum/anchor@^0.23.0": "@project-serum/anchor@0.23.0":
version "0.23.0" version "0.23.0"
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.23.0.tgz#2b2eb6b51601b073e8db26663aa2d6c2f2841771" resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.23.0.tgz#2b2eb6b51601b073e8db26663aa2d6c2f2841771"
integrity sha512-LV2/ifZOJVFTZ4GbEloXln3iVfCvO1YM8i7BBCrUm4tehP7irMx4nr4/IabHWOzrQcQElsxSP/lb1tBp+2ff8A== integrity sha512-LV2/ifZOJVFTZ4GbEloXln3iVfCvO1YM8i7BBCrUm4tehP7irMx4nr4/IabHWOzrQcQElsxSP/lb1tBp+2ff8A==

View File

@ -152,21 +152,22 @@ describe('puppet', () => {
const puppetKeypair = Keypair.generate(); const puppetKeypair = Keypair.generate();
it('Does CPI!', async () => { it('Does CPI!', async () => {
await puppetProgram.rpc.initialize({ await puppetProgram.methods
accounts: { .initialize()
puppet: puppetKeypair.publicKey, .accounts({
user: anchor.getProvider().wallet.publicKey, puppet: puppetKeypair.publicKey,
systemProgram: SystemProgram.programId user: anchor.getProvider().wallet.publicKey,
}, })
signers: [puppetKeypair] .signers([puppetKeypair])
}); .rpc();
await puppetMasterProgram.rpc.pullStrings(new anchor.BN(42),{ await puppetMasterProgram.methods
accounts: { .pullStrings(new anchor.BN(42))
puppetProgram: puppetProgram.programId, .accounts({
puppet: puppetKeypair.publicKey puppetProgram: puppetProgram.programId,
} puppet: puppetKeypair.publicKey
}) })
.rpc();
expect((await puppetProgram.account.data expect((await puppetProgram.account.data
.fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42); .fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42);
@ -281,23 +282,24 @@ describe('puppet', () => {
const authorityKeypair = Keypair.generate(); const authorityKeypair = Keypair.generate();
it('Does CPI!', async () => { it('Does CPI!', async () => {
await puppetProgram.rpc.initialize(authorityKeypair.publicKey, { await puppetProgram.methods
accounts: { .initialize(authorityKeypair.publicKey)
puppet: puppetKeypair.publicKey, .accounts({
user: anchor.getProvider().wallet.publicKey, puppet: puppetKeypair.publicKey,
systemProgram: SystemProgram.programId, user: anchor.getProvider().wallet.publicKey,
}, })
signers: [puppetKeypair] .signers([puppetKeypair])
}); .rpc();
await puppetMasterProgram.rpc.pullStrings(new anchor.BN(42),{ await puppetMasterProgram.methods
accounts: { .pullStrings(new anchor.BN(42))
puppetProgram: puppetProgram.programId, .accounts({
puppet: puppetKeypair.publicKey, puppetProgram: puppetProgram.programId,
authority: authorityKeypair.publicKey puppet: puppetKeypair.publicKey,
}, authority: authorityKeypair.publicKey
signers: [authorityKeypair] })
}) .signers([authorityKeypair])
.rpc();
expect((await puppetProgram.account.data expect((await puppetProgram.account.data
.fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42); .fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42);

View File

@ -198,22 +198,23 @@ anchor.setProvider(anchor.Provider.env());
program.programId program.programId
); );
await program.rpc.createUserStats("brian", { await program.methods
accounts: { .createUserStats("brian")
.accounts({
user: anchor.getProvider().wallet.publicKey, user: anchor.getProvider().wallet.publicKey,
userStats: userStatsPDA, userStats: userStatsPDA,
systemProgram: SystemProgram.programId })
} .rpc();
});
expect((await program.account.userStats.fetch(userStatsPDA)).name).to.equal("brian"); expect((await program.account.userStats.fetch(userStatsPDA)).name).to.equal("brian");
await program.rpc.changeUserName("tom", { await program.methods
accounts: { .changeUserName("tom")
.accounts({
user: anchor.getProvider().wallet.publicKey, user: anchor.getProvider().wallet.publicKey,
userStats: userStatsPDA userStats: userStatsPDA
} })
}) .rpc();
expect((await program.account.userStats.fetch(userStatsPDA)).name).to.equal("tom"); expect((await program.account.userStats.fetch(userStatsPDA)).name).to.equal("tom");
}); });
@ -305,22 +306,23 @@ describe('puppet', () => {
const [puppetMasterPDA, puppetMasterBump] = await PublicKey const [puppetMasterPDA, puppetMasterBump] = await PublicKey
.findProgramAddress([], puppetMasterProgram.programId); .findProgramAddress([], puppetMasterProgram.programId);
await puppetProgram.rpc.initialize(puppetMasterPDA, { await puppetProgram.methods
accounts: { .initialize(puppetMasterPDA)
.accounts({
puppet: puppetKeypair.publicKey, puppet: puppetKeypair.publicKey,
user: anchor.getProvider().wallet.publicKey, user: anchor.getProvider().wallet.publicKey,
systemProgram: SystemProgram.programId, })
}, .signers([puppetKeypair])
signers: [puppetKeypair] .rpc();
});
await puppetMasterProgram.rpc.pullStrings(puppetMasterBump, new anchor.BN(42),{ await puppetMasterProgram.methods
accounts: { .pullStrings(puppetMasterBump, new anchor.BN(42))
.accounts({
puppetProgram: puppetProgram.programId, puppetProgram: puppetProgram.programId,
puppet: puppetKeypair.publicKey, puppet: puppetKeypair.publicKey,
authority: puppetMasterPDA authority: puppetMasterPDA
, })
}}); .rpc();
expect((await puppetProgram.account.data expect((await puppetProgram.account.data
.fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42); .fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42);

View File

@ -280,14 +280,14 @@ Time to test our code! Head over into the `tests` folder in the root directory.
const gameKeypair = anchor.web3.Keypair.generate(); const gameKeypair = anchor.web3.Keypair.generate();
const playerOne = program.provider.wallet; const playerOne = program.provider.wallet;
const playerTwo = anchor.web3.Keypair.generate(); const playerTwo = anchor.web3.Keypair.generate();
await program.rpc.setupGame(playerTwo.publicKey, { await program.methods
accounts: { .setupGame(playerTwo.publicKey)
.accounts({
game: gameKeypair.publicKey, game: gameKeypair.publicKey,
playerOne: playerOne.publicKey, playerOne: playerOne.publicKey,
systemProgram: anchor.web3.SystemProgram.programId })
}, .signers([gameKeypair])
signers: [gameKeypair] .rpc();
});
let gameState = await program.account.game.fetch(gameKeypair.publicKey); let gameState = await program.account.game.fetch(gameKeypair.publicKey);
expect(gameState.turn).to.equal(1); expect(gameState.turn).to.equal(1);
@ -310,14 +310,15 @@ import { expect } from 'chai';
> This is likely because the test file is looking for types from your program that haven't been generated yet. > This is likely because the test file is looking for types from your program that haven't been generated yet.
> To generate them, run `anchor build`. This builds the program and creates the idl and typescript types. > To generate them, run `anchor build`. This builds the program and creates the idl and typescript types.
The test begins by creating some keypairs. Importantly, `playerOne` is not a keypair but the wallet of the program's provider. The provider details are defined in the `Anchor.toml` file in the root of the project. The test begins by creating some keypairs. Importantly, `playerOne` is not a keypair but the wallet of the program's provider. The provider details are defined in the `Anchor.toml` file in the root of the project. The provider serves as the keypair that pays for (and therefore signs) all transactions.
Then, we send the transaction. Because the anchor typescript client has parsed the IDL, all transaction inputs have types. If you remove one of the accounts for example, typescript will complain. Then, we send the transaction.
The structure of the transaction function is as follows: First come the instruction arguments. For this function, the public key of the second player. Then come the accounts. Lastly, we add a signers array. We have to add the `gameKeypair` here because whenever an account gets created, it has to sign its creation transaction. We don't have to add `playerOne` even though we gave it the `Signer` type in the program because it is the program provider and therefore signs the transaction by default. The structure of the transaction function is as follows: First come the instruction arguments. For this function, the public key of the second player. Then come the accounts. Lastly, we add a signers array. We have to add the `gameKeypair` here because whenever an account gets created, it has to sign its creation transaction. We don't have to add `playerOne` even though we gave it the `Signer` type in the program because it is the program provider and therefore signs the transaction by default.
We did not have to specify the `system_program` account. This is because anchor recognizes this account and is able to infer it. This is also true for other known accounts such as the `token_program` or the `rent` sysvar account.
After the transaction returns, we can fetch the state of the game account. You can fetch account state using the `program.account` namespace. After the transaction returns, we can fetch the state of the game account. You can fetch account state using the `program.account` namespace.
Finally, we verify the game has been set up properly. Anchor's typescript client deserializes rust enums like this: `{ active: {}}` for a fieldless variant and `{ won: { winner: Pubkey }}` for a variant with fields. The `None` variant of `Option` becomes `null`. The `Some(x)` variant becomes whatever `x` deserializes to. Finally, we verify the game has been set up properly. Anchor's typescript client deserializes rust enums like this: `{ active: {}}` for a fieldless variant and `{ won: { winner: Pubkey }}` for a variant with fields. The `None` variant of `Option` becomes `null`. The `Some(x)` variant becomes whatever `x` deserializes to.
Now, run `anchor test`. This starts up (and subsequently shuts down) a local validator (make sure you don't have one running) and runs your tests using the test script defined in `Anchor.toml`. Now, run `anchor test`. This starts up (and subsequently shuts down) a local validator (make sure you don't have one running before) and runs your tests using the test script defined in `Anchor.toml`.
> If you get the error `Error: Unable to read keypair file` when running the test, you likely need to generate a Solana keypair using `solana-keygen new`. > If you get the error `Error: Unable to read keypair file` when running the test, you likely need to generate a Solana keypair using `solana-keygen new`.
@ -358,15 +359,16 @@ We've checked in the accounts struct that the `player` account has signed the tr
Testing the `play` instruction works the exact same way. To avoid repeating yourself, create a helper function at the top of the test file: Testing the `play` instruction works the exact same way. To avoid repeating yourself, create a helper function at the top of the test file:
```typescript ```typescript
async function play(program, game, player, async function play(program: Program<TicTacToe>, game, player,
tile, expectedTurn, expectedGameState, expectedBoard) { tile, expectedTurn, expectedGameState, expectedBoard) {
await program.rpc.play(tile, { await program.methods
accounts: { .play(tile)
.accounts({
player: player.publicKey, player: player.publicKey,
game game
}, })
signers: player instanceof (anchor.Wallet as any) ? [] : [player] .signers(player instanceof (anchor.Wallet as any) ? [] : [player])
}); .rpc();
const gameState = await program.account.game.fetch(game); const gameState = await program.account.game.fetch(game);
expect(gameState.turn).to.equal(expectedTurn); expect(gameState.turn).to.equal(expectedTurn);
@ -383,14 +385,14 @@ it('player one wins', async() => {
const gameKeypair = anchor.web3.Keypair.generate(); const gameKeypair = anchor.web3.Keypair.generate();
const playerOne = program.provider.wallet; const playerOne = program.provider.wallet;
const playerTwo = anchor.web3.Keypair.generate(); const playerTwo = anchor.web3.Keypair.generate();
await program.rpc.setupGame(playerTwo.publicKey, { await program.methods
accounts: { .setupGame(playerTwo.publicKey)
.accounts({
game: gameKeypair.publicKey, game: gameKeypair.publicKey,
playerOne: playerOne.publicKey, playerOne: playerOne.publicKey,
systemProgram: anchor.web3.SystemProgram.programId })
}, .signers([gameKeypair])
signers: [gameKeypair] .rpc();
});
let gameState = await program.account.game.fetch(gameKeypair.publicKey); let gameState = await program.account.game.fetch(gameKeypair.publicKey);
expect(gameState.turn).to.equal(1); expect(gameState.turn).to.equal(1);