Skip to content

Instantly share code, notes, and snippets.

@maxims94
Last active December 21, 2023 10:44
Show Gist options
  • Save maxims94/c4d2362330c8fef8ed8bcb7fa6cb2620 to your computer and use it in GitHub Desktop.
Save maxims94/c4d2362330c8fef8ed8bcb7fa6cb2620 to your computer and use it in GitHub Desktop.
Seahorse installation guide

Seahorse installation guide

How to set up a development environment for https://github.com/solana-developers/seahorse from scratch.

Tested on 18th December 2023.

Everything is installed locally (for the current user only). Also, we ensure maximal flexibility by using version managers wherever possible.

This guide assumes that you're installing everything from scratch. If you got stuck at some point, make sure to check out the "WARNING" sections.

Install Node (with nvm)

From https://github.com/nvm-sh/nvm

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

Make sure the startup code is properly injected in .zshrc or similar.

Install the latest version:

$ nvm install node
$ nvm current
v21.4.0

Install yarn

$ corepack enable
$ yarn --version
1.22.21

Install Rust

Use rustup and the official installation script from https://www.rust-lang.org/tools/install

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ rustc --version
1.74.1

Install Solana

From https://docs.solana.com/de/cli/install-solana-cli-tools

Which version should you install? I decided to go with the latest release that's suitable for mainnet beta

$ sh -c "$(curl -sSfL https://release.solana.com/v1.16.23/install)"

Add the directory from the output to your PATH in .zshrc or similar

Confirm the version

$ solana --version
1.16.23

Run solana-keygen new (required for Anchor)

Install Anchor (with avm)

From https://book.anchor-lang.com/getting_started/installation.html

To install the Anchor Version Manager, run this:

$ cargo install --git https://github.com/coral-xyz/anchor avm --locked --force

Install Anchor:

$ avm install latest
$ avm install 0.26.0
$ avm use 0.26.0

WARNING: Seahorse only works with Anchor 0.26.0 as of now.

WARNING: You MUST switch the Anchor version before creating a new Seahorse project. Reason: When you create a new Seahorse project, it will set the current version of Anchor as a dependency in its configuration files.

Confirm that Anchor is installed and has the right version:

$ anchor --version
0.26.0

Install Seahorse

$ cargo install seahorse-dev
$ seahorse -V
0.1.1

Sample seahorse project

Go to ~/projects or similar

$ seahorse init calculator

This will create an Anchor project with a programs_py directory that will store your Seahorse programs.

Copy the calculator.py file of this gist into the programs_py directory. Then, copy the calculator.ts file into the tests directory.

The example is adapted from https://www.seahorse.dev/introduction/calculator-your-first-seahorse-program

Change into the calculator directory.

Now, you can build the project with

$ seahorse build

This will take a while. At the end, your seahorse project is compiled into Anchor code (in the programs directory) and the Anchor code is compiled into bytecode that can be deployed to the chain.

To ensure that a proper Solana program was built, run the test script (calculator.ts) using Anchor:

$ anchor test

Note that anchor test automatically starts a local validator (so we don't have to).

If the tests were successful -- congratulations! You're all set up to develop on Seahorse!

# calculator
# Built with Seahorse v0.1.1
from seahorse.prelude import *
declare_id('Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS')
class Operation(Enum):
ADD = 0
SUB = 1
MUL = 2
DIV = 3
class Calculator(Account):
owner: Pubkey
display: i64
@instruction
def init_calculator(owner: Signer, calculator: Empty[Calculator]):
calculator = calculator.init(
payer = owner,
seeds = ['Calculator', owner]
)
calculator.owner = owner.key()
@instruction
def reset_calculator(owner: Signer, calculator: Calculator):
print(owner.key(), 'is resetting', calculator.key())
# Verify owner
assert owner.key() == calculator.owner, 'This is not your calculator!'
calculator.display = 0
@instruction
def do_operation(owner: Signer, calculator: Calculator, op: Operation, num: i64):
# Verify owner, like before
assert owner.key() == calculator.owner, 'This is not your calculator!'
if op == Operation.ADD:
calculator.display += num
elif op == Operation.SUB:
calculator.display -= num
elif op == Operation.MUL:
calculator.display *= num
elif op == Operation.DIV:
calculator.display //= num
import * as anchor from '@project-serum/anchor'
import { BN, Program, web3 } from '@project-serum/anchor'
const assert = require('assert')
import { Calculator } from '../target/types/calculator'
describe('calculator', () => {
// Run some tests on our calculator program
const provider = anchor.AnchorProvider.env()
anchor.setProvider(provider)
const program = anchor.workspace.Calculator as Program<Calculator>
// Set up some common accounts we'll be using later
const owner = provider.wallet.publicKey
const calculator = web3.PublicKey.findProgramAddressSync(
[Buffer.from('Calculator'), owner.toBuffer()],
program.programId
)[0]
// Try initializing the calculator
it('Inits a calculator', async () => {
await program.methods.initCalculator().accounts({ owner, calculator }).rpc()
})
// Do some operations on the calculator
it('Does some operations', async () => {
const add2 = await program.methods
.doOperation({ add: true }, new BN(2))
.accounts({ owner, calculator })
.instruction()
const mul3 = await program.methods
.doOperation({ mul: true }, new BN(3))
.accounts({ owner, calculator })
.instruction()
const sub1 = await program.methods
.doOperation({ sub: true }, new BN(1))
.accounts({ owner, calculator })
.instruction()
const tx = new web3.Transaction()
tx.add(add2, mul3, sub1)
await provider.sendAndConfirm(tx)
// Get the calculator's on-chain data
const calculatorAccount = await program.account.calculator.fetch(calculator)
assert.ok(calculatorAccount.display.toNumber() === 5)
})
// Make sure our calculator is secure
it('Prevents fraudulent transactions', async () => {
let hackerman = new web3.Keypair()
let shouldFail = await program.methods
.resetCalculator()
.accounts({
owner: hackerman.publicKey,
calculator,
})
.instruction()
let tx = new web3.Transaction()
tx.add(shouldFail)
await provider
.sendAndConfirm(tx, [hackerman])
.then(() => assert.ok(false)) // Error on success, we want a failure
.catch(() => {})
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment