diff --git a/programs/tic-tac-toe/package.json b/programs/tic-tac-toe/package.json index 7c60a97..24d7c1a 100644 --- a/programs/tic-tac-toe/package.json +++ b/programs/tic-tac-toe/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "@project-serum/anchor": "^0.23.0" + "@project-serum/anchor": "0.23.0" }, "devDependencies": { "@types/mocha": "^9.0.0", diff --git a/programs/tic-tac-toe/programs/tic-tac-toe/src/state/game.rs b/programs/tic-tac-toe/programs/tic-tac-toe/src/state/game.rs index 70ed94d..cf3acb0 100644 --- a/programs/tic-tac-toe/programs/tic-tac-toe/src/state/game.rs +++ b/programs/tic-tac-toe/programs/tic-tac-toe/src/state/game.rs @@ -2,7 +2,6 @@ use crate::errors::TicTacToeError; use anchor_lang::prelude::*; use num_derive::*; use num_traits::*; -use std::mem; #[account] pub struct Game { diff --git a/programs/tic-tac-toe/tests/tic-tac-toe.ts b/programs/tic-tac-toe/tests/tic-tac-toe.ts index 0ba0ccb..e2f65e7 100644 --- a/programs/tic-tac-toe/tests/tic-tac-toe.ts +++ b/programs/tic-tac-toe/tests/tic-tac-toe.ts @@ -6,14 +6,15 @@ import chaiAsPromised from 'chai-as-promised'; import { expect } from 'chai'; chai.use(chaiAsPromised); -async function play(program, game, player, tile, expectedTurn, expectedGameState, expectedBoard) { - await program.rpc.play(tile, { - accounts: { +async function play(program: Program, game, player, tile, expectedTurn, expectedGameState, expectedBoard) { + await program.methods + .play(tile) + .accounts({ player: player.publicKey, 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); expect(gameState.turn).to.equal(expectedTurn); @@ -33,14 +34,14 @@ describe('tic-tac-toe', () => { const gameKeypair = anchor.web3.Keypair.generate(); const playerOne = program.provider.wallet; const playerTwo = anchor.web3.Keypair.generate(); - await program.rpc.setupGame(playerTwo.publicKey, { - accounts: { + await program.methods + .setupGame(playerTwo.publicKey) + .accounts({ game: gameKeypair.publicKey, playerOne: playerOne.publicKey, - systemProgram: anchor.web3.SystemProgram.programId - }, - signers: [gameKeypair] - }); + }) + .signers([gameKeypair]) + .rpc(); let gameState = await program.account.game.fetch(gameKeypair.publicKey); expect(gameState.turn).to.equal(1); @@ -57,14 +58,14 @@ describe('tic-tac-toe', () => { const gameKeypair = anchor.web3.Keypair.generate(); const playerOne = program.provider.wallet; const playerTwo = anchor.web3.Keypair.generate(); - await program.rpc.setupGame(playerTwo.publicKey, { - accounts: { + await program.methods + .setupGame(playerTwo.publicKey) + .accounts({ game: gameKeypair.publicKey, playerOne: playerOne.publicKey, - systemProgram: anchor.web3.SystemProgram.programId - }, - signers: [gameKeypair] - }); + }) + .signers([gameKeypair]) + .rpc(); let gameState = await program.account.game.fetch(gameKeypair.publicKey); expect(gameState.turn).to.equal(1); @@ -243,14 +244,14 @@ describe('tic-tac-toe', () => { const gameKeypair = anchor.web3.Keypair.generate(); const playerOne = program.provider.wallet; const playerTwo = anchor.web3.Keypair.generate(); - await program.rpc.setupGame(playerTwo.publicKey, { - accounts: { + await program.methods + .setupGame(playerTwo.publicKey) + .accounts({ game: gameKeypair.publicKey, playerOne: playerOne.publicKey, - systemProgram: anchor.web3.SystemProgram.programId - }, - signers: [gameKeypair] - }); + }) + .signers([gameKeypair]) + .rpc(); let gameState = await program.account.game.fetch(gameKeypair.publicKey); expect(gameState.turn).to.equal(1); diff --git a/programs/tic-tac-toe/yarn.lock b/programs/tic-tac-toe/yarn.lock index e3b8457..b992265 100644 --- a/programs/tic-tac-toe/yarn.lock +++ b/programs/tic-tac-toe/yarn.lock @@ -30,7 +30,7 @@ "@ethersproject/logger" "^5.5.0" hash.js "1.1.7" -"@project-serum/anchor@^0.23.0": +"@project-serum/anchor@0.23.0": version "0.23.0" resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.23.0.tgz#2b2eb6b51601b073e8db26663aa2d6c2f2841771" integrity sha512-LV2/ifZOJVFTZ4GbEloXln3iVfCvO1YM8i7BBCrUm4tehP7irMx4nr4/IabHWOzrQcQElsxSP/lb1tBp+2ff8A== diff --git a/src/chapter_3/CPIs.md b/src/chapter_3/CPIs.md index 27c4edc..04ab48c 100644 --- a/src/chapter_3/CPIs.md +++ b/src/chapter_3/CPIs.md @@ -152,21 +152,22 @@ describe('puppet', () => { const puppetKeypair = Keypair.generate(); it('Does CPI!', async () => { - await puppetProgram.rpc.initialize({ - accounts: { - puppet: puppetKeypair.publicKey, - user: anchor.getProvider().wallet.publicKey, - systemProgram: SystemProgram.programId - }, - signers: [puppetKeypair] - }); + await puppetProgram.methods + .initialize() + .accounts({ + puppet: puppetKeypair.publicKey, + user: anchor.getProvider().wallet.publicKey, + }) + .signers([puppetKeypair]) + .rpc(); - await puppetMasterProgram.rpc.pullStrings(new anchor.BN(42),{ - accounts: { - puppetProgram: puppetProgram.programId, - puppet: puppetKeypair.publicKey - } - }) + await puppetMasterProgram.methods + .pullStrings(new anchor.BN(42)) + .accounts({ + puppetProgram: puppetProgram.programId, + puppet: puppetKeypair.publicKey + }) + .rpc(); expect((await puppetProgram.account.data .fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42); @@ -281,23 +282,24 @@ describe('puppet', () => { const authorityKeypair = Keypair.generate(); it('Does CPI!', async () => { - await puppetProgram.rpc.initialize(authorityKeypair.publicKey, { - accounts: { - puppet: puppetKeypair.publicKey, - user: anchor.getProvider().wallet.publicKey, - systemProgram: SystemProgram.programId, - }, - signers: [puppetKeypair] - }); + await puppetProgram.methods + .initialize(authorityKeypair.publicKey) + .accounts({ + puppet: puppetKeypair.publicKey, + user: anchor.getProvider().wallet.publicKey, + }) + .signers([puppetKeypair]) + .rpc(); - await puppetMasterProgram.rpc.pullStrings(new anchor.BN(42),{ - accounts: { - puppetProgram: puppetProgram.programId, - puppet: puppetKeypair.publicKey, - authority: authorityKeypair.publicKey - }, - signers: [authorityKeypair] - }) + await puppetMasterProgram.methods + .pullStrings(new anchor.BN(42)) + .accounts({ + puppetProgram: puppetProgram.programId, + puppet: puppetKeypair.publicKey, + authority: authorityKeypair.publicKey + }) + .signers([authorityKeypair]) + .rpc(); expect((await puppetProgram.account.data .fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42); diff --git a/src/chapter_3/PDAs.md b/src/chapter_3/PDAs.md index 81a951d..8aafccd 100644 --- a/src/chapter_3/PDAs.md +++ b/src/chapter_3/PDAs.md @@ -198,22 +198,23 @@ anchor.setProvider(anchor.Provider.env()); program.programId ); - await program.rpc.createUserStats("brian", { - accounts: { + await program.methods + .createUserStats("brian") + .accounts({ user: anchor.getProvider().wallet.publicKey, userStats: userStatsPDA, - systemProgram: SystemProgram.programId - } - }); + }) + .rpc(); expect((await program.account.userStats.fetch(userStatsPDA)).name).to.equal("brian"); - await program.rpc.changeUserName("tom", { - accounts: { + await program.methods + .changeUserName("tom") + .accounts({ user: anchor.getProvider().wallet.publicKey, userStats: userStatsPDA - } - }) + }) + .rpc(); expect((await program.account.userStats.fetch(userStatsPDA)).name).to.equal("tom"); }); @@ -305,22 +306,23 @@ describe('puppet', () => { const [puppetMasterPDA, puppetMasterBump] = await PublicKey .findProgramAddress([], puppetMasterProgram.programId); - await puppetProgram.rpc.initialize(puppetMasterPDA, { - accounts: { + await puppetProgram.methods + .initialize(puppetMasterPDA) + .accounts({ puppet: puppetKeypair.publicKey, user: anchor.getProvider().wallet.publicKey, - systemProgram: SystemProgram.programId, - }, - signers: [puppetKeypair] - }); + }) + .signers([puppetKeypair]) + .rpc(); - await puppetMasterProgram.rpc.pullStrings(puppetMasterBump, new anchor.BN(42),{ - accounts: { + await puppetMasterProgram.methods + .pullStrings(puppetMasterBump, new anchor.BN(42)) + .accounts({ puppetProgram: puppetProgram.programId, puppet: puppetKeypair.publicKey, authority: puppetMasterPDA - , - }}); + }) + .rpc(); expect((await puppetProgram.account.data .fetch(puppetKeypair.publicKey)).data.toNumber()).to.equal(42); diff --git a/src/chapter_3/milestone_project_tic-tac-toe.md b/src/chapter_3/milestone_project_tic-tac-toe.md index 68b62bd..4bd1070 100644 --- a/src/chapter_3/milestone_project_tic-tac-toe.md +++ b/src/chapter_3/milestone_project_tic-tac-toe.md @@ -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 playerOne = program.provider.wallet; const playerTwo = anchor.web3.Keypair.generate(); - await program.rpc.setupGame(playerTwo.publicKey, { - accounts: { + await program.methods + .setupGame(playerTwo.publicKey) + .accounts({ game: gameKeypair.publicKey, playerOne: playerOne.publicKey, - systemProgram: anchor.web3.SystemProgram.programId - }, - signers: [gameKeypair] - }); + }) + .signers([gameKeypair]) + .rpc(); let gameState = await program.account.game.fetch(gameKeypair.publicKey); 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. > 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. -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. +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. 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. 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`. @@ -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: ```typescript -async function play(program, game, player, +async function play(program: Program, game, player, tile, expectedTurn, expectedGameState, expectedBoard) { - await program.rpc.play(tile, { - accounts: { + await program.methods + .play(tile) + .accounts({ player: player.publicKey, 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); expect(gameState.turn).to.equal(expectedTurn); @@ -383,14 +385,14 @@ it('player one wins', async() => { const gameKeypair = anchor.web3.Keypair.generate(); const playerOne = program.provider.wallet; const playerTwo = anchor.web3.Keypair.generate(); - await program.rpc.setupGame(playerTwo.publicKey, { - accounts: { + await program.methods + .setupGame(playerTwo.publicKey) + .accounts({ game: gameKeypair.publicKey, playerOne: playerOne.publicKey, - systemProgram: anchor.web3.SystemProgram.programId - }, - signers: [gameKeypair] - }); + }) + .signers([gameKeypair]) + .rpc(); let gameState = await program.account.game.fetch(gameKeypair.publicKey); expect(gameState.turn).to.equal(1);