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

Add example using the instructions array for a state RPC #84 #2501

Merged
Merged
9 changes: 9 additions & 0 deletions examples/tutorial/basic-5/Anchor.toml
lastemp marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"

[programs.localnet]
basic_5 = "DuT6R8tQGYa8ACYXyudFJtxDppSALLcmK39b7918jeSC"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
4 changes: 4 additions & 0 deletions examples/tutorial/basic-5/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[workspace]
members = [
"programs/*"
]
13 changes: 13 additions & 0 deletions examples/tutorial/basic-5/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# basic-5

This is a robot program developed as a Rust Smart Contract(running on Solana Blockchain).
It simplifies actions of a robot on-chain.
This program acts as an example for developers who are new to Solana ecosystem to learn on how the typescript client interacts with the program on-chain.

Instructions of the program:

1. Create
2. Walk
3. Run
4. Jump
5. Reset
19 changes: 19 additions & 0 deletions examples/tutorial/basic-5/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "basic-5",
"version": "0.27.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
},
"scripts": {
"test": "anchor test --skip-lint"
}
}
17 changes: 17 additions & 0 deletions examples/tutorial/basic-5/programs/basic-5/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "basic-5"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.60"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "basic_5"

[features]
no-entrypoint = []
cpi = ["no-entrypoint"]

[dependencies]
anchor-lang = { path = "../../../../../lang" }
2 changes: 2 additions & 0 deletions examples/tutorial/basic-5/programs/basic-5/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
115 changes: 115 additions & 0 deletions examples/tutorial/basic-5/programs/basic-5/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use anchor_lang::prelude::*;

declare_id!("DuT6R8tQGYa8ACYXyudFJtxDppSALLcmK39b7918jeSC");

#[program]
pub mod basic_5 {
use super::*;

pub fn create(ctx: Context<Create>) -> Result<()> {
let action_state = &mut ctx.accounts.action_state;
// * - means dereferencing
action_state.user = *ctx.accounts.user.key;
// Lets initialize the state
action_state.action = 0;

Ok(())
}

pub fn walk(ctx: Context<Walk>) -> Result<()> {
let action_state = &mut ctx.accounts.action_state;
// Lets change the robot action state to "walk"
action_state.action = 1;

Ok(())
}

pub fn run(ctx: Context<Run>) -> Result<()> {
let action_state = &mut ctx.accounts.action_state;
// Lets change the robot action state to "run"
action_state.action = 2;

Ok(())
}

pub fn jump(ctx: Context<Jump>) -> Result<()> {
let action_state = &mut ctx.accounts.action_state;
// Lets change the robot action state to "jump"
action_state.action = 3;

Ok(())
}

pub fn reset(ctx: Context<Reset>) -> Result<()> {
let action_state = &mut ctx.accounts.action_state;
// Lets reset the robot action states
action_state.action = 0;

Ok(())
}
}

#[derive(Accounts)]
pub struct Create<'info> {
// init means to create action_state account
// bump to use unique address for action_state account
#[account(
init,
payer = user,
space = 8 + ActionState::INIT_SPACE,
seeds = [b"action-state", user.key().as_ref()],
bump
)]
pub action_state: Account<'info, ActionState>,
// mut makes it changeble (mutable)
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Walk<'info> {
// Only the user on account action_state, should be able to change state
#[account(mut, has_one = user)]
pub action_state: Account<'info, ActionState>,
// mut makes it changeble (mutable)
#[account(mut)]
pub user: Signer<'info>,
}

#[derive(Accounts)]
pub struct Run<'info> {
// Only the user on account action_state, should be able to change state
#[account(mut, has_one = user)]
pub action_state: Account<'info, ActionState>,
// mut makes it changeble (mutable)
#[account(mut)]
pub user: Signer<'info>,
}

#[derive(Accounts)]
pub struct Jump<'info> {
// Only the user on account action_state, should be able to change state
#[account(mut, has_one = user)]
pub action_state: Account<'info, ActionState>,
// mut makes it changeble (mutable)
#[account(mut)]
pub user: Signer<'info>,
}

#[derive(Accounts)]
pub struct Reset<'info> {
// Only the user on account action_state, should be able to change state
#[account(mut, has_one = user)]
pub action_state: Account<'info, ActionState>,
// mut makes it changeble (mutable)
#[account(mut)]
pub user: Signer<'info>,
}

#[account]
#[derive(InitSpace)]
pub struct ActionState {
pub user: Pubkey,
pub action: u8,
}
123 changes: 123 additions & 0 deletions examples/tutorial/basic-5/tests/basic-5.ts
lastemp marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import * as anchor from "@coral-xyz/anchor";
import {
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { Basic5 } from "../target/types/basic_5";

describe("basic-5", () => {
const provider = anchor.AnchorProvider.local();

// Configure the client to use the local cluster.
anchor.setProvider(provider);

const program = anchor.workspace.Basic5 as anchor.Program<Basic5>;
const user = provider.wallet.publicKey;

let [actionState] = anchor.web3.PublicKey.findProgramAddressSync(
[Buffer.from("action-state"), user.toBuffer()],
program.programId
);

it("basic-5: Robot actions!", async () => {
// Create instruction: set up the Solana accounts to be used
const createInstruction = await program.methods
.create()
.accounts({
actionState,
user,
systemProgram: anchor.web3.SystemProgram.programId,
})
.instruction();
// Walk instruction: Invoke the Robot to walk
const walkInstruction = await program.methods
.walk()
.accounts({
actionState,
user,
})
.instruction();
// Run instruction: Invoke the Robot to run
const runInstruction = await program.methods
.run()
.accounts({
actionState,
user,
})
.instruction();
// Jump instruction: Invoke the Robot to jump
const jumpInstruction = await program.methods
.jump()
.accounts({
actionState,
user,
})
.instruction();
// Reset instruction: Reset actions of the Robot
const resetInstruction = await program.methods
.reset()
.accounts({
actionState,
user,
})
.instruction();

// Array of instructions
const instructions: TransactionInstruction[] = [
createInstruction,
walkInstruction,
runInstruction,
jumpInstruction,
resetInstruction,
];

await createAndSendV0Tx(instructions);
});

async function createAndSendV0Tx(txInstructions: TransactionInstruction[]) {
// Step 1 - Fetch the latest blockhash
let latestBlockhash = await provider.connection.getLatestBlockhash(
"confirmed"
);
console.log(
" ✅ - Fetched latest blockhash. Last Valid Height:",
latestBlockhash.lastValidBlockHeight
);

// Step 2 - Generate Transaction Message
const messageV0 = new TransactionMessage({
payerKey: user,
recentBlockhash: latestBlockhash.blockhash,
instructions: txInstructions,
}).compileToV0Message();
console.log(" ✅ - Compiled Transaction Message");
const transaction = new VersionedTransaction(messageV0);

// Step 3 - Sign your transaction with the required `Signers`
provider.wallet.signTransaction(transaction);
console.log(" ✅ - Transaction Signed");

// Step 4 - Send our v0 transaction to the cluster
const txid = await provider.connection.sendTransaction(transaction, {
maxRetries: 5,
});
console.log(" ✅ - Transaction sent to network");

// Step 5 - Confirm Transaction
const confirmation = await provider.connection.confirmTransaction({
signature: txid,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});
if (confirmation.value.err) {
throw new Error(
` ❌ - Transaction not confirmed.\nReason: ${confirmation.value.err}`
);
}

console.log("🎉 Transaction Succesfully Confirmed!");
let result = await program.account.actionState.fetch(actionState);
console.log("Robot action state details: ", result);
}
});
10 changes: 10 additions & 0 deletions examples/tutorial/basic-5/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"types": ["mocha"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}
12 changes: 8 additions & 4 deletions examples/tutorial/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
"basic-1",
"basic-2",
"basic-3",
"basic-4"
"basic-4",
"basic-5"
],
"dependencies": {
"@coral-xyz/anchor": "file:../../ts/packages/anchor"
},
"devDependencies": {
"mocha": "9.2.2",
"prettier": "^2.5.1"
"mocha": "^9.2.2",
"prettier": "^2.5.1",
"@types/mocha": "^9.1.1",
"ts-mocha": "^10.0.0",
"typescript": "^4.9.5"
}
}
}
Loading