Smart Contract Tutorial (2) — Solana

Jaher
Haderech_DEV
Published in
3 min readApr 28, 2022

Introduction

Fortunately, you don’t have to learn a new programming language to create a smart contract used in Solana. Rust, C, C++ are the languages used to build programs that are deployed on Solana.

This tutorial describes how to run a local testnet and how to deploy & interact with a smart contract on Solana.

Run local cluster

Installation

$ sh -c "$(curl -sSfL <https://release.solana.com/v1.10.8/install>)"
$ export PATH="~/.local/share/solana/install/active_release/bin:$PATH"

Set config url

$ solana config set --url localhostConfig File: /Users/jaher/.config/solana/cli/config.yml
RPC URL: <http://localhost:8899>
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: /Users/jaher/.config/solana/id.json
Commitment: confirmed

Create keypair

$ solana-keygen newGenerating a new keypairFor added security, enter a BIP39 passphraseNOTE! This passphrase improves security of the recovery seed phrase NOT the
keypair file itself, which is stored as insecure plain text
BIP39 Passphrase (empty for none):Wrote new keypair to /Users/jaher/.config/solana/id.json
========================================================================
pubkey: EsYVZzhZJjxXjUKEKKbqwk3yA1DvhbFnQXnvFtC7XHm6
========================================================================
Save this seed phrase and your BIP39 passphrase to recover your new keypair:
traffic toward work roof canyon hockey radar attitude climb ten nut huge
========================================================================

Run local cluster

$ solana-test-validatorLedger location: test-ledger
Log: test-ledger/validator.log
⠤ Initializing...
⠦ Initializing...
Identity: 6yMsNmTNUJfMCgDuzv7svmVdDVRnnApgCTwX9rD5ASad
Genesis Hash: FjEoSmfCJpWrYFh37jeATqmXeLqNRxuH5Fyr9Fa5ZU6y
Version: 1.10.8
Shred Version: 46340
Gossip Address: 127.0.0.1:1024
TPU Address: 127.0.0.1:1027
JSON RPC URL: <http://127.0.0.1:8899>
⠒ 04:59:30 | Processed Slot: 36522 | Confirmed Slot: 36522 | Finalized Slot: 36490 | Full Snapshot Slot: 36400 | Incremental Snapshot Slot: - | Transactions: 36578 | ◎499.817542500

Deploy Contract

High level representation of the Solana development workflow. (From Solana docs)
$ git clone <https://github.com/solana-labs/example-helloworld>
$ cd example-helloworld
  • Compile
$ npm run build:program-rust
  • Deploy
$ solana program deploy dist/program/helloworld.so**Program Id:** ESRMg6AQPCou7SRwLKbHceMa36uMpGpmHK9TUHrRUg2Q
  • Show deployed contract status
$ solana program show --programs --buffer-authority EsYVZzhZJjxXjUKEKKbqwk3yA1DvhbFnQXnvFtC7XHm6Program Id                                   | Slot      | Authority                                    | Balance
ESRMg6AQPCou7SRwLKbHceMa36uMpGpmHK9TUHrRUg2Q | 657 | EsYVZzhZJjxXjUKEKKbqwk3yA1DvhbFnQXnvFtC7XHm6 | 0.67470936 SOL

Interacting with the deployed contract

In this tutorial, a client program is used to interact with the contract.
See the code review below for details.

  • Run script
$ npm run start> helloworld@0.0.1 start
> ts-node src/client/main.ts
Let's say hello to a Solana account...
Connection to cluster established: <http://localhost:8899> { 'feature-set': 1122441720, 'solana-core': '1.10.8' }
Using account EsYVZzhZJjxXjUKEKKbqwk3yA1DvhbFnQXnvFtC7XHm6 containing 499999999.32294047 SOL to pay for fees
Using program ESRMg6AQPCou7SRwLKbHceMa36uMpGpmHK9TUHrRUg2Q
Saying hello to 8MJFNn1AmXaZKkJF81GniHEUst8fgvYycMN616HN1Cd
8MJFNn1AmXaZKkJF81GniHEUst8fgvYycMN616HN1Cd has been greeted 1 time(s)
Success

And you can also see contract logs with solana logs command

$ solana logsStreaming transaction logs. Confirmed commitment
Transaction executed in slot 33117:
Signature: ASv2vEn67dbx7A6pfR3FHEw82kuMYEGRMJbLhQUgNM5B6fVRM6ttvvcddC5mPrU9HX4mMBtDG8KU49AcMTwFXLv
Status: Ok
Log Messages:
Program ESRMg6AQPCou7SRwLKbHceMa36uMpGpmHK9TUHrRUg2Q invoke [1]
Program log: Hello World Rust program entrypoint
Program log: Greeted 1 time(s)!
Program ESRMg6AQPCou7SRwLKbHceMa36uMpGpmHK9TUHrRUg2Q consumed 1061 of 1400000 compute units
Program ESRMg6AQPCou7SRwLKbHceMa36uMpGpmHK9TUHrRUg2Q success

Client code review

Client is written in typescript and consists 3 files:

1. main.ts : perform tasks sequentially.
2. hello_world.ts : Implementation of functional methods.
3. utils.ts : Utility code for get config info from files.

  • Connect RPC
const rpcUrl = await getRpcUrl();
connection = new Connection(rpcUrl, 'confirmed');
const version = await connection.getVersion();
  • Determin payer
    Default keypair path is ‘~/.config/solana/id.json’ which was generated when created new keypair above
export async function getPayer(): Promise<Keypair> {
try {
const config = await getConfig();
if (!config.keypair_path) throw new Error('Missing keypair path');
return await createKeypairFromFile(config.keypair_path);
} catch (err) {
console.warn(
'Failed to create keypair from CLI config file, falling back to new random keypair',
);
return Keypair.generate();
}
}
  • Get program info by using program keypath file
    Program keypair file was generated when compile contract
const programKeypair = await createKeypairFromFile(PROGRAM_KEYPAIR_PATH);
programId = programKeypair.publicKey;
...
const programInfo = await connection.getAccountInfo(programId);
...
const GREETING_SEED = 'hello';
greetedPubkey = await PublicKey.createWithSeed(
payer.publicKey,
GREETING_SEED,
programId,
);
  • Send transaction
export async function sayHello(): Promise<void> {
console.log('Saying hello to', greetedPubkey.toBase58());
const instruction = new TransactionInstruction({
keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}],
programId,
data: Buffer.alloc(0), // All instructions are hellos
});
await sendAndConfirmTransaction(
connection,
new Transaction().add(instruction),
[payer],
);
}

References

https://docs.solana.com/
https://github.com/solana-labs/example-helloworld
https://blog.chain.link/how-to-build-and-deploy-a-solana-smart-contract/

--

--