├── .gitignore ├── README.md ├── anchor └── anchor-escrow-master │ ├── .gitignore │ ├── Anchor.toml │ ├── Cargo.toml │ ├── README.md │ ├── package.json │ ├── programs │ └── escrow │ │ ├── Cargo.toml │ │ ├── Xargo.toml │ │ └── src │ │ └── lib.rs │ ├── tests │ └── escrow.ts │ ├── tsconfig.json │ └── yarn.lock ├── rust ├── day1(FormattedPrint) │ └── FormattedPrint.rs ├── day2(Primitive) │ ├── ArraysAndSlices.rs │ ├── BasicPrimitives.rs │ ├── LiteralsAndOperators.rs │ └── Tuples.rs ├── day3(Types) │ ├── Aliasing.rs │ ├── Casting.rs │ ├── Interference.rs │ └── Literals.rs ├── day3(VariableBinding) │ ├── Basic.rs │ ├── Declare.rs │ ├── Freezing.rs │ ├── Mutability.rs │ └── ScopeAndShadowing.rs ├── day4(Conversion) │ ├── Form.rs │ ├── FromStrings.rs │ ├── Intro.rs │ ├── ToStrings.rs │ └── TryFormAndTryIntro.rs └── tests │ ├── day1-answer.rs │ ├── day1-test.rs │ ├── day2-answer.rs │ └── day2-test.rs └── solana ├── escrow ├── escrow-ui │ ├── README.md │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── src │ │ ├── Alice.vue │ │ ├── App.vue │ │ ├── Bob.vue │ │ ├── global.d.ts │ │ ├── main.ts │ │ ├── router.ts │ │ ├── shims-vue.d.ts │ │ └── util │ │ │ ├── initEscrow.ts │ │ │ ├── layout.ts │ │ │ └── takeTrade.ts │ └── tsconfig.json └── program │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── doc.md │ ├── scripts │ ├── patch.crates-io.sh │ └── update-solana-dependencies.sh │ └── src │ ├── entrypoint.rs │ ├── error.rs │ ├── instruction.rs │ ├── lib.rs │ ├── processor.rs │ └── state.rs └── helloWorld ├── .gitignore ├── README.md ├── cluster-devnet.env ├── cluster-mainnet-beta.env ├── cluster-testnet.env ├── package.json ├── src ├── client │ ├── hello_world.ts │ ├── main.ts │ └── utils.ts └── program-rust │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Xargo.toml │ ├── src │ └── lib.rs │ └── tests │ └── lib.rs └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | */.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solana program - basic 2 | 3 | This is a Rust & Solana tutorial repo. 4 | You can learn solanan smart contract development using Rust by following the below steps. 5 | Before starting tutorial repo, please check their README.md files first. 6 | 7 | 8 | ## Step1: Learn Rust programming. 9 | - You will learn Rust programming in [here](/rust) 10 | - Each day has at least 3 Rust code tutorials. 11 | - You can check your understanding with fixing/completing test.rs file. 12 | 13 | 14 | ## Step2: Hello World Solana Smart Contract Development. 15 | 16 | - You will build solana smart contracts & dApps by following this [tutorial](https://solhack.com/courses/building-solana-smart-contracts-dapps-with-james-bachini/) 17 | - The final source code is [here](/solana/helloWorld) 18 | 19 | 20 | ## Step3: Escrow Solana Smart Contract Development. 21 | 22 | - You will build a bit complex solana smart contract program here by following this [tutorial](https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction/) 23 | - The final source code is [here](/solana/escrow) 24 | - Before leaving this tutorial, plese check if you understand [these](/escrow/program/doc.md) fully. 25 | 26 | 27 | ## Step4: Anchor Escrow Program 28 | - Using Anchor framework, We're building Escrow Programing again here. But we modified [escrow](https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction/) program a bid differently here. You can check details [here](/anchor/anchor-escrow-master/README.md). 29 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .anchor 3 | node_modules 4 | test_ledger 5 | Cargo.lock 6 | package-lock.json 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/Anchor.toml: -------------------------------------------------------------------------------- 1 | [provider] 2 | cluster = "localnet" 3 | wallet = "~/.config/solana/id.json" 4 | 5 | [scripts] 6 | test = "mocha --require ts-node/register tests/*.ts -t 1000000" 7 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "programs/*" 4 | ] 5 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/README.md: -------------------------------------------------------------------------------- 1 | # Anchor Example: Escrow Program 2 | 3 | - See this [doc](https://hackmd.io/@ironaddicteddog/anchor_example_escrow) for more implementation details 4 | 5 | ## Overview 6 | 7 | Since this program is extended from the original [Escrow Program](https://github.com/paul-schaaf/solana-escrow), I assumed you have gone through the [original blog post](https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction/#instruction-rs-part-1-general-code-structure-and-the-beginning-of-the-escrow-program-flow) at least once. 8 | 9 | However, there is one major difference between this exmaple and the original Escrow program: Instead of letting initializer create a token account to be reset to a PDA authority, we create a token account `Vault` that has both a PDA key and a PDA authority. 10 | 11 | ### Initialize 12 | 13 | ![](https://i.imgur.com/VmRKZUy.png) 14 | 15 | `Initializer` can send a transaction to the escrow program to initialize the Vault. In this transaction, two new accounts: `Vault` and `EscrowAccount`, will be created and tokens (Token A) to be exchanged will be transfered from `Initializer` to `Vault`. 16 | 17 | ### Cancel 18 | 19 | ![](https://i.imgur.com/f6ahGXy.png) 20 | 21 | `Initializer` can also send a transaction to the escrow program to cancel the demand of escrow. The tokens will be transfered back to the `Initialzer` and both `Vault` and `EscrowAccount` will be closed in this case. 22 | 23 | ### Exchange 24 | 25 | ![](https://i.imgur.com/MzG26dm.png) 26 | 27 | `Taker` can send a transaction to the escrow to exchange Token B for Token A. First, tokens (Token B) will be transfered from `Taker` to `Initializer`. Afterward, the tokens (Token A) kept in the Vault will be transfered to `Taker`. Finally, both `Vault` and `EscrowAccount` will be closed. 28 | 29 | ## Build, Deploy and Test 30 | 31 | Let's run the test once to see what happens. 32 | 33 | First, install dependencies: 34 | 35 | ``` 36 | $ npm install 37 | 38 | $ npm install -g mocha 39 | ``` 40 | 41 | Make sure you have your local solana validator running if you want to deploy the program locally: 42 | 43 | ``` 44 | $ solana-test-validator 45 | ``` 46 | 47 | > If you are on Apple Sillicon M1 chip, you will have to build Solana from the source. See [this document](https://docs.solana.com/cli/install-solana-cli-tools#build-from-source) for more details 48 | 49 | Next, we will build and deploy the program via Anchor. 50 | 51 | First, let's build the program: 52 | 53 | ``` 54 | $ anchor build 55 | ``` 56 | 57 | Deploy the program: 58 | 59 | ``` 60 | $ anchor deploy 61 | ``` 62 | 63 | Finally, run the test: 64 | 65 | ``` 66 | $ anchor test 67 | ``` 68 | 69 | > Make sure to terminate the `solana-test-validator` before you run the `test` command 70 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@project-serum/anchor": "latest", 4 | "@project-serum/serum": "latest", 5 | "@solana/spl-token": "latest", 6 | "@solana/web3.js": "latest", 7 | "@types/mocha": "^9.0.0", 8 | "@types/node": "^14.14.37", 9 | "bn.js": "^5.2.0", 10 | "camelcase": "^6.2.0", 11 | "chai": "^4.3.4" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/programs/escrow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "Anchor Escrow" 3 | edition = "2018" 4 | name = "escrow" 5 | version = "0.1.0" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "escrow" 10 | 11 | [features] 12 | cpi = ["no-entrypoint"] 13 | default = [] 14 | no-entrypoint = [] 15 | no-idl = [] 16 | 17 | [dependencies] 18 | anchor-lang = {version = "0.18.0"} 19 | anchor-spl = {version = "0.18.0"} 20 | spl-token = {version = "3.1.1", features = ["no-entrypoint"]} 21 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/programs/escrow/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/programs/escrow/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An example of an escrow program, inspired by PaulX tutorial seen here 2 | //! https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction/ 3 | //! This example has some changes to implementation, but more or less should be the same overall 4 | //! Also gives examples on how to use some newer anchor features and CPI 5 | //! 6 | //! User (Initializer) constructs an escrow deal: 7 | //! - SPL token (X) they will offer and amount 8 | //! - SPL token (Y) count they want in return and amount 9 | //! - Program will take ownership of initializer's token X account 10 | //! 11 | //! Once this escrow is initialised, either: 12 | //! 1. User (Taker) can call the exchange function to exchange their Y for X 13 | //! - This will close the escrow account and no longer be usable 14 | //! OR 15 | //! 2. If no one has exchanged, the initializer can close the escrow account 16 | //! - Initializer will get back ownership of their token X account 17 | 18 | use anchor_lang::prelude::*; 19 | use anchor_spl::token::{self, CloseAccount, Mint, SetAuthority, TokenAccount, Transfer}; 20 | use spl_token::instruction::AuthorityType; 21 | 22 | declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); 23 | 24 | #[program] 25 | pub mod escrow { 26 | use super::*; 27 | 28 | const ESCROW_PDA_SEED: &[u8] = b"escrow"; 29 | 30 | pub fn initialize_escrow( 31 | ctx: Context, 32 | _vault_account_bump: u8, 33 | initializer_amount: u64, 34 | taker_amount: u64, 35 | ) -> ProgramResult { 36 | // input accounts are assigned to EscrowAccount fileds one by one 37 | ctx.accounts.escrow_account.initializer_key = *ctx.accounts.initializer.key; 38 | ctx.accounts.escrow_account.initializer_deposit_token_account = 39 | *ctx.accounts 40 | .initializer_deposit_token_account 41 | .to_account_info() 42 | .key; 43 | ctx.accounts.escrow_account.initializer_receive_token_account = 44 | *ctx.accounts 45 | .initializer_receive_token_account 46 | .to_account_info() 47 | .key; 48 | ctx.accounts.escrow_account.initializer_amount = initializer_amount; 49 | ctx.accounts.escrow_account.taker_amount = taker_amount; 50 | 51 | // new PDA (vault_authority) 52 | let (vault_authority, _vault_authority_bump) = 53 | Pubkey::find_program_address(&[ESCROW_PDA_SEED], ctx.program_id); 54 | 55 | // set initializer's authority to the above PDA 56 | token::set_authority( 57 | ctx.accounts.into_set_authority_context(), 58 | AuthorityType::AccountOwner, 59 | Some(vault_authority), 60 | )?; 61 | 62 | // transfer initializer_amount tokens to PDA(vault_account) 63 | token::transfer( 64 | ctx.accounts.into_transfer_to_pda_context(), 65 | ctx.accounts.escrow_account.initializer_amount, 66 | )?; 67 | 68 | Ok(()) 69 | } 70 | 71 | pub fn cancel_escrow(ctx: Context) -> ProgramResult { 72 | // PDA for vault_authority 73 | let (_vault_authority, vault_authority_bump) = 74 | Pubkey::find_program_address(&[ESCROW_PDA_SEED], ctx.program_id); 75 | let authority_seeds = &[&ESCROW_PDA_SEED[..], &[vault_authority_bump]]; 76 | 77 | // transfer x tokens from vault_account to initializer_deposit_token_account 78 | token::transfer( 79 | ctx.accounts // &mut CancelEscrow 80 | .into_transfer_to_initializer_context() // CpiContext 81 | .with_signer(&[&authority_seeds[..]]), 82 | ctx.accounts.escrow_account.initializer_amount, 83 | )?; 84 | 85 | // close PDA(vault_account) 86 | token::close_account( 87 | ctx.accounts // &mut CancelEscrow 88 | .into_close_context() // Cpitext(CloseAccount) 89 | .with_signer(&[&authority_seeds[..]]), // signer_seeds 90 | )?; 91 | 92 | Ok(()) 93 | } 94 | 95 | pub fn exchange(ctx: Context) -> ProgramResult { 96 | let (_vault_authority, vault_authority_bump) = 97 | Pubkey::find_program_address(&[ESCROW_PDA_SEED], ctx.program_id); 98 | let authority_seeds = &[&ESCROW_PDA_SEED[..], &[vault_authority_bump]]; 99 | 100 | // transfer y tokens from taker_deposit_token_account to initializer_deposit_token_account 101 | token::transfer( 102 | ctx.accounts.into_transfer_to_initializer_context(), 103 | ctx.accounts.escrow_account.taker_amount, 104 | )?; 105 | 106 | // transfer x tokens from vault_account to taker_receive_token_account 107 | token::transfer( 108 | ctx.accounts // &mut Exchange 109 | .into_transfer_to_taker_context() // CpiContext 110 | .with_signer(&[&authority_seeds[..]]), // signer_seeds 111 | ctx.accounts.escrow_account.initializer_amount, 112 | )?; 113 | 114 | // close vault_account 115 | token::close_account( 116 | ctx.accounts // &mut Exchange 117 | .into_close_context() // CpiContext 118 | .with_signer(&[&authority_seeds[..]]), 119 | )?; 120 | 121 | Ok(()) 122 | } 123 | } 124 | 125 | /** 126 | * ProgramAccount: Boxed container for a deserialized `account` 127 | * being used to reference account owned by the currently executing Program 128 | * AccountInfo: key, is_signer, is_writable, lamports, data, owner, exectable, rent_epoch 129 | * Account: Account continaer that checks ownership and deserialization 130 | */ 131 | #[derive(Accounts)] 132 | #[instruction(vault_account_bump: u8, initializer_amount: u64)] 133 | pub struct InitializeEscrow<'info> { 134 | #[account(mut, signer)] 135 | pub initializer: AccountInfo<'info>, 136 | pub mint: Account<'info, Mint>, 137 | #[account( 138 | init, 139 | seeds = [b"token-seed".as_ref()], 140 | bump = vault_account_bump, 141 | payer = initializer, 142 | token::mint = mint, 143 | token::authority = initializer, 144 | )] 145 | pub vault_account: Account<'info, TokenAccount>, 146 | #[account( 147 | mut, 148 | constraint = initializer_deposit_token_account.amount >= initializer_amount 149 | )] 150 | pub initializer_deposit_token_account: Account<'info, TokenAccount>, 151 | pub initializer_receive_token_account: Account<'info, TokenAccount>, 152 | #[account(zero)] 153 | pub escrow_account: ProgramAccount<'info, EscrowAccount>, 154 | pub system_program: AccountInfo<'info>, 155 | pub rent: Sysvar<'info, Rent>, 156 | pub token_program: AccountInfo<'info>, 157 | } 158 | 159 | #[derive(Accounts)] 160 | pub struct CancelEscrow<'info> { 161 | #[account(mut, signer)] 162 | pub initializer: AccountInfo<'info>, 163 | #[account(mut)] 164 | pub vault_account: Account<'info, TokenAccount>, 165 | pub vault_authority: AccountInfo<'info>, 166 | #[account(mut)] 167 | pub initializer_deposit_token_account: Account<'info, TokenAccount>, 168 | #[account( 169 | mut, 170 | constraint = escrow_account.initializer_key == *initializer.key, 171 | constraint = escrow_account.initializer_deposit_token_account == *initializer_deposit_token_account.to_account_info().key, 172 | close = initializer 173 | )] 174 | pub escrow_account: ProgramAccount<'info, EscrowAccount>, 175 | pub token_program: AccountInfo<'info>, 176 | } 177 | 178 | // derive in Rust: allows new item to be automatically generated for data structures 179 | #[derive(Accounts)] 180 | pub struct Exchange<'info> { 181 | #[account(signer)] // check if given account signed the Tx 182 | pub taker: AccountInfo<'info>, 183 | #[account(mut)] // mark the account as mutable and persists the state transition 184 | pub taker_deposit_token_account: Account<'info, TokenAccount>, 185 | #[account(mut)] 186 | pub taker_receive_token_account: Account<'info, TokenAccount>, 187 | #[account(mut)] 188 | pub initializer_deposit_token_account: Account<'info, TokenAccount>, 189 | #[account(mut)] 190 | pub initializer_receive_token_account: Account<'info, TokenAccount>, 191 | #[account(mut)] 192 | pub initializer: AccountInfo<'info>, 193 | #[account( 194 | mut, 195 | constraint = escrow_account.taker_amount <= taker_deposit_token_account.amount, 196 | constraint = escrow_account.initializer_deposit_token_account == *initializer_deposit_token_account.to_account_info().key, 197 | constraint = escrow_account.initializer_receive_token_account == *initializer_receive_token_account.to_account_info().key, 198 | constraint = escrow_account.initializer_key == *initializer.key, 199 | close = initializer // mark the account as beingn closed at the end of Ix's execution, sending rent exemption lamports to the initializer 200 | )] 201 | pub escrow_account: ProgramAccount<'info, EscrowAccount>, 202 | #[account(mut)] 203 | pub vault_account: Account<'info, TokenAccount>, 204 | pub vault_authority: AccountInfo<'info>, 205 | pub token_program: AccountInfo<'info>, 206 | } 207 | 208 | #[account] 209 | pub struct EscrowAccount { 210 | pub initializer_key: Pubkey, 211 | pub initializer_deposit_token_account: Pubkey, 212 | pub initializer_receive_token_account: Pubkey, 213 | pub initializer_amount: u64, 214 | pub taker_amount: u64, 215 | } 216 | 217 | impl<'info> InitializeEscrow<'info> { 218 | // transfer x tokens from initializer_deposit_token_account to vault_account 219 | fn into_transfer_to_pda_context(&self) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> { 220 | let cpi_accounts = Transfer { 221 | from: self 222 | .initializer_deposit_token_account 223 | .to_account_info() 224 | .clone(), 225 | to: self.vault_account.to_account_info().clone(), 226 | authority: self.initializer.clone(), 227 | }; 228 | CpiContext::new(self.token_program.clone(), cpi_accounts) 229 | } 230 | 231 | // set initializer's authority to vault_account 232 | fn into_set_authority_context(&self) -> CpiContext<'_, '_, '_, 'info, SetAuthority<'info>> { 233 | let cpi_accounts = SetAuthority { 234 | account_or_mint: self.vault_account.to_account_info().clone(), 235 | current_authority: self.initializer.clone(), 236 | }; 237 | let cpi_program = self.token_program.to_account_info(); 238 | CpiContext::new(cpi_program, cpi_accounts) 239 | } 240 | } 241 | 242 | impl<'info> CancelEscrow<'info> { 243 | // generate CPI context for Transfer 244 | fn into_transfer_to_initializer_context( 245 | &self, 246 | ) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> { 247 | let cpi_accounts = Transfer { 248 | from: self.vault_account.to_account_info().clone(), 249 | to: self 250 | .initializer_deposit_token_account 251 | .to_account_info() 252 | .clone(), 253 | authority: self.vault_authority.clone(), 254 | }; 255 | let cpi_program = self.token_program.to_account_info(); 256 | CpiContext::new(cpi_program, cpi_accounts) 257 | } 258 | 259 | fn into_close_context(&self) -> CpiContext<'_, '_, '_, 'info, CloseAccount<'info>> { 260 | let cpi_accounts = CloseAccount { 261 | account: self.vault_account.to_account_info().clone(), 262 | destination: self.initializer.clone(), 263 | authority: self.vault_authority.clone(), 264 | }; 265 | let cpi_program = self.token_program.to_account_info(); 266 | CpiContext::new(cpi_program, cpi_accounts) 267 | } 268 | } 269 | 270 | impl<'info> Exchange<'info> { 271 | fn into_transfer_to_initializer_context( 272 | &self, 273 | ) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> { 274 | let cpi_accounts = Transfer { 275 | from: self.taker_deposit_token_account.to_account_info().clone(), 276 | to: self 277 | .initializer_receive_token_account 278 | .to_account_info() 279 | .clone(), 280 | authority: self.taker.clone(), 281 | }; 282 | let cpi_program = self.token_program.to_account_info(); 283 | CpiContext::new(cpi_program, cpi_accounts) 284 | } 285 | 286 | fn into_transfer_to_taker_context(&self) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> { 287 | let cpi_accounts = Transfer { 288 | from: self.vault_account.to_account_info().clone(), 289 | to: self.taker_receive_token_account.to_account_info().clone(), 290 | authority: self.vault_authority.clone(), 291 | }; 292 | CpiContext::new(self.token_program.clone(), cpi_accounts) 293 | } 294 | 295 | fn into_close_context(&self) -> CpiContext<'_, '_, '_, 'info, CloseAccount<'info>> { 296 | let cpi_accounts = CloseAccount { 297 | account: self.vault_account.to_account_info().clone(), 298 | destination: self.initializer.clone(), 299 | authority: self.vault_authority.clone(), 300 | }; 301 | CpiContext::new(self.token_program.clone(), cpi_accounts) 302 | } 303 | } -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/tests/escrow.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | import { 3 | PublicKey, 4 | SystemProgram, 5 | Transaction, 6 | } from '@solana/web3.js'; 7 | import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token"; 8 | import { assert } from "chai"; 9 | 10 | describe("escrow", () => { 11 | const provider = anchor.Provider.env(); 12 | anchor.setProvider(provider); 13 | 14 | const program = anchor.workspace.Escrow; 15 | 16 | let mintA = null; 17 | let mintB = null; 18 | let initializerTokenAccountA = null; 19 | let initializerTokenAccountB = null; 20 | let takerTokenAccountA = null; 21 | let takerTokenAccountB = null; 22 | let vault_account_pda = null; 23 | let vault_account_bump = null; 24 | let vault_authority_pda = null; 25 | 26 | const takerAmount = 1000; 27 | const initializerAmount = 500; 28 | 29 | const escrowAccount = anchor.web3.Keypair.generate(); 30 | const payer = anchor.web3.Keypair.generate(); 31 | const mintAuthority = anchor.web3.Keypair.generate(); 32 | const initializerMainAccount = anchor.web3.Keypair.generate(); 33 | const takerMainAccount = anchor.web3.Keypair.generate(); 34 | 35 | it("Initialise escrow state", async () => { 36 | // Airdropping tokens to a payer. 37 | await provider.connection.confirmTransaction( 38 | await provider.connection.requestAirdrop(payer.publicKey, 10000000000), 39 | "confirmed" 40 | ); 41 | 42 | // Fund Main Accounts 43 | await provider.send( 44 | (() => { 45 | const tx = new Transaction(); 46 | tx.add( 47 | SystemProgram.transfer({ 48 | fromPubkey: payer.publicKey, 49 | toPubkey: initializerMainAccount.publicKey, 50 | lamports: 1000000000, 51 | }), 52 | SystemProgram.transfer({ 53 | fromPubkey: payer.publicKey, 54 | toPubkey: takerMainAccount.publicKey, 55 | lamports: 1000000000, 56 | }) 57 | ); 58 | return tx; 59 | })(), 60 | [payer] 61 | ); 62 | 63 | mintA = await Token.createMint( 64 | provider.connection, 65 | payer, 66 | mintAuthority.publicKey, 67 | null, 68 | 0, 69 | TOKEN_PROGRAM_ID 70 | ); 71 | 72 | mintB = await Token.createMint( 73 | provider.connection, 74 | payer, 75 | mintAuthority.publicKey, 76 | null, 77 | 0, 78 | TOKEN_PROGRAM_ID 79 | ); 80 | 81 | initializerTokenAccountA = await mintA.createAccount(initializerMainAccount.publicKey); 82 | takerTokenAccountA = await mintA.createAccount(takerMainAccount.publicKey); 83 | 84 | initializerTokenAccountB = await mintB.createAccount(initializerMainAccount.publicKey); 85 | takerTokenAccountB = await mintB.createAccount(takerMainAccount.publicKey); 86 | 87 | await mintA.mintTo( 88 | initializerTokenAccountA, 89 | mintAuthority.publicKey, 90 | [mintAuthority], 91 | initializerAmount 92 | ); 93 | 94 | await mintB.mintTo( 95 | takerTokenAccountB, 96 | mintAuthority.publicKey, 97 | [mintAuthority], 98 | takerAmount 99 | ); 100 | 101 | let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA); 102 | let _takerTokenAccountB = await mintB.getAccountInfo(takerTokenAccountB); 103 | 104 | assert.ok(_initializerTokenAccountA.amount.toNumber() == initializerAmount); 105 | assert.ok(_takerTokenAccountB.amount.toNumber() == takerAmount); 106 | }); 107 | 108 | it("Initialize escrow", async () => { 109 | const [_vault_account_pda, _vault_account_bump] = await PublicKey.findProgramAddress( 110 | [Buffer.from(anchor.utils.bytes.utf8.encode("token-seed"))], 111 | program.programId 112 | ); 113 | vault_account_pda = _vault_account_pda; 114 | vault_account_bump = _vault_account_bump; 115 | 116 | const [_vault_authority_pda, _vault_authority_bump] = await PublicKey.findProgramAddress( 117 | [Buffer.from(anchor.utils.bytes.utf8.encode("escrow"))], 118 | program.programId 119 | ); 120 | vault_authority_pda = _vault_authority_pda; 121 | 122 | await program.rpc.initializeEscrow( 123 | vault_account_bump, 124 | new anchor.BN(initializerAmount), 125 | new anchor.BN(takerAmount), 126 | { 127 | accounts: { 128 | initializer: initializerMainAccount.publicKey, 129 | vaultAccount: vault_account_pda, 130 | mint: mintA.publicKey, 131 | initializerDepositTokenAccount: initializerTokenAccountA, 132 | initializerReceiveTokenAccount: initializerTokenAccountB, 133 | escrowAccount: escrowAccount.publicKey, 134 | systemProgram: anchor.web3.SystemProgram.programId, 135 | rent: anchor.web3.SYSVAR_RENT_PUBKEY, 136 | tokenProgram: TOKEN_PROGRAM_ID, 137 | }, 138 | instructions: [ 139 | await program.account.escrowAccount.createInstruction(escrowAccount), 140 | ], 141 | signers: [escrowAccount, initializerMainAccount], 142 | } 143 | ); 144 | 145 | let _vault = await mintA.getAccountInfo(vault_account_pda); 146 | 147 | let _escrowAccount = await program.account.escrowAccount.fetch( 148 | escrowAccount.publicKey 149 | ); 150 | 151 | // Check that the new owner is the PDA. 152 | assert.ok(_vault.owner.equals(vault_authority_pda)); 153 | 154 | // Check that the values in the escrow account match what we expect. 155 | assert.ok(_escrowAccount.initializerKey.equals(initializerMainAccount.publicKey)); 156 | assert.ok(_escrowAccount.initializerAmount.toNumber() == initializerAmount); 157 | assert.ok(_escrowAccount.takerAmount.toNumber() == takerAmount); 158 | assert.ok( 159 | _escrowAccount.initializerDepositTokenAccount.equals(initializerTokenAccountA) 160 | ); 161 | assert.ok( 162 | _escrowAccount.initializerReceiveTokenAccount.equals(initializerTokenAccountB) 163 | ); 164 | }); 165 | 166 | it("Exchange escrow", async () => { 167 | await program.rpc.exchange({ 168 | accounts: { 169 | taker: takerMainAccount.publicKey, 170 | takerDepositTokenAccount: takerTokenAccountB, 171 | takerReceiveTokenAccount: takerTokenAccountA, 172 | initializerDepositTokenAccount: initializerTokenAccountA, 173 | initializerReceiveTokenAccount: initializerTokenAccountB, 174 | initializer: initializerMainAccount.publicKey, 175 | escrowAccount: escrowAccount.publicKey, 176 | vaultAccount: vault_account_pda, 177 | vaultAuthority: vault_authority_pda, 178 | tokenProgram: TOKEN_PROGRAM_ID, 179 | }, 180 | signers: [takerMainAccount] 181 | }); 182 | 183 | let _takerTokenAccountA = await mintA.getAccountInfo(takerTokenAccountA); 184 | let _takerTokenAccountB = await mintB.getAccountInfo(takerTokenAccountB); 185 | let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA); 186 | let _initializerTokenAccountB = await mintB.getAccountInfo(initializerTokenAccountB); 187 | 188 | // TODO: Assert if the PDA token account is closed 189 | 190 | assert.ok(_takerTokenAccountA.amount.toNumber() == initializerAmount); 191 | assert.ok(_initializerTokenAccountA.amount.toNumber() == 0); 192 | assert.ok(_initializerTokenAccountB.amount.toNumber() == takerAmount); 193 | assert.ok(_takerTokenAccountB.amount.toNumber() == 0); 194 | }); 195 | 196 | it("Initialize escrow and cancel escrow", async () => { 197 | // Put back tokens into initializer token A account. 198 | await mintA.mintTo( 199 | initializerTokenAccountA, 200 | mintAuthority.publicKey, 201 | [mintAuthority], 202 | initializerAmount 203 | ); 204 | 205 | await program.rpc.initializeEscrow( 206 | vault_account_bump, 207 | new anchor.BN(initializerAmount), 208 | new anchor.BN(takerAmount), 209 | { 210 | accounts: { 211 | initializer: initializerMainAccount.publicKey, 212 | vaultAccount: vault_account_pda, 213 | mint: mintA.publicKey, 214 | initializerDepositTokenAccount: initializerTokenAccountA, 215 | initializerReceiveTokenAccount: initializerTokenAccountB, 216 | escrowAccount: escrowAccount.publicKey, 217 | systemProgram: anchor.web3.SystemProgram.programId, 218 | rent: anchor.web3.SYSVAR_RENT_PUBKEY, 219 | tokenProgram: TOKEN_PROGRAM_ID, 220 | }, 221 | instructions: [ 222 | await program.account.escrowAccount.createInstruction(escrowAccount), 223 | ], 224 | signers: [escrowAccount, initializerMainAccount], 225 | } 226 | ); 227 | 228 | // Cancel the escrow. 229 | await program.rpc.cancelEscrow({ 230 | accounts: { 231 | initializer: initializerMainAccount.publicKey, 232 | initializerDepositTokenAccount: initializerTokenAccountA, 233 | vaultAccount: vault_account_pda, 234 | vaultAuthority: vault_authority_pda, 235 | escrowAccount: escrowAccount.publicKey, 236 | tokenProgram: TOKEN_PROGRAM_ID, 237 | }, 238 | signers: [initializerMainAccount] 239 | }); 240 | 241 | // TODO: Assert if the PDA token account is closed 242 | 243 | // Check the final owner should be the provider public key. 244 | const _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA); 245 | assert.ok(_initializerTokenAccountA.owner.equals(initializerMainAccount.publicKey)); 246 | 247 | // Check all the funds are still there. 248 | assert.ok(_initializerTokenAccountA.amount.toNumber() == initializerAmount); 249 | }); 250 | }); 251 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["mocha", "chai", "node"], 4 | "typeRoots": ["./node_modules/@types"], 5 | "lib": ["es2015"], 6 | "module": "commonjs", 7 | "target": "es6", 8 | "esModuleInterop": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /anchor/anchor-escrow-master/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5": 6 | "integrity" "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==" 7 | "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz" 8 | "version" "7.15.4" 9 | dependencies: 10 | "regenerator-runtime" "^0.13.4" 11 | 12 | "@ethersproject/abi@^5.5.0", "@ethersproject/abi@5.5.0": 13 | "integrity" "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==" 14 | "resolved" "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz" 15 | "version" "5.5.0" 16 | dependencies: 17 | "@ethersproject/address" "^5.5.0" 18 | "@ethersproject/bignumber" "^5.5.0" 19 | "@ethersproject/bytes" "^5.5.0" 20 | "@ethersproject/constants" "^5.5.0" 21 | "@ethersproject/hash" "^5.5.0" 22 | "@ethersproject/keccak256" "^5.5.0" 23 | "@ethersproject/logger" "^5.5.0" 24 | "@ethersproject/properties" "^5.5.0" 25 | "@ethersproject/strings" "^5.5.0" 26 | 27 | "@ethersproject/abstract-provider@^5.5.0", "@ethersproject/abstract-provider@5.5.1": 28 | "integrity" "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==" 29 | "resolved" "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz" 30 | "version" "5.5.1" 31 | dependencies: 32 | "@ethersproject/bignumber" "^5.5.0" 33 | "@ethersproject/bytes" "^5.5.0" 34 | "@ethersproject/logger" "^5.5.0" 35 | "@ethersproject/networks" "^5.5.0" 36 | "@ethersproject/properties" "^5.5.0" 37 | "@ethersproject/transactions" "^5.5.0" 38 | "@ethersproject/web" "^5.5.0" 39 | 40 | "@ethersproject/abstract-signer@^5.5.0", "@ethersproject/abstract-signer@5.5.0": 41 | "integrity" "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==" 42 | "resolved" "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz" 43 | "version" "5.5.0" 44 | dependencies: 45 | "@ethersproject/abstract-provider" "^5.5.0" 46 | "@ethersproject/bignumber" "^5.5.0" 47 | "@ethersproject/bytes" "^5.5.0" 48 | "@ethersproject/logger" "^5.5.0" 49 | "@ethersproject/properties" "^5.5.0" 50 | 51 | "@ethersproject/address@^5.5.0", "@ethersproject/address@5.5.0": 52 | "integrity" "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==" 53 | "resolved" "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz" 54 | "version" "5.5.0" 55 | dependencies: 56 | "@ethersproject/bignumber" "^5.5.0" 57 | "@ethersproject/bytes" "^5.5.0" 58 | "@ethersproject/keccak256" "^5.5.0" 59 | "@ethersproject/logger" "^5.5.0" 60 | "@ethersproject/rlp" "^5.5.0" 61 | 62 | "@ethersproject/base64@^5.5.0", "@ethersproject/base64@5.5.0": 63 | "integrity" "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==" 64 | "resolved" "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz" 65 | "version" "5.5.0" 66 | dependencies: 67 | "@ethersproject/bytes" "^5.5.0" 68 | 69 | "@ethersproject/basex@^5.5.0", "@ethersproject/basex@5.5.0": 70 | "integrity" "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==" 71 | "resolved" "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz" 72 | "version" "5.5.0" 73 | dependencies: 74 | "@ethersproject/bytes" "^5.5.0" 75 | "@ethersproject/properties" "^5.5.0" 76 | 77 | "@ethersproject/bignumber@^5.5.0", "@ethersproject/bignumber@5.5.0": 78 | "integrity" "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==" 79 | "resolved" "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz" 80 | "version" "5.5.0" 81 | dependencies: 82 | "@ethersproject/bytes" "^5.5.0" 83 | "@ethersproject/logger" "^5.5.0" 84 | "bn.js" "^4.11.9" 85 | 86 | "@ethersproject/bytes@^5.5.0", "@ethersproject/bytes@5.5.0": 87 | "integrity" "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==" 88 | "resolved" "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz" 89 | "version" "5.5.0" 90 | dependencies: 91 | "@ethersproject/logger" "^5.5.0" 92 | 93 | "@ethersproject/constants@^5.5.0", "@ethersproject/constants@5.5.0": 94 | "integrity" "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==" 95 | "resolved" "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz" 96 | "version" "5.5.0" 97 | dependencies: 98 | "@ethersproject/bignumber" "^5.5.0" 99 | 100 | "@ethersproject/contracts@5.5.0": 101 | "integrity" "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==" 102 | "resolved" "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz" 103 | "version" "5.5.0" 104 | dependencies: 105 | "@ethersproject/abi" "^5.5.0" 106 | "@ethersproject/abstract-provider" "^5.5.0" 107 | "@ethersproject/abstract-signer" "^5.5.0" 108 | "@ethersproject/address" "^5.5.0" 109 | "@ethersproject/bignumber" "^5.5.0" 110 | "@ethersproject/bytes" "^5.5.0" 111 | "@ethersproject/constants" "^5.5.0" 112 | "@ethersproject/logger" "^5.5.0" 113 | "@ethersproject/properties" "^5.5.0" 114 | "@ethersproject/transactions" "^5.5.0" 115 | 116 | "@ethersproject/hash@^5.5.0", "@ethersproject/hash@5.5.0": 117 | "integrity" "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==" 118 | "resolved" "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz" 119 | "version" "5.5.0" 120 | dependencies: 121 | "@ethersproject/abstract-signer" "^5.5.0" 122 | "@ethersproject/address" "^5.5.0" 123 | "@ethersproject/bignumber" "^5.5.0" 124 | "@ethersproject/bytes" "^5.5.0" 125 | "@ethersproject/keccak256" "^5.5.0" 126 | "@ethersproject/logger" "^5.5.0" 127 | "@ethersproject/properties" "^5.5.0" 128 | "@ethersproject/strings" "^5.5.0" 129 | 130 | "@ethersproject/hdnode@^5.5.0", "@ethersproject/hdnode@5.5.0": 131 | "integrity" "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==" 132 | "resolved" "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz" 133 | "version" "5.5.0" 134 | dependencies: 135 | "@ethersproject/abstract-signer" "^5.5.0" 136 | "@ethersproject/basex" "^5.5.0" 137 | "@ethersproject/bignumber" "^5.5.0" 138 | "@ethersproject/bytes" "^5.5.0" 139 | "@ethersproject/logger" "^5.5.0" 140 | "@ethersproject/pbkdf2" "^5.5.0" 141 | "@ethersproject/properties" "^5.5.0" 142 | "@ethersproject/sha2" "^5.5.0" 143 | "@ethersproject/signing-key" "^5.5.0" 144 | "@ethersproject/strings" "^5.5.0" 145 | "@ethersproject/transactions" "^5.5.0" 146 | "@ethersproject/wordlists" "^5.5.0" 147 | 148 | "@ethersproject/json-wallets@^5.5.0", "@ethersproject/json-wallets@5.5.0": 149 | "integrity" "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==" 150 | "resolved" "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz" 151 | "version" "5.5.0" 152 | dependencies: 153 | "@ethersproject/abstract-signer" "^5.5.0" 154 | "@ethersproject/address" "^5.5.0" 155 | "@ethersproject/bytes" "^5.5.0" 156 | "@ethersproject/hdnode" "^5.5.0" 157 | "@ethersproject/keccak256" "^5.5.0" 158 | "@ethersproject/logger" "^5.5.0" 159 | "@ethersproject/pbkdf2" "^5.5.0" 160 | "@ethersproject/properties" "^5.5.0" 161 | "@ethersproject/random" "^5.5.0" 162 | "@ethersproject/strings" "^5.5.0" 163 | "@ethersproject/transactions" "^5.5.0" 164 | "aes-js" "3.0.0" 165 | "scrypt-js" "3.0.1" 166 | 167 | "@ethersproject/keccak256@^5.5.0", "@ethersproject/keccak256@5.5.0": 168 | "integrity" "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==" 169 | "resolved" "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz" 170 | "version" "5.5.0" 171 | dependencies: 172 | "@ethersproject/bytes" "^5.5.0" 173 | "js-sha3" "0.8.0" 174 | 175 | "@ethersproject/logger@^5.5.0", "@ethersproject/logger@5.5.0": 176 | "integrity" "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" 177 | "resolved" "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz" 178 | "version" "5.5.0" 179 | 180 | "@ethersproject/networks@^5.5.0", "@ethersproject/networks@5.5.0": 181 | "integrity" "sha512-KWfP3xOnJeF89Uf/FCJdV1a2aDJe5XTN2N52p4fcQ34QhDqQFkgQKZ39VGtiqUgHcLI8DfT0l9azC3KFTunqtA==" 182 | "resolved" "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.0.tgz" 183 | "version" "5.5.0" 184 | dependencies: 185 | "@ethersproject/logger" "^5.5.0" 186 | 187 | "@ethersproject/pbkdf2@^5.5.0", "@ethersproject/pbkdf2@5.5.0": 188 | "integrity" "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==" 189 | "resolved" "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz" 190 | "version" "5.5.0" 191 | dependencies: 192 | "@ethersproject/bytes" "^5.5.0" 193 | "@ethersproject/sha2" "^5.5.0" 194 | 195 | "@ethersproject/properties@^5.5.0", "@ethersproject/properties@5.5.0": 196 | "integrity" "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==" 197 | "resolved" "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz" 198 | "version" "5.5.0" 199 | dependencies: 200 | "@ethersproject/logger" "^5.5.0" 201 | 202 | "@ethersproject/providers@5.5.0": 203 | "integrity" "sha512-xqMbDnS/FPy+J/9mBLKddzyLLAQFjrVff5g00efqxPzcAwXiR+SiCGVy6eJ5iAIirBOATjx7QLhDNPGV+AEQsw==" 204 | "resolved" "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.0.tgz" 205 | "version" "5.5.0" 206 | dependencies: 207 | "@ethersproject/abstract-provider" "^5.5.0" 208 | "@ethersproject/abstract-signer" "^5.5.0" 209 | "@ethersproject/address" "^5.5.0" 210 | "@ethersproject/basex" "^5.5.0" 211 | "@ethersproject/bignumber" "^5.5.0" 212 | "@ethersproject/bytes" "^5.5.0" 213 | "@ethersproject/constants" "^5.5.0" 214 | "@ethersproject/hash" "^5.5.0" 215 | "@ethersproject/logger" "^5.5.0" 216 | "@ethersproject/networks" "^5.5.0" 217 | "@ethersproject/properties" "^5.5.0" 218 | "@ethersproject/random" "^5.5.0" 219 | "@ethersproject/rlp" "^5.5.0" 220 | "@ethersproject/sha2" "^5.5.0" 221 | "@ethersproject/strings" "^5.5.0" 222 | "@ethersproject/transactions" "^5.5.0" 223 | "@ethersproject/web" "^5.5.0" 224 | "bech32" "1.1.4" 225 | "ws" "7.4.6" 226 | 227 | "@ethersproject/random@^5.5.0", "@ethersproject/random@5.5.0": 228 | "integrity" "sha512-egGYZwZ/YIFKMHcoBUo8t3a8Hb/TKYX8BCBoLjudVCZh892welR3jOxgOmb48xznc9bTcMm7Tpwc1gHC1PFNFQ==" 229 | "resolved" "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.0.tgz" 230 | "version" "5.5.0" 231 | dependencies: 232 | "@ethersproject/bytes" "^5.5.0" 233 | "@ethersproject/logger" "^5.5.0" 234 | 235 | "@ethersproject/rlp@^5.5.0", "@ethersproject/rlp@5.5.0": 236 | "integrity" "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==" 237 | "resolved" "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz" 238 | "version" "5.5.0" 239 | dependencies: 240 | "@ethersproject/bytes" "^5.5.0" 241 | "@ethersproject/logger" "^5.5.0" 242 | 243 | "@ethersproject/sha2@^5.5.0", "@ethersproject/sha2@5.5.0": 244 | "integrity" "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==" 245 | "resolved" "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz" 246 | "version" "5.5.0" 247 | dependencies: 248 | "@ethersproject/bytes" "^5.5.0" 249 | "@ethersproject/logger" "^5.5.0" 250 | "hash.js" "1.1.7" 251 | 252 | "@ethersproject/signing-key@^5.5.0", "@ethersproject/signing-key@5.5.0": 253 | "integrity" "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==" 254 | "resolved" "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz" 255 | "version" "5.5.0" 256 | dependencies: 257 | "@ethersproject/bytes" "^5.5.0" 258 | "@ethersproject/logger" "^5.5.0" 259 | "@ethersproject/properties" "^5.5.0" 260 | "bn.js" "^4.11.9" 261 | "elliptic" "6.5.4" 262 | "hash.js" "1.1.7" 263 | 264 | "@ethersproject/solidity@5.5.0": 265 | "integrity" "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==" 266 | "resolved" "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz" 267 | "version" "5.5.0" 268 | dependencies: 269 | "@ethersproject/bignumber" "^5.5.0" 270 | "@ethersproject/bytes" "^5.5.0" 271 | "@ethersproject/keccak256" "^5.5.0" 272 | "@ethersproject/logger" "^5.5.0" 273 | "@ethersproject/sha2" "^5.5.0" 274 | "@ethersproject/strings" "^5.5.0" 275 | 276 | "@ethersproject/strings@^5.5.0", "@ethersproject/strings@5.5.0": 277 | "integrity" "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==" 278 | "resolved" "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz" 279 | "version" "5.5.0" 280 | dependencies: 281 | "@ethersproject/bytes" "^5.5.0" 282 | "@ethersproject/constants" "^5.5.0" 283 | "@ethersproject/logger" "^5.5.0" 284 | 285 | "@ethersproject/transactions@^5.5.0", "@ethersproject/transactions@5.5.0": 286 | "integrity" "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==" 287 | "resolved" "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz" 288 | "version" "5.5.0" 289 | dependencies: 290 | "@ethersproject/address" "^5.5.0" 291 | "@ethersproject/bignumber" "^5.5.0" 292 | "@ethersproject/bytes" "^5.5.0" 293 | "@ethersproject/constants" "^5.5.0" 294 | "@ethersproject/keccak256" "^5.5.0" 295 | "@ethersproject/logger" "^5.5.0" 296 | "@ethersproject/properties" "^5.5.0" 297 | "@ethersproject/rlp" "^5.5.0" 298 | "@ethersproject/signing-key" "^5.5.0" 299 | 300 | "@ethersproject/units@5.5.0": 301 | "integrity" "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==" 302 | "resolved" "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz" 303 | "version" "5.5.0" 304 | dependencies: 305 | "@ethersproject/bignumber" "^5.5.0" 306 | "@ethersproject/constants" "^5.5.0" 307 | "@ethersproject/logger" "^5.5.0" 308 | 309 | "@ethersproject/wallet@5.5.0": 310 | "integrity" "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==" 311 | "resolved" "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz" 312 | "version" "5.5.0" 313 | dependencies: 314 | "@ethersproject/abstract-provider" "^5.5.0" 315 | "@ethersproject/abstract-signer" "^5.5.0" 316 | "@ethersproject/address" "^5.5.0" 317 | "@ethersproject/bignumber" "^5.5.0" 318 | "@ethersproject/bytes" "^5.5.0" 319 | "@ethersproject/hash" "^5.5.0" 320 | "@ethersproject/hdnode" "^5.5.0" 321 | "@ethersproject/json-wallets" "^5.5.0" 322 | "@ethersproject/keccak256" "^5.5.0" 323 | "@ethersproject/logger" "^5.5.0" 324 | "@ethersproject/properties" "^5.5.0" 325 | "@ethersproject/random" "^5.5.0" 326 | "@ethersproject/signing-key" "^5.5.0" 327 | "@ethersproject/transactions" "^5.5.0" 328 | "@ethersproject/wordlists" "^5.5.0" 329 | 330 | "@ethersproject/web@^5.5.0", "@ethersproject/web@5.5.0": 331 | "integrity" "sha512-BEgY0eL5oH4mAo37TNYVrFeHsIXLRxggCRG/ksRIxI2X5uj5IsjGmcNiRN/VirQOlBxcUhCgHhaDLG4m6XAVoA==" 332 | "resolved" "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.0.tgz" 333 | "version" "5.5.0" 334 | dependencies: 335 | "@ethersproject/base64" "^5.5.0" 336 | "@ethersproject/bytes" "^5.5.0" 337 | "@ethersproject/logger" "^5.5.0" 338 | "@ethersproject/properties" "^5.5.0" 339 | "@ethersproject/strings" "^5.5.0" 340 | 341 | "@ethersproject/wordlists@^5.5.0", "@ethersproject/wordlists@5.5.0": 342 | "integrity" "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==" 343 | "resolved" "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz" 344 | "version" "5.5.0" 345 | dependencies: 346 | "@ethersproject/bytes" "^5.5.0" 347 | "@ethersproject/hash" "^5.5.0" 348 | "@ethersproject/logger" "^5.5.0" 349 | "@ethersproject/properties" "^5.5.0" 350 | "@ethersproject/strings" "^5.5.0" 351 | 352 | "@project-serum/anchor@^0.11.1": 353 | "integrity" "sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA==" 354 | "resolved" "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.11.1.tgz" 355 | "version" "0.11.1" 356 | dependencies: 357 | "@project-serum/borsh" "^0.2.2" 358 | "@solana/web3.js" "^1.17.0" 359 | "base64-js" "^1.5.1" 360 | "bn.js" "^5.1.2" 361 | "bs58" "^4.0.1" 362 | "buffer-layout" "^1.2.0" 363 | "camelcase" "^5.3.1" 364 | "crypto-hash" "^1.3.0" 365 | "eventemitter3" "^4.0.7" 366 | "find" "^0.3.0" 367 | "js-sha256" "^0.9.0" 368 | "pako" "^2.0.3" 369 | "snake-case" "^3.0.4" 370 | "toml" "^3.0.0" 371 | 372 | "@project-serum/anchor@latest": 373 | "integrity" "sha512-uUqojV+oTmzjf/OelVj7anD71/wWnkdKAaNpz4Q57T06XUaFPnNbOvKh7c991/WDVGXrdDCQcx6nRQEP1tQn2Q==" 374 | "resolved" "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.17.1-beta.2.tgz" 375 | "version" "0.17.1-beta.2" 376 | dependencies: 377 | "@project-serum/borsh" "^0.2.2" 378 | "@solana/web3.js" "^1.17.0" 379 | "base64-js" "^1.5.1" 380 | "bn.js" "^5.1.2" 381 | "bs58" "^4.0.1" 382 | "buffer-layout" "^1.2.0" 383 | "camelcase" "^5.3.1" 384 | "crypto-hash" "^1.3.0" 385 | "eventemitter3" "^4.0.7" 386 | "find" "^0.3.0" 387 | "js-sha256" "^0.9.0" 388 | "pako" "^2.0.3" 389 | "snake-case" "^3.0.4" 390 | "toml" "^3.0.0" 391 | 392 | "@project-serum/borsh@^0.2.2": 393 | "integrity" "sha512-Ms+aWmGVW6bWd3b0+MWwoaYig2QD0F90h0uhr7AzY3dpCb5e2S6RsRW02vFTfa085pY2VLB7nTZNbFECQ1liTg==" 394 | "resolved" "https://registry.npmjs.org/@project-serum/borsh/-/borsh-0.2.2.tgz" 395 | "version" "0.2.2" 396 | dependencies: 397 | "bn.js" "^5.1.2" 398 | "buffer-layout" "^1.2.0" 399 | 400 | "@project-serum/serum@latest": 401 | "integrity" "sha512-fGsp9F0ZAS48YQ2HNy+6CNoifJESFXxVsOLPd9QK1XNV8CTuQoECOnVXxV6s5cKGre8pLNq5hrhi5J6aCGauEQ==" 402 | "resolved" "https://registry.npmjs.org/@project-serum/serum/-/serum-0.13.60.tgz" 403 | "version" "0.13.60" 404 | dependencies: 405 | "@project-serum/anchor" "^0.11.1" 406 | "@solana/spl-token" "^0.1.6" 407 | "@solana/web3.js" "^1.21.0" 408 | "bn.js" "^5.1.2" 409 | "buffer-layout" "^1.2.0" 410 | 411 | "@solana/buffer-layout@^3.0.0": 412 | "integrity" "sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w==" 413 | "resolved" "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz" 414 | "version" "3.0.0" 415 | dependencies: 416 | "buffer" "~6.0.3" 417 | 418 | "@solana/spl-token@^0.1.6", "@solana/spl-token@latest": 419 | "integrity" "sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ==" 420 | "resolved" "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.1.8.tgz" 421 | "version" "0.1.8" 422 | dependencies: 423 | "@babel/runtime" "^7.10.5" 424 | "@solana/web3.js" "^1.21.0" 425 | "bn.js" "^5.1.0" 426 | "buffer" "6.0.3" 427 | "buffer-layout" "^1.2.0" 428 | "dotenv" "10.0.0" 429 | 430 | "@solana/web3.js@^1.17.0", "@solana/web3.js@^1.2.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@latest": 431 | "integrity" "sha512-iiL4mprpYkiPnl512SanjKOSvbvJS9LRnEi3c28nNh/mZE+e+2wdtBkpY6+waTXQKzR+gMJQmK6aLCLTynXfgg==" 432 | "resolved" "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.30.0.tgz" 433 | "version" "1.30.0" 434 | dependencies: 435 | "@babel/runtime" "^7.12.5" 436 | "@solana/buffer-layout" "^3.0.0" 437 | "bn.js" "^5.0.0" 438 | "borsh" "^0.4.0" 439 | "bs58" "^4.0.1" 440 | "buffer" "6.0.1" 441 | "cross-fetch" "^3.1.4" 442 | "ethers" "^5.5.1" 443 | "jayson" "^3.4.4" 444 | "js-sha3" "^0.8.0" 445 | "rpc-websockets" "^7.4.2" 446 | "secp256k1" "^4.0.2" 447 | "superstruct" "^0.14.2" 448 | "tweetnacl" "^1.0.0" 449 | 450 | "@types/bn.js@^4.11.5": 451 | "integrity" "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==" 452 | "resolved" "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz" 453 | "version" "4.11.6" 454 | dependencies: 455 | "@types/node" "*" 456 | 457 | "@types/connect@^3.4.33": 458 | "integrity" "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==" 459 | "resolved" "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" 460 | "version" "3.4.35" 461 | dependencies: 462 | "@types/node" "*" 463 | 464 | "@types/express-serve-static-core@^4.17.9": 465 | "integrity" "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==" 466 | "resolved" "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz" 467 | "version" "4.17.24" 468 | dependencies: 469 | "@types/node" "*" 470 | "@types/qs" "*" 471 | "@types/range-parser" "*" 472 | 473 | "@types/lodash@^4.14.159": 474 | "integrity" "sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ==" 475 | "resolved" "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.176.tgz" 476 | "version" "4.14.176" 477 | 478 | "@types/mocha@^9.0.0": 479 | "integrity" "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==" 480 | "resolved" "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz" 481 | "version" "9.0.0" 482 | 483 | "@types/node@*", "@types/node@^14.14.37": 484 | "integrity" "sha512-sd4CHI9eTJXTH2vF3RGtGkqvWRwhsSSUFsXD4oG38GZzSZ0tNPbWikd2AbOAcKxCXhOg57fL8FPxjpfSzb2pIQ==" 485 | "resolved" "https://registry.npmjs.org/@types/node/-/node-14.17.29.tgz" 486 | "version" "14.17.29" 487 | 488 | "@types/node@^12.12.54": 489 | "integrity" "sha512-+G6kIkmDOyWs7Co8M48lgyauuOlgZeRib64/DFBwYlY6ngwT7wgcF7ga1DsmZImUDfm2rE1jMnUhIEC/gdJ5rw==" 490 | "resolved" "https://registry.npmjs.org/@types/node/-/node-12.20.34.tgz" 491 | "version" "12.20.34" 492 | 493 | "@types/qs@*": 494 | "integrity" "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" 495 | "resolved" "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" 496 | "version" "6.9.7" 497 | 498 | "@types/range-parser@*": 499 | "integrity" "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" 500 | "resolved" "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" 501 | "version" "1.2.4" 502 | 503 | "@types/ws@^7.4.4": 504 | "integrity" "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==" 505 | "resolved" "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz" 506 | "version" "7.4.7" 507 | dependencies: 508 | "@types/node" "*" 509 | 510 | "aes-js@3.0.0": 511 | "integrity" "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" 512 | "resolved" "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz" 513 | "version" "3.0.0" 514 | 515 | "assertion-error@^1.1.0": 516 | "integrity" "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" 517 | "resolved" "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" 518 | "version" "1.1.0" 519 | 520 | "base-x@^3.0.2": 521 | "integrity" "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==" 522 | "resolved" "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz" 523 | "version" "3.0.9" 524 | dependencies: 525 | "safe-buffer" "^5.0.1" 526 | 527 | "base64-js@^1.3.1", "base64-js@^1.5.1": 528 | "integrity" "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 529 | "resolved" "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" 530 | "version" "1.5.1" 531 | 532 | "bech32@1.1.4": 533 | "integrity" "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" 534 | "resolved" "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" 535 | "version" "1.1.4" 536 | 537 | "bn.js@^4.11.9": 538 | "integrity" "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" 539 | "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" 540 | "version" "4.12.0" 541 | 542 | "bn.js@^5.0.0", "bn.js@^5.1.0", "bn.js@^5.1.2", "bn.js@^5.2.0": 543 | "integrity" "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" 544 | "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" 545 | "version" "5.2.0" 546 | 547 | "borsh@^0.4.0": 548 | "integrity" "sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==" 549 | "resolved" "https://registry.npmjs.org/borsh/-/borsh-0.4.0.tgz" 550 | "version" "0.4.0" 551 | dependencies: 552 | "@types/bn.js" "^4.11.5" 553 | "bn.js" "^5.0.0" 554 | "bs58" "^4.0.0" 555 | "text-encoding-utf-8" "^1.0.2" 556 | 557 | "brorand@^1.1.0": 558 | "integrity" "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" 559 | "resolved" "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" 560 | "version" "1.1.0" 561 | 562 | "bs58@^4.0.0", "bs58@^4.0.1": 563 | "integrity" "sha1-vhYedsNU9veIrkBx9j806MTwpCo=" 564 | "resolved" "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz" 565 | "version" "4.0.1" 566 | dependencies: 567 | "base-x" "^3.0.2" 568 | 569 | "buffer-layout@^1.2.0": 570 | "integrity" "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==" 571 | "resolved" "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz" 572 | "version" "1.2.2" 573 | 574 | "buffer@~6.0.3", "buffer@6.0.3": 575 | "integrity" "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==" 576 | "resolved" "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" 577 | "version" "6.0.3" 578 | dependencies: 579 | "base64-js" "^1.3.1" 580 | "ieee754" "^1.2.1" 581 | 582 | "buffer@6.0.1": 583 | "integrity" "sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ==" 584 | "resolved" "https://registry.npmjs.org/buffer/-/buffer-6.0.1.tgz" 585 | "version" "6.0.1" 586 | dependencies: 587 | "base64-js" "^1.3.1" 588 | "ieee754" "^1.2.1" 589 | 590 | "bufferutil@^4.0.1": 591 | "integrity" "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==" 592 | "resolved" "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz" 593 | "version" "4.0.5" 594 | dependencies: 595 | "node-gyp-build" "^4.3.0" 596 | 597 | "camelcase@^5.3.1": 598 | "integrity" "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 599 | "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" 600 | "version" "5.3.1" 601 | 602 | "camelcase@^6.2.0": 603 | "integrity" "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==" 604 | "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" 605 | "version" "6.2.0" 606 | 607 | "chai@^4.3.4": 608 | "integrity" "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==" 609 | "resolved" "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz" 610 | "version" "4.3.4" 611 | dependencies: 612 | "assertion-error" "^1.1.0" 613 | "check-error" "^1.0.2" 614 | "deep-eql" "^3.0.1" 615 | "get-func-name" "^2.0.0" 616 | "pathval" "^1.1.1" 617 | "type-detect" "^4.0.5" 618 | 619 | "check-error@^1.0.2": 620 | "integrity" "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" 621 | "resolved" "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" 622 | "version" "1.0.2" 623 | 624 | "circular-json@^0.5.9": 625 | "integrity" "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==" 626 | "resolved" "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz" 627 | "version" "0.5.9" 628 | 629 | "commander@^2.20.3": 630 | "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" 631 | "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" 632 | "version" "2.20.3" 633 | 634 | "cross-fetch@^3.1.4": 635 | "integrity" "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==" 636 | "resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz" 637 | "version" "3.1.4" 638 | dependencies: 639 | "node-fetch" "2.6.1" 640 | 641 | "crypto-hash@^1.3.0": 642 | "integrity" "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==" 643 | "resolved" "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz" 644 | "version" "1.3.0" 645 | 646 | "deep-eql@^3.0.1": 647 | "integrity" "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==" 648 | "resolved" "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" 649 | "version" "3.0.1" 650 | dependencies: 651 | "type-detect" "^4.0.0" 652 | 653 | "delay@^5.0.0": 654 | "integrity" "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" 655 | "resolved" "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz" 656 | "version" "5.0.0" 657 | 658 | "dot-case@^3.0.4": 659 | "integrity" "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==" 660 | "resolved" "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" 661 | "version" "3.0.4" 662 | dependencies: 663 | "no-case" "^3.0.4" 664 | "tslib" "^2.0.3" 665 | 666 | "dotenv@10.0.0": 667 | "integrity" "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" 668 | "resolved" "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz" 669 | "version" "10.0.0" 670 | 671 | "elliptic@^6.5.2", "elliptic@6.5.4": 672 | "integrity" "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==" 673 | "resolved" "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" 674 | "version" "6.5.4" 675 | dependencies: 676 | "bn.js" "^4.11.9" 677 | "brorand" "^1.1.0" 678 | "hash.js" "^1.0.0" 679 | "hmac-drbg" "^1.0.1" 680 | "inherits" "^2.0.4" 681 | "minimalistic-assert" "^1.0.1" 682 | "minimalistic-crypto-utils" "^1.0.1" 683 | 684 | "es6-promise@^4.0.3": 685 | "integrity" "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" 686 | "resolved" "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" 687 | "version" "4.2.8" 688 | 689 | "es6-promisify@^5.0.0": 690 | "integrity" "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=" 691 | "resolved" "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" 692 | "version" "5.0.0" 693 | dependencies: 694 | "es6-promise" "^4.0.3" 695 | 696 | "ethers@^5.5.1": 697 | "integrity" "sha512-RodEvUFZI+EmFcE6bwkuJqpCYHazdzeR1nMzg+YWQSmQEsNtfl1KHGfp/FWZYl48bI/g7cgBeP2IlPthjiVngw==" 698 | "resolved" "https://registry.npmjs.org/ethers/-/ethers-5.5.1.tgz" 699 | "version" "5.5.1" 700 | dependencies: 701 | "@ethersproject/abi" "5.5.0" 702 | "@ethersproject/abstract-provider" "5.5.1" 703 | "@ethersproject/abstract-signer" "5.5.0" 704 | "@ethersproject/address" "5.5.0" 705 | "@ethersproject/base64" "5.5.0" 706 | "@ethersproject/basex" "5.5.0" 707 | "@ethersproject/bignumber" "5.5.0" 708 | "@ethersproject/bytes" "5.5.0" 709 | "@ethersproject/constants" "5.5.0" 710 | "@ethersproject/contracts" "5.5.0" 711 | "@ethersproject/hash" "5.5.0" 712 | "@ethersproject/hdnode" "5.5.0" 713 | "@ethersproject/json-wallets" "5.5.0" 714 | "@ethersproject/keccak256" "5.5.0" 715 | "@ethersproject/logger" "5.5.0" 716 | "@ethersproject/networks" "5.5.0" 717 | "@ethersproject/pbkdf2" "5.5.0" 718 | "@ethersproject/properties" "5.5.0" 719 | "@ethersproject/providers" "5.5.0" 720 | "@ethersproject/random" "5.5.0" 721 | "@ethersproject/rlp" "5.5.0" 722 | "@ethersproject/sha2" "5.5.0" 723 | "@ethersproject/signing-key" "5.5.0" 724 | "@ethersproject/solidity" "5.5.0" 725 | "@ethersproject/strings" "5.5.0" 726 | "@ethersproject/transactions" "5.5.0" 727 | "@ethersproject/units" "5.5.0" 728 | "@ethersproject/wallet" "5.5.0" 729 | "@ethersproject/web" "5.5.0" 730 | "@ethersproject/wordlists" "5.5.0" 731 | 732 | "eventemitter3@^4.0.7": 733 | "integrity" "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" 734 | "resolved" "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" 735 | "version" "4.0.7" 736 | 737 | "eyes@^0.1.8": 738 | "integrity" "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" 739 | "resolved" "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" 740 | "version" "0.1.8" 741 | 742 | "find@^0.3.0": 743 | "integrity" "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==" 744 | "resolved" "https://registry.npmjs.org/find/-/find-0.3.0.tgz" 745 | "version" "0.3.0" 746 | dependencies: 747 | "traverse-chain" "~0.1.0" 748 | 749 | "get-func-name@^2.0.0": 750 | "integrity" "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" 751 | "resolved" "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" 752 | "version" "2.0.0" 753 | 754 | "hash.js@^1.0.0", "hash.js@^1.0.3", "hash.js@1.1.7": 755 | "integrity" "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==" 756 | "resolved" "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" 757 | "version" "1.1.7" 758 | dependencies: 759 | "inherits" "^2.0.3" 760 | "minimalistic-assert" "^1.0.1" 761 | 762 | "hmac-drbg@^1.0.1": 763 | "integrity" "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=" 764 | "resolved" "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" 765 | "version" "1.0.1" 766 | dependencies: 767 | "hash.js" "^1.0.3" 768 | "minimalistic-assert" "^1.0.0" 769 | "minimalistic-crypto-utils" "^1.0.1" 770 | 771 | "ieee754@^1.2.1": 772 | "integrity" "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 773 | "resolved" "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" 774 | "version" "1.2.1" 775 | 776 | "inherits@^2.0.3", "inherits@^2.0.4": 777 | "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 778 | "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" 779 | "version" "2.0.4" 780 | 781 | "isomorphic-ws@^4.0.1": 782 | "integrity" "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" 783 | "resolved" "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz" 784 | "version" "4.0.1" 785 | 786 | "jayson@^3.4.4": 787 | "integrity" "sha512-wmOjX+eQcnCDyPF4KORomaIj9wj3h0B5VEbeD0+2VHfTfErB+h1zpR7oBkgCZp36AFjp3+a4CLz6U72BYpFHAw==" 788 | "resolved" "https://registry.npmjs.org/jayson/-/jayson-3.6.5.tgz" 789 | "version" "3.6.5" 790 | dependencies: 791 | "@types/connect" "^3.4.33" 792 | "@types/express-serve-static-core" "^4.17.9" 793 | "@types/lodash" "^4.14.159" 794 | "@types/node" "^12.12.54" 795 | "@types/ws" "^7.4.4" 796 | "commander" "^2.20.3" 797 | "delay" "^5.0.0" 798 | "es6-promisify" "^5.0.0" 799 | "eyes" "^0.1.8" 800 | "isomorphic-ws" "^4.0.1" 801 | "json-stringify-safe" "^5.0.1" 802 | "JSONStream" "^1.3.5" 803 | "lodash" "^4.17.20" 804 | "uuid" "^3.4.0" 805 | "ws" "^7.4.5" 806 | 807 | "js-sha256@^0.9.0": 808 | "integrity" "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" 809 | "resolved" "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz" 810 | "version" "0.9.0" 811 | 812 | "js-sha3@^0.8.0", "js-sha3@0.8.0": 813 | "integrity" "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" 814 | "resolved" "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" 815 | "version" "0.8.0" 816 | 817 | "json-stringify-safe@^5.0.1": 818 | "integrity" "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 819 | "resolved" "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" 820 | "version" "5.0.1" 821 | 822 | "jsonparse@^1.2.0": 823 | "integrity" "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" 824 | "resolved" "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" 825 | "version" "1.3.1" 826 | 827 | "JSONStream@^1.3.5": 828 | "integrity" "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==" 829 | "resolved" "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" 830 | "version" "1.3.5" 831 | dependencies: 832 | "jsonparse" "^1.2.0" 833 | "through" ">=2.2.7 <3" 834 | 835 | "lodash@^4.17.20": 836 | "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 837 | "resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" 838 | "version" "4.17.21" 839 | 840 | "lower-case@^2.0.2": 841 | "integrity" "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==" 842 | "resolved" "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" 843 | "version" "2.0.2" 844 | dependencies: 845 | "tslib" "^2.0.3" 846 | 847 | "minimalistic-assert@^1.0.0", "minimalistic-assert@^1.0.1": 848 | "integrity" "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 849 | "resolved" "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" 850 | "version" "1.0.1" 851 | 852 | "minimalistic-crypto-utils@^1.0.1": 853 | "integrity" "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" 854 | "resolved" "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" 855 | "version" "1.0.1" 856 | 857 | "no-case@^3.0.4": 858 | "integrity" "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==" 859 | "resolved" "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" 860 | "version" "3.0.4" 861 | dependencies: 862 | "lower-case" "^2.0.2" 863 | "tslib" "^2.0.3" 864 | 865 | "node-addon-api@^2.0.0": 866 | "integrity" "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" 867 | "resolved" "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz" 868 | "version" "2.0.2" 869 | 870 | "node-fetch@2.6.1": 871 | "integrity" "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 872 | "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz" 873 | "version" "2.6.1" 874 | 875 | "node-gyp-build@^4.2.0", "node-gyp-build@^4.3.0": 876 | "integrity" "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" 877 | "resolved" "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz" 878 | "version" "4.3.0" 879 | 880 | "pako@^2.0.3": 881 | "integrity" "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==" 882 | "resolved" "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz" 883 | "version" "2.0.4" 884 | 885 | "pathval@^1.1.1": 886 | "integrity" "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==" 887 | "resolved" "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" 888 | "version" "1.1.1" 889 | 890 | "regenerator-runtime@^0.13.4": 891 | "integrity" "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" 892 | "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" 893 | "version" "0.13.9" 894 | 895 | "rpc-websockets@^7.4.2": 896 | "integrity" "sha512-ICuqy1MeGl7z0/kesAShN1Lz0AYvOPLpmv2hzfIV5YNGhmJl+B/osmFoWrvxZcQnn/wyPl4Q29ItTJkXFNc9oA==" 897 | "resolved" "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.4.15.tgz" 898 | "version" "7.4.15" 899 | dependencies: 900 | "@babel/runtime" "^7.11.2" 901 | "circular-json" "^0.5.9" 902 | "eventemitter3" "^4.0.7" 903 | "uuid" "^8.3.0" 904 | "ws" "^7.4.5" 905 | optionalDependencies: 906 | "bufferutil" "^4.0.1" 907 | "utf-8-validate" "^5.0.2" 908 | 909 | "safe-buffer@^5.0.1": 910 | "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 911 | "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" 912 | "version" "5.2.1" 913 | 914 | "scrypt-js@3.0.1": 915 | "integrity" "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" 916 | "resolved" "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz" 917 | "version" "3.0.1" 918 | 919 | "secp256k1@^4.0.2": 920 | "integrity" "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==" 921 | "resolved" "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz" 922 | "version" "4.0.2" 923 | dependencies: 924 | "elliptic" "^6.5.2" 925 | "node-addon-api" "^2.0.0" 926 | "node-gyp-build" "^4.2.0" 927 | 928 | "snake-case@^3.0.4": 929 | "integrity" "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==" 930 | "resolved" "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" 931 | "version" "3.0.4" 932 | dependencies: 933 | "dot-case" "^3.0.4" 934 | "tslib" "^2.0.3" 935 | 936 | "superstruct@^0.14.2": 937 | "integrity" "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" 938 | "resolved" "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz" 939 | "version" "0.14.2" 940 | 941 | "text-encoding-utf-8@^1.0.2": 942 | "integrity" "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" 943 | "resolved" "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz" 944 | "version" "1.0.2" 945 | 946 | "through@>=2.2.7 <3": 947 | "integrity" "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 948 | "resolved" "https://registry.npmjs.org/through/-/through-2.3.8.tgz" 949 | "version" "2.3.8" 950 | 951 | "toml@^3.0.0": 952 | "integrity" "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" 953 | "resolved" "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz" 954 | "version" "3.0.0" 955 | 956 | "traverse-chain@~0.1.0": 957 | "integrity" "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=" 958 | "resolved" "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz" 959 | "version" "0.1.0" 960 | 961 | "tslib@^2.0.3": 962 | "integrity" "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" 963 | "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" 964 | "version" "2.3.1" 965 | 966 | "tweetnacl@^1.0.0": 967 | "integrity" "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" 968 | "resolved" "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz" 969 | "version" "1.0.3" 970 | 971 | "type-detect@^4.0.0", "type-detect@^4.0.5": 972 | "integrity" "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" 973 | "resolved" "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" 974 | "version" "4.0.8" 975 | 976 | "utf-8-validate@^5.0.2": 977 | "integrity" "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==" 978 | "resolved" "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz" 979 | "version" "5.0.7" 980 | dependencies: 981 | "node-gyp-build" "^4.3.0" 982 | 983 | "uuid@^3.4.0": 984 | "integrity" "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 985 | "resolved" "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" 986 | "version" "3.4.0" 987 | 988 | "uuid@^8.3.0": 989 | "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 990 | "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" 991 | "version" "8.3.2" 992 | 993 | "ws@*", "ws@^7.4.5", "ws@7.4.6": 994 | "integrity" "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" 995 | "resolved" "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz" 996 | "version" "7.4.6" 997 | -------------------------------------------------------------------------------- /rust/day1(FormattedPrint)/FormattedPrint.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // In general, the `{}` will be automatically replaced with any 3 | // arguments. These will be stringified. 4 | println!("{} days", 31); 5 | 6 | // Without a suffix, 31 becomes an i32. You can change what type 31 is 7 | // by providing a suffix. The number 31i64 for example has the type i64. 8 | 9 | // There are various optional patterns this works with. Positional 10 | // arguments can be used. 11 | println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); 12 | 13 | // As can named arguments. 14 | println!("{subject} {verb} {object}", 15 | object="the lazy dog", 16 | subject="the quick brown fox", 17 | verb="jumps over" 18 | ); 19 | 20 | // Special formatting can be specified after a `:`. 21 | println!("{} of {:b} people know binary, the other half doesn't", 1, 4); 22 | 23 | // You can right-align text with a specified width. This will output 24 | // " 1". 5 white spaces and a "1". 25 | println!("{number:>width$}", number=1, width=6); 26 | 27 | // You can pad numbers with extra zeroes. This will output "000001". 28 | println!("{number:0>width$}", number=1, width=6); 29 | 30 | // Rust even checks to make sure the correct number of arguments are 31 | // used. 32 | println!("My name is {0}, {1} {0}", "Binh", "Binh"); 33 | 34 | // Create a structure named `Structure` which contains an `i32`. 35 | #[derive(Debug)] 36 | struct Person { 37 | x: i32, 38 | y: i32 39 | } 40 | 41 | // However, custom types such as this structure require more complicated 42 | // handling. This will not work. 43 | let x = 10; 44 | let y = 1; 45 | let data = Person {x, y}; 46 | println!("{:?}", data); 47 | 48 | // Hello {next arg ("x")} is {arg 2 (0.01) with precision 49 | // specified in its predecessor (5)} 50 | println!("Hello {} is {2:.*}", "x", 2, 0.01); 51 | } -------------------------------------------------------------------------------- /rust/day2(Primitive)/ArraysAndSlices.rs: -------------------------------------------------------------------------------- 1 | ////////////////////////// 2 | /// Arrays and Slices 3 | ////////////////////////// 4 | 5 | use std::mem; 6 | 7 | // This function borrows a slice 8 | fn analyze_slice(slice: &[i32]) { 9 | println!("first element of the slice: {}", slice[0]); 10 | println!("the slice has {} elements", slice.len()); 11 | } 12 | 13 | fn main() { 14 | // Fixed-size array (type signature is superfluous) 15 | let xs: [i32; 5] = [1, 2, 3, 4, 5]; 16 | 17 | // All elements can be initialized to the same value 18 | let ys: [i32; 500] = [0; 500]; 19 | 20 | // Indexing starts at 0 21 | println!("first element of the array: {}", xs[0]); 22 | println!("second element of the array: {}", xs[1]); 23 | 24 | // `len` returns the count of elements in the array 25 | println!("number of elements in array: {}", xs.len()); 26 | 27 | // Arrays are stack allocated 28 | println!("array occupies {} bytes", mem::size_of_val(&xs)); 29 | 30 | // Arrays can be automatically borrowed as slices 31 | println!("borrow the whole array as a slice"); 32 | analyze_slice(&xs); 33 | 34 | // Slices can point to a section of an array 35 | // They are of the form [starting_index..ending_index] 36 | // starting_index is the first position in the slice 37 | // ending_index is one more than the last position in the slice 38 | println!("borrow a section of the array as a slice"); 39 | analyze_slice(&ys[1 .. 4]); 40 | 41 | // Out of bound indexing causes compile error 42 | println!("{}", xs[5]); 43 | } -------------------------------------------------------------------------------- /rust/day2(Primitive)/BasicPrimitives.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Variables can be type annotated. 3 | let logical: bool = true; 4 | 5 | // let a_float: f64 = 1.0; // Regular annotation 6 | // let an_integer = 5i32; // Suffix annotation 7 | 8 | // Or a default will be used. 9 | let default_float = 3.0; // `f64` 10 | let default_integer = 7; // `i32` 11 | 12 | // A type can also be inferred from context 13 | let mut inferred_type = 12; // Type i64 is inferred from another line 14 | inferred_type = 4294967296i64; 15 | 16 | // A mutable variable's value can be changed. 17 | let mut mutable = 12; // Mutable `i32` 18 | mutable = 21; 19 | 20 | // Error! The type of a variable can't be changed. 21 | // mutable = true; 22 | 23 | // Variables can be overwritten with shadowing. 24 | let mutable = true; 25 | } -------------------------------------------------------------------------------- /rust/day2(Primitive)/LiteralsAndOperators.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Integer addition 3 | println!("1 + 2 = {}", 1u32 + 2); 4 | 5 | // Integer subtraction 6 | println!("1 - 2 = {}", 1i32 - 2); 7 | // TODO ^ Try changing `1i32` to `1u32` to see why the type is important 8 | 9 | // Short-circuiting boolean logic 10 | println!("true AND false is {}", true && false); 11 | println!("true OR false is {}", true || false); 12 | println!("NOT true is {}", !true); 13 | 14 | // Bitwise operations 15 | println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101); 16 | println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101); 17 | println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101); 18 | println!("1 << 5 is {}", 1u32 << 5); 19 | println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2); 20 | 21 | // Use underscores to improve readability! 22 | println!("One million is written as {}", 1_000_000u32); 23 | } -------------------------------------------------------------------------------- /rust/day2(Primitive)/Tuples.rs: -------------------------------------------------------------------------------- 1 | // Tuples can be used as function arguments and as return values 2 | fn reverse(pair: (i32, bool)) -> (bool, i32) { 3 | // `let` can be used to bind the members of a tuple to variables 4 | let (integer, boolean) = pair; 5 | 6 | (boolean, integer) 7 | } 8 | 9 | // The following struct is for the activity. 10 | #[derive(Debug)] 11 | struct Matrix(f32, f32, f32, f32); 12 | 13 | fn main() { 14 | // A tuple with a bunch of different types 15 | let long_tuple = (1u8, 2u16, 3u32, 4u64, 16 | -1i8, -2i16, -3i32, -4i64, 17 | 0.1f32, 0.2f64, 18 | 'a', true); 19 | 20 | // Values can be extracted from the tuple using tuple indexing 21 | println!("long tuple first value: {}", long_tuple.0); 22 | println!("long tuple second value: {}", long_tuple.1); 23 | 24 | // Tuples can be tuple members 25 | let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16); 26 | 27 | // Tuples are printable 28 | println!("tuple of tuples: {:?}", tuple_of_tuples); 29 | 30 | // But long Tuples cannot be printed 31 | // let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); 32 | // println!("too long tuple: {:?}", too_long_tuple); 33 | // TODO ^ Uncomment the above 2 lines to see the compiler error 34 | 35 | let pair = (1, true); 36 | println!("pair is {:?}", pair); 37 | 38 | println!("the reversed pair is {:?}", reverse(pair)); 39 | 40 | // To create one element tuples, the comma is required to tell them apart 41 | // from a literal surrounded by parentheses 42 | println!("one element tuple: {:?}", (5u32,)); 43 | println!("just an integer: {:?}", (5u32)); 44 | 45 | //tuples can be destructured to create bindings 46 | let tuple = (1, "hello", 4.5, true); 47 | 48 | let (a, b, c, d) = tuple; 49 | println!("{:?}, {:?}, {:?}, {:?}", a, b, c, d); 50 | 51 | let matrix = Matrix(1.1, 1.2, 2.1, 2.2); 52 | println!("{:?}", matrix); 53 | 54 | } -------------------------------------------------------------------------------- /rust/day3(Types)/Aliasing.rs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | /// 3 | /// The type statement can be used to give a new name to an existing type. 4 | /// Types must have UpperCamelCase names, or the compiler will raise a warning. 5 | /// The exception to this rule are the primitive types: usize, f32, etc. 6 | /// 7 | ////////////////////////////////////////////////////////////////////////// 8 | 9 | // `NanoSecond` is a new name for `u64`. 10 | type NanoSecond = u64; 11 | type Inch = u64; 12 | 13 | // Use an attribute to silence warning. 14 | #[allow(non_camel_case_types)] 15 | type u64_t = u64; 16 | // TODO ^ Try removing the attribute 17 | 18 | fn main() { 19 | // `NanoSecond` = `Inch` = `u64_t` = `u64`. 20 | let nanoseconds: NanoSecond = 5 as u64_t; 21 | let inches: Inch = 2 as u64_t; 22 | 23 | // Note that type aliases *don't* provide any extra type safety, because 24 | // aliases are *not* new types 25 | println!("{} nanoseconds + {} inches = {} unit?", 26 | nanoseconds, 27 | inches, 28 | nanoseconds + inches); 29 | } 30 | -------------------------------------------------------------------------------- /rust/day3(Types)/Casting.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Rust provides no implicit type conversion (coercion) between primitive types. But, explicit type conversion (casting) can be performed using the as keyword. 3 | Rules for converting between integral types follow C conventions generally, except in cases where C has undefined behavior. The behavior of all casts between integral types is well defined in Rust. 4 | */ 5 | 6 | 7 | // Suppress all warnings from casts which overflow. 8 | #![allow(overflowing_literals)] 9 | 10 | fn main() { 11 | let decimal = 65.4321_f32; 12 | 13 | // Error! No implicit conversion 14 | let integer: u8 = decimal; 15 | // FIXME ^ Comment out this line 16 | 17 | // Explicit conversion 18 | let integer = decimal as u8; 19 | let character = integer as char; 20 | 21 | // Error! There are limitations in conversion rules. 22 | // A float cannot be directly converted to a char. 23 | let character = decimal as char; 24 | // FIXME ^ Comment out this line 25 | 26 | println!("Casting: {} -> {} -> {}", decimal, integer, character); 27 | 28 | // when casting any value to an unsigned type, T, 29 | // T::MAX + 1 is added or subtracted until the value 30 | // fits into the new type 31 | 32 | // 1000 already fits in a u16 33 | println!("1000 as a u16 is: {}", 1000 as u16); 34 | 35 | // 1000 - 256 - 256 - 256 = 232 36 | // Under the hood, the first 8 least significant bits (LSB) are kept, 37 | // while the rest towards the most significant bit (MSB) get truncated. 38 | println!("1000 as a u8 is : {}", 1000 as u8); 39 | // -1 + 256 = 255 40 | println!(" -1 as a u8 is : {}", (-1i8) as u8); 41 | 42 | // For positive numbers, this is the same as the modulus 43 | println!("1000 mod 256 is : {}", 1000 % 256); 44 | 45 | // When casting to a signed type, the (bitwise) result is the same as 46 | // first casting to the corresponding unsigned type. If the most significant 47 | // bit of that value is 1, then the value is negative. 48 | 49 | // Unless it already fits, of course. 50 | println!(" 128 as a i16 is: {}", 128 as i16); 51 | // 128 as u8 -> 128, whose two's complement in eight bits is: 52 | println!(" 128 as a i8 is : {}", 128 as i8); 53 | 54 | // repeating the example above 55 | // 1000 as u8 -> 232 56 | println!("1000 as a u8 is : {}", 1000 as u8); 57 | // and the two's complement of 232 is -24 58 | println!(" 232 as a i8 is : {}", 232 as i8); 59 | 60 | // Since Rust 1.45, the `as` keyword performs a *saturating cast* 61 | // when casting from float to int. If the floating point value exceeds 62 | // the upper bound or is less than the lower bound, the returned value 63 | // will be equal to the bound crossed. 64 | 65 | // 300.0 is 255 66 | println!("300.0 is {}", 300.0_f32 as u8); 67 | // -100.0 as u8 is 0 68 | println!("-100.0 as u8 is {}", -100.0_f32 as u8); 69 | // nan as u8 is 0 70 | println!("nan as u8 is {}", f32::NAN as u8); 71 | 72 | // This behavior incurs a small runtime cost and can be avoided 73 | // with unsafe methods, however the results might overflow and 74 | // return **unsound values**. Use these methods wisely: 75 | unsafe { 76 | // 300.0 is 44 77 | println!("300.0 is {}", 300.0_f32.to_int_unchecked::()); 78 | // -100.0 as u8 is 156 79 | println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::()); 80 | // nan as u8 is 0 81 | println!("nan as u8 is {}", f32::NAN.to_int_unchecked::()); 82 | } 83 | } -------------------------------------------------------------------------------- /rust/day3(Types)/Interference.rs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////// 2 | /// 3 | /// The type inference engine is pretty smart. 4 | /// It does more than looking at the type of the value expression during an initialization. It also looks at how the variable is used afterwards to infer its type. 5 | /// Here's an advanced example of type inference: 6 | /// 7 | //////////////////////////////////////////// 8 | 9 | fn main() { 10 | // Because of the annotation, the compiler knows that `elem` has type u8. 11 | let elem = 5u8; 12 | 13 | // Create an empty vector (a growable array). 14 | let mut vec = Vec::new(); 15 | // At this point the compiler doesn't know the exact type of `vec`, it 16 | // just knows that it's a vector of something (`Vec<_>`). 17 | 18 | // Insert `elem` in the vector. 19 | vec.push(elem); 20 | // Aha! Now the compiler knows that `vec` is a vector of `u8`s (`Vec`) 21 | // TODO ^ Try commenting out the `vec.push(elem)` line 22 | 23 | println!("{:?}", vec); 24 | } 25 | -------------------------------------------------------------------------------- /rust/day3(Types)/Literals.rs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | /// 3 | /// Numeric literals can be type annotated by adding the type as a suffix. As an example, to specify that the literal 42 should have the type i32, write 42i32. 4 | /// The type of unsuffixed numeric literals will depend on how they are used. If no constraint exists, the compiler will use i32 for integers, and f64 for floating-point numbers. 5 | /// 6 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 7 | 8 | 9 | fn main() { 10 | // Suffixed literals, their types are known at initialization 11 | let x = 1u8; 12 | let y = 2u32; 13 | let z = 3f32; 14 | 15 | // Unsuffixed literals, their types depend on how they are used 16 | let i = 1; 17 | let f = 1.0; 18 | 19 | // `size_of_val` returns the size of a variable in bytes 20 | println!("size of `x` in bytes: {}", std::mem::size_of_val(&x)); 21 | println!("size of `y` in bytes: {}", std::mem::size_of_val(&y)); 22 | println!("size of `z` in bytes: {}", std::mem::size_of_val(&z)); 23 | println!("size of `i` in bytes: {}", std::mem::size_of_val(&i)); 24 | println!("size of `f` in bytes: {}", std::mem::size_of_val(&f)); 25 | } 26 | -------------------------------------------------------------------------------- /rust/day3(VariableBinding)/Basic.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let an_integer = 1u32; 3 | let a_boolean = true; 4 | let unit = (); 5 | 6 | // copy `an_integer` into `copied_integer` 7 | let copied_integer = an_integer; 8 | 9 | println!("An integer: {:?}", copied_integer); 10 | println!("A boolean: {:?}", a_boolean); 11 | println!("Meet the unit value: {:?}", unit); 12 | 13 | // The compiler warns about unused variable bindings; these warnings can 14 | // be silenced by prefixing the variable name with an underscore 15 | let _unused_variable = 3u32; 16 | 17 | let noisy_unused_variable = 2u32; 18 | // FIXME ^ Prefix with an underscore to suppress the warning 19 | } -------------------------------------------------------------------------------- /rust/day3(VariableBinding)/Declare.rs: -------------------------------------------------------------------------------- 1 | //////////////////////// 2 | /// Declare first 3 | ////////////////////////// 4 | 5 | // It's possible to declare variable bindings first, and initialize them later. However, this form is seldom used, as it may lead to the use of uninitialized variables. 6 | fn main() { 7 | // Declare a variable binding 8 | let a_binding; 9 | 10 | { 11 | let x = 2; 12 | 13 | // Initialize the binding 14 | a_binding = x * x; 15 | } 16 | 17 | println!("a binding: {}", a_binding); 18 | 19 | let another_binding; 20 | 21 | // Error! Use of uninitialized binding 22 | // println!("another binding: {}", another_binding); 23 | // FIXME ^ Comment out this line 24 | 25 | another_binding = 1; 26 | 27 | println!("another binding: {}", another_binding); 28 | } -------------------------------------------------------------------------------- /rust/day3(VariableBinding)/Freezing.rs: -------------------------------------------------------------------------------- 1 | // When data is bound by the same name immutably, it also freezes. Frozen data can't be modified until the immutable binding goes out of scope: 2 | fn main() { 3 | let mut _mutable_integer = 7i32; 4 | 5 | { 6 | // Shadowing by immutable `_mutable_integer` 7 | let _mutable_integer = _mutable_integer; 8 | 9 | // Error! `_mutable_integer` is frozen in this scope 10 | _mutable_integer = 50; 11 | // FIXME ^ Comment out this line 12 | 13 | // `_mutable_integer` goes out of scope 14 | } 15 | 16 | // Ok! `_mutable_integer` is not frozen in this scope 17 | _mutable_integer = 3; 18 | } -------------------------------------------------------------------------------- /rust/day3(VariableBinding)/Mutability.rs: -------------------------------------------------------------------------------- 1 | // Variable bindings are immutable by default, but this can be overridden using the mut modifier. 2 | fn main() { 3 | let _immutable_binding = 1; 4 | let mut mutable_binding = 1; 5 | 6 | println!("Before mutation: {}", mutable_binding); 7 | 8 | // Ok 9 | mutable_binding += 1; 10 | 11 | println!("After mutation: {}", mutable_binding); 12 | 13 | // Error! 14 | _immutable_binding += 1; 15 | // FIXME ^ Comment out this line 16 | } 17 | -------------------------------------------------------------------------------- /rust/day3(VariableBinding)/ScopeAndShadowing.rs: -------------------------------------------------------------------------------- 1 | // Variable bindings have a scope, and are constrained to live in a block. A block is a collection of statements enclosed by braces {}. 2 | fn main() { 3 | // This binding lives in the main function 4 | let long_lived_binding = 1; 5 | 6 | // This is a block, and has a smaller scope than the main function 7 | { 8 | // This binding only exists in this block 9 | let short_lived_binding = 2; 10 | 11 | println!("inner short: {}", short_lived_binding); 12 | } 13 | // End of the block 14 | 15 | // Error! `short_lived_binding` doesn't exist in this scope 16 | println!("outer short: {}", short_lived_binding); 17 | // FIXME ^ Comment out this line 18 | 19 | println!("outer long: {}", long_lived_binding); 20 | } 21 | 22 | // Also, variable shadowing is allowed. 23 | fn main() { 24 | let shadowed_binding = 1; 25 | 26 | { 27 | println!("before being shadowed: {}", shadowed_binding); 28 | 29 | // This binding *shadows* the outer one 30 | let shadowed_binding = "abc"; 31 | 32 | println!("shadowed in inner block: {}", shadowed_binding); 33 | } 34 | println!("outside inner block: {}", shadowed_binding); 35 | 36 | // This binding *shadows* the previous binding 37 | let shadowed_binding = 2; 38 | println!("shadowed in outer block: {}", shadowed_binding); 39 | } 40 | -------------------------------------------------------------------------------- /rust/day4(Conversion)/Form.rs: -------------------------------------------------------------------------------- 1 | use std::convert::From; 2 | 3 | #[derive(Debug)] 4 | struct Number { 5 | value: i32, 6 | } 7 | 8 | impl From for Number { 9 | fn from(item: i32) -> Self { 10 | Number { value: item } 11 | } 12 | } 13 | 14 | fn main() { 15 | let num = Number::from(30); 16 | println!("My number is {:?}", num); 17 | } -------------------------------------------------------------------------------- /rust/day4(Conversion)/FromStrings.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let parsed: i32 = "5".parse().unwrap(); 3 | let turbo_parsed = "10".parse::().unwrap(); 4 | 5 | let sum = parsed + turbo_parsed; 6 | println!("Sum: {:?}", sum); 7 | } -------------------------------------------------------------------------------- /rust/day4(Conversion)/Intro.rs: -------------------------------------------------------------------------------- 1 | use std::convert::From; 2 | 3 | #[derive(Debug)] 4 | struct Number { 5 | value: i32, 6 | } 7 | 8 | impl From for Number { 9 | fn from(item: i32) -> Self { 10 | Number { value: item } 11 | } 12 | } 13 | 14 | fn main() { 15 | let int = 5; 16 | // Try removing the type declaration 17 | let num: Number = int.into(); 18 | println!("My number is {:?}", num); 19 | } -------------------------------------------------------------------------------- /rust/day4(Conversion)/ToStrings.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | struct Circle { 4 | radius: i32 5 | } 6 | 7 | impl fmt::Display for Circle { 8 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 9 | write!(f, "Circle of radius {}", self.radius) 10 | } 11 | } 12 | 13 | fn main() { 14 | let circle = Circle { radius: 6 }; 15 | println!("{}", circle.to_string()); 16 | } 17 | -------------------------------------------------------------------------------- /rust/day4(Conversion)/TryFormAndTryIntro.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | use std::convert::TryInto; 3 | 4 | #[derive(Debug, PartialEq)] 5 | struct EvenNumber(i32); 6 | 7 | impl TryFrom for EvenNumber { 8 | type Error = (); 9 | 10 | fn try_from(value: i32) -> Result { 11 | if value % 2 == 0 { 12 | Ok(EvenNumber(value)) 13 | } else { 14 | Err(()) 15 | } 16 | } 17 | } 18 | 19 | fn main() { 20 | // TryFrom 21 | 22 | assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8))); 23 | assert_eq!(EvenNumber::try_from(5), Err(())); 24 | 25 | // TryInto 26 | 27 | let result: Result = 8i32.try_into(); 28 | assert_eq!(result, Ok(EvenNumber(8))); 29 | let result: Result = 5i32.try_into(); 30 | assert_eq!(result, Err(())); 31 | } 32 | -------------------------------------------------------------------------------- /rust/tests/day1-answer.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; // Import the `fmt` module. 2 | 3 | struct Color { 4 | red: u8, 5 | green: u8, 6 | blue: u8, 7 | } 8 | 9 | impl fmt::Display for Color { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | write!(f, "RGB ({0}, {1}, {2}) 0x{0:0>2X}{1:0>2X}{2:0>2X}", self.red, self.green, self.blue) 12 | } 13 | } 14 | 15 | struct Matrix(f32, f32, f32, f32); 16 | 17 | impl fmt::Display for Matrix { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | write!(f, "( {} {} )\n( {} {} )", self.0, self.2, self.1, self.3) 20 | } 21 | } 22 | 23 | fn transpose(matrix: Matrix) -> Matrix { 24 | return Matrix(matrix.0, matrix.3, matrix.1, matrix.2) 25 | } 26 | 27 | struct Point { 28 | x: i32, 29 | y: i32, 30 | } 31 | 32 | struct Rectangle { 33 | top_left: Point, 34 | bottom_right: Point, 35 | } 36 | 37 | impl Rectangle { 38 | fn area(&self) -> i32 { 39 | let Point { x: left_edge, y: top_edge } = self.top_left; 40 | let Point { x: right_edge, y: bottom_edge } = self.bottom_right; 41 | (right_edge-left_edge) * (bottom_edge-top_edge) 42 | } 43 | } 44 | 45 | fn main() { 46 | // RGB (128, 255, 90) 0x80FF5A 47 | // RGB (0, 3, 254) 0x0003FE 48 | // RGB (0, 0, 0) 0x000000 49 | for color in [ 50 | Color { red: 128, green: 255, blue: 90 }, 51 | Color { red: 0, green: 3, blue: 254 }, 52 | Color { red: 0, green: 0, blue: 0 }, 53 | ].iter() { 54 | println!("{:}", *color); 55 | } 56 | 57 | // Matrix: 58 | // ( 1.1 1.2 ) 59 | // ( 2.1 2.2 ) 60 | // Transpose: 61 | // ( 1.1 2.1 ) 62 | // ( 1.2 2.2 ) 63 | let matrix = Matrix(1.1, 1.2, 2.1, 2.2); 64 | println!("Matrix:\n{}", matrix); 65 | println!("Transpose:\n{}", transpose(matrix)); 66 | // Rect Area: 1 67 | println!("Rect Area: {}", Rectangle{top_left: Point{x:1, y:1}, bottom_right: Point{x:2, y:2}}.area()); 68 | } 69 | -------------------------------------------------------------------------------- /rust/tests/day1-test.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; // Import the `fmt` module. 2 | 3 | struct Color { 4 | red: u8, 5 | green: u8, 6 | blue: u8, 7 | } 8 | 9 | impl fmt::Display for Color { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | // TODO: 12 | } 13 | } 14 | 15 | struct Matrix(f32, f32, f32, f32); 16 | 17 | impl fmt::Display for Matrix { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | // TODO: 20 | } 21 | } 22 | 23 | fn transpose(matrix: Matrix) -> Matrix { 24 | // TODO: 25 | } 26 | 27 | struct Point { 28 | x: i32, 29 | y: i32, 30 | } 31 | 32 | struct Rectangle { 33 | top_left: Point, 34 | bottom_right: Point, 35 | } 36 | 37 | impl Rectangle { 38 | fn area(&self) -> i32 { 39 | let /* TODO: */ = self.top_left; 40 | let /* TODO: */ = self.bottom_right; 41 | (right_edge-left_edge) * (bottom_edge-top_edge) 42 | } 43 | } 44 | 45 | fn main() { 46 | // RGB (128, 255, 90) 0x80FF5A 47 | // RGB (0, 3, 254) 0x0003FE 48 | // RGB (0, 0, 0) 0x000000 49 | for color in [ 50 | Color { red: 128, green: 255, blue: 90 }, 51 | Color { red: 0, green: 3, blue: 254 }, 52 | Color { red: 0, green: 0, blue: 0 }, 53 | ].iter() { 54 | println!("{:}", *color); 55 | } 56 | 57 | // Matrix: 58 | // ( 1.1 1.2 ) 59 | // ( 2.1 2.2 ) 60 | // Transpose: 61 | // ( 1.1 2.1 ) 62 | // ( 1.2 2.2 ) 63 | let matrix = Matrix(1.1, 1.2, 2.1, 2.2); 64 | println!("Matrix:\n{}", matrix); 65 | println!("Transpose:\n{}", transpose(matrix)); 66 | // Rect Area: 1 67 | println!("Rect Area: {}", Rectangle{top_left: Point{x:1, y:1}, bottom_right: Point{x:2, y:2}}.area()); 68 | } 69 | -------------------------------------------------------------------------------- /rust/tests/day2-answer.rs: -------------------------------------------------------------------------------- 1 | #![allow(overflowing_literals)] 2 | use std::convert::From; 3 | 4 | #[derive(Debug)] 5 | struct Point { 6 | x: i32, 7 | y: i32 8 | } 9 | 10 | impl From for Point { 11 | fn from(xx: i32) -> Self { 12 | Point { x: xx, y: xx } 13 | } 14 | } 15 | 16 | fn main() { 17 | // before 7 18 | // after 50 19 | // outer 7 20 | let _mutable_integer = 7i32; 21 | { 22 | println!("before {}", _mutable_integer); 23 | let mut _mutable_integer = _mutable_integer; 24 | _mutable_integer = 50; 25 | println!("after {}", _mutable_integer); 26 | } 27 | println!("outer {}", _mutable_integer); 28 | 29 | // The Point is Point { x: 30, y: 30 } 30 | let point = Point::from(30); 31 | println!("The Point is {:?}", point); 32 | 33 | // x as a u8 is : 132 34 | let x: u32 = 132 + 256; 35 | println!("x as a u8 is : {}", x as u8); 36 | } 37 | -------------------------------------------------------------------------------- /rust/tests/day2-test.rs: -------------------------------------------------------------------------------- 1 | #![allow(overflowing_literals)] 2 | use std::convert::From; 3 | 4 | #[derive(Debug)] 5 | struct Point { 6 | x: i32, 7 | y: i32 8 | } 9 | 10 | fn main() { 11 | // before 7 12 | // after 50 13 | // outer 7 14 | let _mutable_integer = 7i32; 15 | { 16 | println!("before {}", _mutable_integer); 17 | let _mutable_integer = _mutable_integer; 18 | _mutable_integer = 50; 19 | println!("after {}", _mutable_integer); 20 | } 21 | println!("outer {}", _mutable_integer); 22 | 23 | // The Point is Point { x: 30, y: 30 } 24 | let point = Point::from(30); 25 | println!("The Point is {:?}", point); 26 | 27 | // x as a u8 is : 132 28 | println!("x as a u8 is : {}", /* x */ as u8); 29 | } 30 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/README.md: -------------------------------------------------------------------------------- 1 | # escrow-ui 2 | 3 | UI used in [this guide](https://paulx.dev/2021/01/14/programming-on-solana-an-introduction) 4 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "escrow-ui", 3 | "testnetDefaultChannel": "v1.5.0", 4 | "version": "0.1.0", 5 | "private": true, 6 | "scripts": { 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "@solana/spl-token": "0.0.13", 13 | "@solana/web3.js": "^0.88.0", 14 | "bn.js": "^5.1.3", 15 | "core-js": "^3.6.5", 16 | "vue": "^3.0.0", 17 | "vue-router": "^4.0.2" 18 | }, 19 | "devDependencies": { 20 | "@types/bn.js": "^4.11.6", 21 | "@types/node": "^14.14.16", 22 | "@typescript-eslint/eslint-plugin": "^2.33.0", 23 | "@typescript-eslint/parser": "^2.33.0", 24 | "@vue/cli-plugin-babel": "~4.5.0", 25 | "@vue/cli-plugin-eslint": "~4.5.0", 26 | "@vue/cli-plugin-typescript": "^4.5.9", 27 | "@vue/cli-service": "~4.5.0", 28 | "@vue/compiler-sfc": "^3.0.0", 29 | "@vue/eslint-config-typescript": "^5.0.2", 30 | "babel-eslint": "^10.1.0", 31 | "eslint": "^6.7.2", 32 | "eslint-plugin-vue": "^7.0.0-0", 33 | "typescript": "~3.9.3" 34 | }, 35 | "eslintConfig": { 36 | "root": true, 37 | "env": { 38 | "node": true 39 | }, 40 | "extends": [ 41 | "plugin:vue/vue3-essential", 42 | "eslint:recommended", 43 | "@vue/typescript" 44 | ], 45 | "parserOptions": { 46 | "parser": "@typescript-eslint/parser" 47 | }, 48 | "rules": {} 49 | }, 50 | "browserslist": [ 51 | "> 1%", 52 | "last 2 versions", 53 | "not dead" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-trust/solana-rust-dev/ffb12ca17c6f0f5a1e13f37e0c4e50fd6254567a/solana/escrow/escrow-ui/public/favicon.ico -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/Alice.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 146 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 54 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/Bob.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 81 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'buffer-layout'; 2 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | 5 | const app = createApp(App) 6 | 7 | app.use(router) 8 | 9 | app.mount('#app') 10 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/router.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from 'vue-router' 2 | 3 | const Alice = () => import("./Alice.vue") 4 | const Bob = () => import("./Bob.vue") 5 | 6 | export default createRouter({ 7 | history: createWebHashHistory(), 8 | routes: [ 9 | { 10 | name: "Alice", 11 | path: "/", 12 | component: Alice 13 | }, 14 | { 15 | name: "Bob", 16 | path: "/bob", 17 | component: Bob 18 | } 19 | ] 20 | }) 21 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import type { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/util/initEscrow.ts: -------------------------------------------------------------------------------- 1 | import { AccountLayout, Token, TOKEN_PROGRAM_ID } from "@solana/spl-token"; 2 | import { Account, Connection, PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, Transaction, TransactionInstruction } from "@solana/web3.js"; 3 | import BN from "bn.js"; 4 | import { ESCROW_ACCOUNT_DATA_LAYOUT, EscrowLayout } from "./layout"; 5 | 6 | const connection = new Connection("http://localhost:8899", 'singleGossip'); 7 | 8 | export const initEscrow = async ( 9 | privateKeyByteArray: string, 10 | initializerXTokenAccountPubkeyString: string, 11 | amountXTokensToSendToEscrow: number, 12 | initializerReceivingTokenAccountPubkeyString: string, 13 | expectedAmount: number, 14 | escrowProgramIdString: string) => { 15 | const initializerXTokenAccountPubkey = new PublicKey(initializerXTokenAccountPubkeyString); 16 | 17 | //@ts-expect-error 18 | const XTokenMintAccountPubkey = new PublicKey((await connection.getParsedAccountInfo(initializerXTokenAccountPubkey, 'singleGossip')).value!.data.parsed.info.mint); 19 | 20 | const privateKeyDecoded = privateKeyByteArray.split(',').map(s => parseInt(s)); 21 | const initializerAccount = new Account(privateKeyDecoded); 22 | 23 | const tempTokenAccount = new Account(); 24 | const createTempTokenAccountIx = SystemProgram.createAccount({ 25 | programId: TOKEN_PROGRAM_ID, 26 | space: AccountLayout.span, 27 | lamports: await connection.getMinimumBalanceForRentExemption(AccountLayout.span, 'singleGossip'), 28 | fromPubkey: initializerAccount.publicKey, 29 | newAccountPubkey: tempTokenAccount.publicKey 30 | }); 31 | const initTempAccountIx = Token.createInitAccountInstruction(TOKEN_PROGRAM_ID, XTokenMintAccountPubkey, tempTokenAccount.publicKey, initializerAccount.publicKey); 32 | const transferXTokensToTempAccIx = Token 33 | .createTransferInstruction(TOKEN_PROGRAM_ID, initializerXTokenAccountPubkey, tempTokenAccount.publicKey, initializerAccount.publicKey, [], amountXTokensToSendToEscrow); 34 | 35 | const escrowAccount = new Account(); 36 | const escrowProgramId = new PublicKey(escrowProgramIdString); 37 | 38 | const createEscrowAccountIx = SystemProgram.createAccount({ 39 | space: ESCROW_ACCOUNT_DATA_LAYOUT.span, 40 | lamports: await connection.getMinimumBalanceForRentExemption(ESCROW_ACCOUNT_DATA_LAYOUT.span, 'singleGossip'), 41 | fromPubkey: initializerAccount.publicKey, 42 | newAccountPubkey: escrowAccount.publicKey, 43 | programId: escrowProgramId 44 | }); 45 | 46 | const initEscrowIx = new TransactionInstruction({ 47 | programId: escrowProgramId, 48 | keys: [ 49 | { pubkey: initializerAccount.publicKey, isSigner: true, isWritable: false }, 50 | { pubkey: tempTokenAccount.publicKey, isSigner: false, isWritable: true }, 51 | { pubkey: new PublicKey(initializerReceivingTokenAccountPubkeyString), isSigner: false, isWritable: false }, 52 | { pubkey: escrowAccount.publicKey, isSigner: false, isWritable: true }, 53 | { pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false}, 54 | { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, 55 | ], 56 | data: Buffer.from(Uint8Array.of(0, ...new BN(expectedAmount).toArray("le", 8))) 57 | }) 58 | 59 | const tx = new Transaction() 60 | .add(createTempTokenAccountIx, initTempAccountIx, transferXTokensToTempAccIx, createEscrowAccountIx, initEscrowIx); 61 | await connection.sendTransaction(tx, [initializerAccount, tempTokenAccount, escrowAccount], {skipPreflight: false, preflightCommitment: 'singleGossip'}); 62 | 63 | await new Promise((resolve) => setTimeout(resolve, 1000)); 64 | 65 | const encodedEscrowState = (await connection.getAccountInfo(escrowAccount.publicKey, 'singleGossip'))!.data; 66 | const decodedEscrowState = ESCROW_ACCOUNT_DATA_LAYOUT.decode(encodedEscrowState) as EscrowLayout; 67 | return { 68 | escrowAccountPubkey: escrowAccount.publicKey.toBase58(), 69 | isInitialized: !!decodedEscrowState.isInitialized, 70 | initializerAccountPubkey: new PublicKey(decodedEscrowState.initializerPubkey).toBase58(), 71 | XTokenTempAccountPubkey: new PublicKey(decodedEscrowState.initializerTempTokenAccountPubkey).toBase58(), 72 | initializerYTokenAccount: new PublicKey(decodedEscrowState.initializerReceivingTokenAccountPubkey).toBase58(), 73 | expectedAmount: new BN(decodedEscrowState.expectedAmount, 10, "le").toNumber() 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/util/layout.ts: -------------------------------------------------------------------------------- 1 | import * as BufferLayout from "buffer-layout"; 2 | 3 | /** 4 | * Layout for a public key 5 | */ 6 | const publicKey = (property = "publicKey") => { 7 | return BufferLayout.blob(32, property); 8 | }; 9 | 10 | /** 11 | * Layout for a 64bit unsigned value 12 | */ 13 | const uint64 = (property = "uint64") => { 14 | return BufferLayout.blob(8, property); 15 | }; 16 | 17 | export const ESCROW_ACCOUNT_DATA_LAYOUT = BufferLayout.struct([ 18 | BufferLayout.u8("isInitialized"), 19 | publicKey("initializerPubkey"), 20 | publicKey("initializerTempTokenAccountPubkey"), 21 | publicKey("initializerReceivingTokenAccountPubkey"), 22 | uint64("expectedAmount"), 23 | ]); 24 | 25 | export interface EscrowLayout { 26 | isInitialized: number, 27 | initializerPubkey: Uint8Array, 28 | initializerReceivingTokenAccountPubkey: Uint8Array, 29 | initializerTempTokenAccountPubkey: Uint8Array, 30 | expectedAmount: Uint8Array 31 | } 32 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/src/util/takeTrade.ts: -------------------------------------------------------------------------------- 1 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 2 | import { Account, Connection, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js"; 3 | import BN from "bn.js"; 4 | import { ESCROW_ACCOUNT_DATA_LAYOUT, EscrowLayout } from "./layout"; 5 | 6 | const connection = new Connection("http://localhost:8899", 'singleGossip'); 7 | 8 | export const takeTrade = async ( 9 | privateKeyByteArray: string, 10 | escrowAccountAddressString: string, 11 | takerXTokenAccountAddressString: string, 12 | takerYTokenAccountAddressString: string, 13 | takerExpectedXTokenAmount: number, 14 | programIdString: string, 15 | ) => { 16 | const takerAccount = new Account(privateKeyByteArray.split(',').map(s => parseInt(s))); 17 | const escrowAccountPubkey = new PublicKey(escrowAccountAddressString); 18 | const takerXTokenAccountPubkey = new PublicKey(takerXTokenAccountAddressString); 19 | const takerYTokenAccountPubkey = new PublicKey(takerYTokenAccountAddressString); 20 | const programId = new PublicKey(programIdString); 21 | 22 | let encodedEscrowState; 23 | try { 24 | encodedEscrowState = (await connection.getAccountInfo(escrowAccountPubkey, 'singleGossip'))!.data; 25 | } catch (err) { 26 | throw new Error("Could not find escrow at given address!") 27 | } 28 | const decodedEscrowLayout = ESCROW_ACCOUNT_DATA_LAYOUT.decode(encodedEscrowState) as EscrowLayout; 29 | const escrowState = { 30 | escrowAccountPubkey: escrowAccountPubkey, 31 | isInitialized: !!decodedEscrowLayout.isInitialized, 32 | initializerAccountPubkey: new PublicKey(decodedEscrowLayout.initializerPubkey), 33 | XTokenTempAccountPubkey: new PublicKey(decodedEscrowLayout.initializerTempTokenAccountPubkey), 34 | initializerYTokenAccount: new PublicKey(decodedEscrowLayout.initializerReceivingTokenAccountPubkey), 35 | expectedAmount: new BN(decodedEscrowLayout.expectedAmount, 10, "le") 36 | }; 37 | 38 | const PDA = await PublicKey.findProgramAddress([Buffer.from("escrow")], programId); 39 | 40 | const exchangeInstruction = new TransactionInstruction({ 41 | programId, 42 | data: Buffer.from(Uint8Array.of(1, ...new BN(takerExpectedXTokenAmount).toArray("le", 8))), 43 | keys: [ 44 | { pubkey: takerAccount.publicKey, isSigner: true, isWritable: false }, 45 | { pubkey: takerYTokenAccountPubkey, isSigner: false, isWritable: true }, 46 | { pubkey: takerXTokenAccountPubkey, isSigner: false, isWritable: true }, 47 | { pubkey: escrowState.XTokenTempAccountPubkey, isSigner: false, isWritable: true}, 48 | { pubkey: escrowState.initializerAccountPubkey, isSigner: false, isWritable: true}, 49 | { pubkey: escrowState.initializerYTokenAccount, isSigner: false, isWritable: true}, 50 | { pubkey: escrowAccountPubkey, isSigner: false, isWritable: true }, 51 | { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false}, 52 | { pubkey: PDA[0], isSigner: false, isWritable: false} 53 | ] 54 | }) 55 | 56 | await connection.sendTransaction(new Transaction().add(exchangeInstruction), [takerAccount], {skipPreflight: false, preflightCommitment: 'singleGossip'}); 57 | } 58 | -------------------------------------------------------------------------------- /solana/escrow/escrow-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "sourceMap": true, 12 | "baseUrl": ".", 13 | "types": [ 14 | "webpack-env", 15 | "node" 16 | ], 17 | "paths": { 18 | "@/*": [ 19 | "src/*" 20 | ] 21 | }, 22 | "lib": [ 23 | "esnext", 24 | "dom", 25 | "dom.iterable", 26 | "scripthost" 27 | ] 28 | }, 29 | "include": [ 30 | "src/**/*.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "tests/**/*.ts", 34 | "tests/**/*.tsx" 35 | ], 36 | "exclude": [ 37 | "node_modules" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /solana/escrow/program/.gitignore: -------------------------------------------------------------------------------- 1 | /*-dump.txt 2 | /*.so 3 | /target/ 4 | /test-ledger/ -------------------------------------------------------------------------------- /solana/escrow/program/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.4.7" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "0.7.18" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "arrayref" 22 | version = "0.3.6" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 25 | 26 | [[package]] 27 | name = "arrayvec" 28 | version = "0.5.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 31 | 32 | [[package]] 33 | name = "atty" 34 | version = "0.2.14" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 37 | dependencies = [ 38 | "hermit-abi", 39 | "libc", 40 | "winapi", 41 | ] 42 | 43 | [[package]] 44 | name = "autocfg" 45 | version = "1.0.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 48 | 49 | [[package]] 50 | name = "base64" 51 | version = "0.12.3" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 54 | 55 | [[package]] 56 | name = "base64" 57 | version = "0.13.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 60 | 61 | [[package]] 62 | name = "bincode" 63 | version = "1.3.3" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 66 | dependencies = [ 67 | "serde", 68 | ] 69 | 70 | [[package]] 71 | name = "blake3" 72 | version = "0.3.8" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3" 75 | dependencies = [ 76 | "arrayref", 77 | "arrayvec", 78 | "cc", 79 | "cfg-if 0.1.10", 80 | "constant_time_eq", 81 | "crypto-mac", 82 | "digest 0.9.0", 83 | ] 84 | 85 | [[package]] 86 | name = "block-buffer" 87 | version = "0.9.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 90 | dependencies = [ 91 | "block-padding", 92 | "generic-array 0.14.4", 93 | ] 94 | 95 | [[package]] 96 | name = "block-padding" 97 | version = "0.2.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" 100 | 101 | [[package]] 102 | name = "borsh" 103 | version = "0.9.1" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "18dda7dc709193c0d86a1a51050a926dc3df1cf262ec46a23a25dba421ea1924" 106 | dependencies = [ 107 | "borsh-derive", 108 | "hashbrown", 109 | ] 110 | 111 | [[package]] 112 | name = "borsh-derive" 113 | version = "0.9.1" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "684155372435f578c0fa1acd13ebbb182cc19d6b38b64ae7901da4393217d264" 116 | dependencies = [ 117 | "borsh-derive-internal", 118 | "borsh-schema-derive-internal", 119 | "proc-macro-crate 0.1.5", 120 | "proc-macro2", 121 | "syn", 122 | ] 123 | 124 | [[package]] 125 | name = "borsh-derive-internal" 126 | version = "0.9.1" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "2102f62f8b6d3edeab871830782285b64cc1830168094db05c8e458f209bc5c3" 129 | dependencies = [ 130 | "proc-macro2", 131 | "quote", 132 | "syn", 133 | ] 134 | 135 | [[package]] 136 | name = "borsh-schema-derive-internal" 137 | version = "0.9.1" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "196c978c4c9b0b142d446ef3240690bf5a8a33497074a113ff9a337ccb750483" 140 | dependencies = [ 141 | "proc-macro2", 142 | "quote", 143 | "syn", 144 | ] 145 | 146 | [[package]] 147 | name = "bs58" 148 | version = "0.3.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" 151 | 152 | [[package]] 153 | name = "bv" 154 | version = "0.11.1" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" 157 | dependencies = [ 158 | "feature-probe", 159 | "serde", 160 | ] 161 | 162 | [[package]] 163 | name = "bytemuck" 164 | version = "1.7.2" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" 167 | dependencies = [ 168 | "bytemuck_derive", 169 | ] 170 | 171 | [[package]] 172 | name = "bytemuck_derive" 173 | version = "1.0.1" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" 176 | dependencies = [ 177 | "proc-macro2", 178 | "quote", 179 | "syn", 180 | ] 181 | 182 | [[package]] 183 | name = "byteorder" 184 | version = "1.4.3" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 187 | 188 | [[package]] 189 | name = "cc" 190 | version = "1.0.71" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" 193 | 194 | [[package]] 195 | name = "cfg-if" 196 | version = "0.1.10" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 199 | 200 | [[package]] 201 | name = "cfg-if" 202 | version = "1.0.0" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 205 | 206 | [[package]] 207 | name = "constant_time_eq" 208 | version = "0.1.5" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 211 | 212 | [[package]] 213 | name = "cpufeatures" 214 | version = "0.2.1" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" 217 | dependencies = [ 218 | "libc", 219 | ] 220 | 221 | [[package]] 222 | name = "crunchy" 223 | version = "0.2.2" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 226 | 227 | [[package]] 228 | name = "crypto-mac" 229 | version = "0.8.0" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" 232 | dependencies = [ 233 | "generic-array 0.14.4", 234 | "subtle", 235 | ] 236 | 237 | [[package]] 238 | name = "curve25519-dalek" 239 | version = "2.1.3" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" 242 | dependencies = [ 243 | "byteorder", 244 | "digest 0.8.1", 245 | "rand_core", 246 | "subtle", 247 | "zeroize", 248 | ] 249 | 250 | [[package]] 251 | name = "derivative" 252 | version = "2.2.0" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 255 | dependencies = [ 256 | "proc-macro2", 257 | "quote", 258 | "syn", 259 | ] 260 | 261 | [[package]] 262 | name = "digest" 263 | version = "0.8.1" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 266 | dependencies = [ 267 | "generic-array 0.12.4", 268 | ] 269 | 270 | [[package]] 271 | name = "digest" 272 | version = "0.9.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 275 | dependencies = [ 276 | "generic-array 0.14.4", 277 | ] 278 | 279 | [[package]] 280 | name = "either" 281 | version = "1.6.1" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 284 | 285 | [[package]] 286 | name = "env_logger" 287 | version = "0.8.4" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" 290 | dependencies = [ 291 | "atty", 292 | "humantime", 293 | "log", 294 | "regex", 295 | "termcolor", 296 | ] 297 | 298 | [[package]] 299 | name = "feature-probe" 300 | version = "0.1.1" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" 303 | 304 | [[package]] 305 | name = "generic-array" 306 | version = "0.12.4" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 309 | dependencies = [ 310 | "typenum", 311 | ] 312 | 313 | [[package]] 314 | name = "generic-array" 315 | version = "0.14.4" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 318 | dependencies = [ 319 | "serde", 320 | "typenum", 321 | "version_check", 322 | ] 323 | 324 | [[package]] 325 | name = "getrandom" 326 | version = "0.1.16" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 329 | dependencies = [ 330 | "cfg-if 1.0.0", 331 | "libc", 332 | "wasi", 333 | ] 334 | 335 | [[package]] 336 | name = "hashbrown" 337 | version = "0.9.1" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 340 | dependencies = [ 341 | "ahash", 342 | ] 343 | 344 | [[package]] 345 | name = "hermit-abi" 346 | version = "0.1.19" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 349 | dependencies = [ 350 | "libc", 351 | ] 352 | 353 | [[package]] 354 | name = "hex" 355 | version = "0.4.3" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 358 | 359 | [[package]] 360 | name = "hmac" 361 | version = "0.8.1" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" 364 | dependencies = [ 365 | "crypto-mac", 366 | "digest 0.9.0", 367 | ] 368 | 369 | [[package]] 370 | name = "hmac-drbg" 371 | version = "0.3.0" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" 374 | dependencies = [ 375 | "digest 0.9.0", 376 | "generic-array 0.14.4", 377 | "hmac", 378 | ] 379 | 380 | [[package]] 381 | name = "humantime" 382 | version = "2.1.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 385 | 386 | [[package]] 387 | name = "itertools" 388 | version = "0.9.0" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" 391 | dependencies = [ 392 | "either", 393 | ] 394 | 395 | [[package]] 396 | name = "keccak" 397 | version = "0.1.0" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" 400 | 401 | [[package]] 402 | name = "lazy_static" 403 | version = "1.4.0" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 406 | 407 | [[package]] 408 | name = "libc" 409 | version = "0.2.105" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" 412 | 413 | [[package]] 414 | name = "libsecp256k1" 415 | version = "0.5.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "bd1137239ab33b41aa9637a88a28249e5e70c40a42ccc92db7f12cc356c1fcd7" 418 | dependencies = [ 419 | "arrayref", 420 | "base64 0.12.3", 421 | "digest 0.9.0", 422 | "hmac-drbg", 423 | "libsecp256k1-core", 424 | "libsecp256k1-gen-ecmult", 425 | "libsecp256k1-gen-genmult", 426 | "rand", 427 | "serde", 428 | "sha2", 429 | "typenum", 430 | ] 431 | 432 | [[package]] 433 | name = "libsecp256k1-core" 434 | version = "0.2.2" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" 437 | dependencies = [ 438 | "crunchy", 439 | "digest 0.9.0", 440 | "subtle", 441 | ] 442 | 443 | [[package]] 444 | name = "libsecp256k1-gen-ecmult" 445 | version = "0.2.1" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" 448 | dependencies = [ 449 | "libsecp256k1-core", 450 | ] 451 | 452 | [[package]] 453 | name = "libsecp256k1-gen-genmult" 454 | version = "0.2.1" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" 457 | dependencies = [ 458 | "libsecp256k1-core", 459 | ] 460 | 461 | [[package]] 462 | name = "log" 463 | version = "0.4.14" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 466 | dependencies = [ 467 | "cfg-if 1.0.0", 468 | ] 469 | 470 | [[package]] 471 | name = "memchr" 472 | version = "2.4.1" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 475 | 476 | [[package]] 477 | name = "memmap2" 478 | version = "0.1.0" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" 481 | dependencies = [ 482 | "libc", 483 | ] 484 | 485 | [[package]] 486 | name = "num-derive" 487 | version = "0.3.3" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" 490 | dependencies = [ 491 | "proc-macro2", 492 | "quote", 493 | "syn", 494 | ] 495 | 496 | [[package]] 497 | name = "num-traits" 498 | version = "0.2.14" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 501 | dependencies = [ 502 | "autocfg", 503 | ] 504 | 505 | [[package]] 506 | name = "num_enum" 507 | version = "0.5.4" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" 510 | dependencies = [ 511 | "derivative", 512 | "num_enum_derive", 513 | ] 514 | 515 | [[package]] 516 | name = "num_enum_derive" 517 | version = "0.5.4" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" 520 | dependencies = [ 521 | "proc-macro-crate 1.1.0", 522 | "proc-macro2", 523 | "quote", 524 | "syn", 525 | ] 526 | 527 | [[package]] 528 | name = "opaque-debug" 529 | version = "0.3.0" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 532 | 533 | [[package]] 534 | name = "ppv-lite86" 535 | version = "0.2.15" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" 538 | 539 | [[package]] 540 | name = "proc-macro-crate" 541 | version = "0.1.5" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" 544 | dependencies = [ 545 | "toml", 546 | ] 547 | 548 | [[package]] 549 | name = "proc-macro-crate" 550 | version = "1.1.0" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" 553 | dependencies = [ 554 | "thiserror", 555 | "toml", 556 | ] 557 | 558 | [[package]] 559 | name = "proc-macro2" 560 | version = "1.0.32" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" 563 | dependencies = [ 564 | "unicode-xid", 565 | ] 566 | 567 | [[package]] 568 | name = "quote" 569 | version = "1.0.10" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 572 | dependencies = [ 573 | "proc-macro2", 574 | ] 575 | 576 | [[package]] 577 | name = "rand" 578 | version = "0.7.3" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 581 | dependencies = [ 582 | "getrandom", 583 | "libc", 584 | "rand_chacha", 585 | "rand_core", 586 | "rand_hc", 587 | ] 588 | 589 | [[package]] 590 | name = "rand_chacha" 591 | version = "0.2.2" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 594 | dependencies = [ 595 | "ppv-lite86", 596 | "rand_core", 597 | ] 598 | 599 | [[package]] 600 | name = "rand_core" 601 | version = "0.5.1" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 604 | dependencies = [ 605 | "getrandom", 606 | ] 607 | 608 | [[package]] 609 | name = "rand_hc" 610 | version = "0.2.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 613 | dependencies = [ 614 | "rand_core", 615 | ] 616 | 617 | [[package]] 618 | name = "regex" 619 | version = "1.5.4" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 622 | dependencies = [ 623 | "aho-corasick", 624 | "memchr", 625 | "regex-syntax", 626 | ] 627 | 628 | [[package]] 629 | name = "regex-syntax" 630 | version = "0.6.25" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 633 | 634 | [[package]] 635 | name = "rustc_version" 636 | version = "0.2.3" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 639 | dependencies = [ 640 | "semver", 641 | ] 642 | 643 | [[package]] 644 | name = "rustversion" 645 | version = "1.0.5" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" 648 | 649 | [[package]] 650 | name = "semver" 651 | version = "0.9.0" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 654 | dependencies = [ 655 | "semver-parser", 656 | ] 657 | 658 | [[package]] 659 | name = "semver-parser" 660 | version = "0.7.0" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 663 | 664 | [[package]] 665 | name = "serde" 666 | version = "1.0.130" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 669 | dependencies = [ 670 | "serde_derive", 671 | ] 672 | 673 | [[package]] 674 | name = "serde_bytes" 675 | version = "0.11.5" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" 678 | dependencies = [ 679 | "serde", 680 | ] 681 | 682 | [[package]] 683 | name = "serde_derive" 684 | version = "1.0.130" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 687 | dependencies = [ 688 | "proc-macro2", 689 | "quote", 690 | "syn", 691 | ] 692 | 693 | [[package]] 694 | name = "sha2" 695 | version = "0.9.8" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" 698 | dependencies = [ 699 | "block-buffer", 700 | "cfg-if 1.0.0", 701 | "cpufeatures", 702 | "digest 0.9.0", 703 | "opaque-debug", 704 | ] 705 | 706 | [[package]] 707 | name = "sha3" 708 | version = "0.9.1" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" 711 | dependencies = [ 712 | "block-buffer", 713 | "digest 0.9.0", 714 | "keccak", 715 | "opaque-debug", 716 | ] 717 | 718 | [[package]] 719 | name = "solana-escrow" 720 | version = "0.1.0" 721 | dependencies = [ 722 | "arrayref", 723 | "solana-program", 724 | "spl-token", 725 | "thiserror", 726 | ] 727 | 728 | [[package]] 729 | name = "solana-frozen-abi" 730 | version = "1.8.2" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "9ab31b4bda342736987ec16526a6cac4fa817f86ced9634f020ce1dcfac0867f" 733 | dependencies = [ 734 | "bs58", 735 | "bv", 736 | "generic-array 0.14.4", 737 | "log", 738 | "memmap2", 739 | "rustc_version", 740 | "serde", 741 | "serde_derive", 742 | "sha2", 743 | "solana-frozen-abi-macro", 744 | "solana-logger", 745 | "thiserror", 746 | ] 747 | 748 | [[package]] 749 | name = "solana-frozen-abi-macro" 750 | version = "1.8.2" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "d532b5214f70604ac067250a004478389c0ebea8923ae58a49fa7dadd0e69f61" 753 | dependencies = [ 754 | "proc-macro2", 755 | "quote", 756 | "rustc_version", 757 | "syn", 758 | ] 759 | 760 | [[package]] 761 | name = "solana-logger" 762 | version = "1.8.2" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "356fc4bc5395d26e7d0f3c7e6272b1a28dc591a81f1ee6e577c1d8859e946422" 765 | dependencies = [ 766 | "env_logger", 767 | "lazy_static", 768 | "log", 769 | ] 770 | 771 | [[package]] 772 | name = "solana-program" 773 | version = "1.8.2" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "a6ebce89024394bc7d9289978f14220ab3bd7dac568ec4e081c2d9eb35d92c8f" 776 | dependencies = [ 777 | "base64 0.13.0", 778 | "bincode", 779 | "blake3", 780 | "borsh", 781 | "borsh-derive", 782 | "bs58", 783 | "bv", 784 | "bytemuck", 785 | "curve25519-dalek", 786 | "hex", 787 | "itertools", 788 | "lazy_static", 789 | "libsecp256k1", 790 | "log", 791 | "num-derive", 792 | "num-traits", 793 | "rand", 794 | "rustc_version", 795 | "rustversion", 796 | "serde", 797 | "serde_bytes", 798 | "serde_derive", 799 | "sha2", 800 | "sha3", 801 | "solana-frozen-abi", 802 | "solana-frozen-abi-macro", 803 | "solana-logger", 804 | "solana-sdk-macro", 805 | "thiserror", 806 | ] 807 | 808 | [[package]] 809 | name = "solana-sdk-macro" 810 | version = "1.8.2" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "49de94601f4af95d8834817ac6c6f3cbedac8fd582a5c5b940e78fe07803b78b" 813 | dependencies = [ 814 | "bs58", 815 | "proc-macro2", 816 | "quote", 817 | "rustversion", 818 | "syn", 819 | ] 820 | 821 | [[package]] 822 | name = "spl-token" 823 | version = "3.2.0" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "93bfdd5bd7c869cb565c7d7635c4fafe189b988a0bdef81063cd9585c6b8dc01" 826 | dependencies = [ 827 | "arrayref", 828 | "num-derive", 829 | "num-traits", 830 | "num_enum", 831 | "solana-program", 832 | "thiserror", 833 | ] 834 | 835 | [[package]] 836 | name = "subtle" 837 | version = "2.4.1" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 840 | 841 | [[package]] 842 | name = "syn" 843 | version = "1.0.81" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" 846 | dependencies = [ 847 | "proc-macro2", 848 | "quote", 849 | "unicode-xid", 850 | ] 851 | 852 | [[package]] 853 | name = "termcolor" 854 | version = "1.1.2" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 857 | dependencies = [ 858 | "winapi-util", 859 | ] 860 | 861 | [[package]] 862 | name = "thiserror" 863 | version = "1.0.30" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 866 | dependencies = [ 867 | "thiserror-impl", 868 | ] 869 | 870 | [[package]] 871 | name = "thiserror-impl" 872 | version = "1.0.30" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 875 | dependencies = [ 876 | "proc-macro2", 877 | "quote", 878 | "syn", 879 | ] 880 | 881 | [[package]] 882 | name = "toml" 883 | version = "0.5.8" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 886 | dependencies = [ 887 | "serde", 888 | ] 889 | 890 | [[package]] 891 | name = "typenum" 892 | version = "1.14.0" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" 895 | 896 | [[package]] 897 | name = "unicode-xid" 898 | version = "0.2.2" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 901 | 902 | [[package]] 903 | name = "version_check" 904 | version = "0.9.3" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 907 | 908 | [[package]] 909 | name = "wasi" 910 | version = "0.9.0+wasi-snapshot-preview1" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 913 | 914 | [[package]] 915 | name = "winapi" 916 | version = "0.3.9" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 919 | dependencies = [ 920 | "winapi-i686-pc-windows-gnu", 921 | "winapi-x86_64-pc-windows-gnu", 922 | ] 923 | 924 | [[package]] 925 | name = "winapi-i686-pc-windows-gnu" 926 | version = "0.4.0" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 929 | 930 | [[package]] 931 | name = "winapi-util" 932 | version = "0.1.5" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 935 | dependencies = [ 936 | "winapi", 937 | ] 938 | 939 | [[package]] 940 | name = "winapi-x86_64-pc-windows-gnu" 941 | version = "0.4.0" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 944 | 945 | [[package]] 946 | name = "zeroize" 947 | version = "1.4.2" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970" 950 | -------------------------------------------------------------------------------- /solana/escrow/program/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "solana-escrow" 3 | version = "0.1.0" 4 | edition = "2018" 5 | license = "WTFPL" 6 | publish = false 7 | 8 | [dependencies] 9 | solana-program = "1.6.9" 10 | thiserror = "1.0.24" 11 | spl-token = { version = "3.1.1", features = ["no-entrypoint"] } 12 | arrayref = "0.3.6" 13 | 14 | [lib] 15 | crate-type = ["cdylib", "lib"] -------------------------------------------------------------------------------- /solana/escrow/program/README.md: -------------------------------------------------------------------------------- 1 | ## Intro & Motivation 2 | 3 | This guide is meant to serve as an intro to coding on the [Solana](https://solana.com/) Blockchain, using an escrow program as an example. We'll go through the code together, building the escrow program step by step. I've also created a UI you will be able to use to try out your program. Additionally, you'll get to play with the (shameless plug) [spl-token-ui](https://www.spl-token-ui.com). 4 | 5 | ## Setup Dev Environment 6 | 7 | ### Install Run and Solana 8 | 9 | 1. Install Rust from https://rustup.rs/ 10 | 2. Install Solana from https://docs.solana.com/cli/install-solana-cli-tools#use-solanas-install-tool 11 | 12 | - Build and test for program compiled natively 13 | `$ cargo build`
14 | `$ cargo test` 15 | 16 | - Build and test the program compiled for BPF 17 | `$ cargo build-bpf`
18 | `$ cargo test-bpf` 19 | 20 | ## Necessary knowledge 21 | 22 | ### Rust Basic 23 | 24 | - [Crate](https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html)
25 | - [Marcro](https://doc.rust-lang.org/stable/book/ch19-06-macros.html) 26 | 27 | ### Solana Basic 28 | 29 | - [entrypoint](https://docs.solana.com/developing/on-chain-programs/developing-rust#program-entrypoint) 30 | - [BPF Loader](https://docs.solana.com/developing/on-chain-programs/overview) 31 | - [accounts](https://docs.solana.com/developing/programming-model/accounts) 32 | - [Programs](https://docs.solana.com/developing/on-chain-programs/overview) 33 | 34 | ### Project architecture 35 | 36 | 1. Someone calls the entrypoint 37 | 2. entrypoint forwards the arguments to the processor 38 | 3. processor asks instruction.rs to decode the instruction_data from the entrypoint. 39 | 4. Using decoded data, processor will now decide which processing function to use to process the request. 40 | 5. processor may use state.rs to encode state into or decode the state of an account which has been passed into the entrypoint. 41 | -------------------------------------------------------------------------------- /solana/escrow/program/doc.md: -------------------------------------------------------------------------------- 1 | ### Account 2 | 1. each program is processed by its BPF Loader and has an entrypoint whose structure depends on which BPF Loader is used 3 | 2. accounts are used to store state 4 | 3. accounts are owned by programs 5 | 4. only the account owner may debit an account and adjust its data 6 | 5. all accounts to be written to or read must be passed into the entrypoint 7 | 8 | 9 | ### Program 10 | 1. developers should use the data field to save data inside accounts 11 | 2. the token program owns token accounts which - inside their data field - hold [relevant information](https://github.com/solana-labs/solana-program-library/blob/master/token/program/src/state.rs#L86) 12 | 3. the token program also owns token mint accounts with [relevant data](https://github.com/solana-labs/solana-program-library/blob/master/token/program/src/state.rs#L86) 13 | 4. each token account holds a reference to their token mint account, thereby stating which token mint they belong to 14 | 5. the token program allows the (user space) owner of a token account to transfer its ownership to another address 15 | 6. All internal Solana internal account information is saved into [fields on the account](https://docs.rs/solana-program/1.5.0/solana_program/account_info/struct.AccountInfo.html#fields) but never into the data field which is solely meant for userspace information 16 | 17 | 18 | ### Solana Sysbar 19 | Solana has sysvars that are parameters of the Solana cluster you are on. These sysvars can be accessed through accounts and store parameters such as what the current fee or rent is. As of solana-program version 1.6.5, sysvars can also be accessed without being passed into the entrypoint as an account (opens new window)(this tutorial will continue to use the old way for now, but you shouldn't!). 20 | 21 | 22 | ### Rent 23 | Rent is deducted from an accounts' balance according to their space requirements regularly. An account can, however, be made rend-exempt if its balance is higher than some threshold that depends on the space it's consuming 24 | 25 | 26 | ### Program Derived Address(PDA) & [CPI](https://docs.solana.com/developing/programming-model/calling-between-programs#cross-program-invocations) 27 | - Program Derived Addresses do not lie on the ed255519 curve and therefore have no private key associated with them. 28 | - When including a `signed` account in program call, in all CPIs including that account made by that program inside the current instruction, the account will also be `signed`. i.e, the signature is extended to the CPIs. 29 | 30 | 31 | ### Instructions 32 | - There can be several instructions(ix) inside one transactions(tx) in Solana. These ixs are executed out synchronously and the tx as a whole is executed atomically. These instructions can call different programs. 33 | - The system program is responsible for allocating account space and assigning (internal - not user space) account ownership. 34 | - Instructions may depend on previous instructions inside the same transaction. 35 | - Commitment settings give downstream developers ways to query the network which differ in finality likelihood. 36 | 37 | 38 | ### 39 | - When a program calls `invoke_signed`, the runtime uses the given seeds and the program id of the calling program to recreate the PDA and if it matches one of the given accounts inside invoke_signed's arguments, that account's signed property will be set to true. 40 | - If an account has no balance left, it will be purged from memory by the runtime after the tx. *you can see this when going to an account that has been closed in the explorer) 41 | - "closing" instructions must set the data field properly, even if the intent is to have the account be purged from memory after the tx. -------------------------------------------------------------------------------- /solana/escrow/program/scripts/patch.crates-io.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Patches the SPL crates for developing against a local solana monorepo 4 | # 5 | 6 | here="$(dirname "$0")" 7 | 8 | solana_dir=$1 9 | if [[ -z $solana_dir ]]; then 10 | echo "Usage: $0 " 11 | exit 1 12 | fi 13 | 14 | workspace_crates=( 15 | "$here"/../Cargo.toml 16 | ) 17 | 18 | if [[ ! -r "$solana_dir"/scripts/read-cargo-variable.sh ]]; then 19 | echo "$solana_dir is not a path to the solana monorepo" 20 | exit 1 21 | fi 22 | 23 | set -e 24 | 25 | solana_dir=$(cd "$solana_dir" && pwd) 26 | 27 | source "$solana_dir"/scripts/read-cargo-variable.sh 28 | solana_ver=$(readCargoVariable version "$solana_dir"/sdk/Cargo.toml) 29 | 30 | echo "Patching in $solana_ver from $solana_dir" 31 | 32 | if ! git diff --quiet && [[ -z $DIRTY_OK ]]; then 33 | echo "Error: dirty tree" 34 | exit 1 35 | fi 36 | export DIRTY_OK=1 37 | 38 | for crate in "${workspace_crates[@]}"; do 39 | if grep -q '\[patch.crates-io\]' "$crate"; then 40 | echo "* $crate is already patched" 41 | else 42 | echo "* patched $crate" 43 | cat >> "$crate" <" 11 | exit 1 12 | fi 13 | 14 | if [[ $solana_ver =~ ^v ]]; then 15 | # Drop `v` from v1.2.3... 16 | solana_ver=${solana_ver:1} 17 | fi 18 | 19 | cd "$here"/.. 20 | 21 | echo "Updating Solana version to $solana_ver in $PWD" 22 | 23 | if ! git diff --quiet && [[ -z $DIRTY_OK ]]; then 24 | echo "Error: dirty tree" 25 | exit 1 26 | fi 27 | 28 | declare tomls=() 29 | while IFS='' read -r line; do tomls+=("$line"); done < <(find . -name Cargo.toml) 30 | 31 | crates=( 32 | solana-clap-utils 33 | solana-cli-config 34 | solana-client 35 | solana-logger 36 | solana-program 37 | solana-program-test 38 | solana-remote-wallet 39 | solana-sdk 40 | solana-validator 41 | ) 42 | 43 | set -x 44 | for crate in "${crates[@]}"; do 45 | sed -i -e "s#\(${crate} = \"\).*\(\"\)#\1=$solana_ver\2#g" "${tomls[@]}" 46 | done 47 | 48 | -------------------------------------------------------------------------------- /solana/escrow/program/src/entrypoint.rs: -------------------------------------------------------------------------------- 1 | // entrypoint to the program 2 | 3 | use solana_program::{ 4 | account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, 5 | }; 6 | 7 | use crate::processor::Processor; 8 | 9 | entrypoint!(process_instruction); 10 | fn process_instruction( 11 | program_id: &Pubkey, 12 | accounts: &[AccountInfo], 13 | instruction_data: &[u8], 14 | ) -> ProgramResult { 15 | Processor::process(program_id, accounts, instruction_data) 16 | } -------------------------------------------------------------------------------- /solana/escrow/program/src/error.rs: -------------------------------------------------------------------------------- 1 | // program specific errors 2 | use thiserror::Error; 3 | 4 | use solana_program::program_error::ProgramError; 5 | 6 | #[derive(Error, Debug, Copy, Clone)] 7 | pub enum EscrowError { 8 | /// Invalid instruction 9 | #[error("Invalid Instruction")] 10 | InvalidInstruction, 11 | /// Not Rent Exempt 12 | #[error("Not Rent Exempt")] 13 | NotRentExempt, 14 | } 15 | 16 | impl From for ProgramError { 17 | fn from(e: EscrowError) -> Self { 18 | ProgramError::Custom(e as u32) 19 | } 20 | } -------------------------------------------------------------------------------- /solana/escrow/program/src/instruction.rs: -------------------------------------------------------------------------------- 1 | // program API, de(serializing) instruction data 2 | use std::convert::TryInto; 3 | use solana_program::program_error::ProgramError; 4 | 5 | use crate::error::EscrowError::InvalidInstruction; 6 | 7 | pub enum EscrowInstruction { 8 | /// Starts the trade by creating and populating an escrow account and transfering ownership of the given temp token account to the PDA 9 | /// 10 | /// 11 | /// Accounts expected; 12 | /// 13 | /// 0. `[signer]` The account of the person initializing the escrow 14 | /// 1. `[writable]` Temporary token account that should be created prior to this instruction and owned by the initializer 15 | /// 2. `[]` The initializer's token account for the token they will receive should the trade go throught 16 | /// 3. `[writable]` The escrow account, it will hold all necessary info about the trade 17 | /// 4. `[]` The rent sysvar 18 | /// 5. `[]` The token program 19 | 20 | InitEscrow { 21 | // The amount party A expects to receive of token Y 22 | amount: u64, 23 | }, 24 | 25 | /// Accepts a trade 26 | /// 27 | /// 28 | /// Accounts expected: 29 | /// 0. `[signer]` The accont of the person taking the trade 30 | /// 1. `[writable]` The taker's token account for the token they send 31 | /// 2. `[writable]` The taker's token account for the token they will receive should the trade go through 32 | /// 3. `[writable]` The PDA's temp token account to get tokens from and eventually close 33 | /// 4. `[writable]` The initializer's main account to send their rent fees to 34 | /// 5. `[writable]` The initializer's token account that will receive tokens 35 | /// 6. `[writable]` The escrow account holding the escrow info 36 | /// 7. `[]` The token program 37 | /// 8. `[]` The PDA account 38 | Exchange { 39 | /// the amount the taker expects to be paid in the other token, as a u64 because that's the max possible supply of a token 40 | amount: u64, 41 | } 42 | } 43 | 44 | impl EscrowInstruction { 45 | /// Unpacks a byte buffer into a [EscrowInstruction](enum.EscrowInstruction.html) 46 | pub fn unpack(input: &[u8]) -> Result { 47 | let (tag, rest) = input.split_first().ok_or(InvalidInstruction)?; 48 | 49 | Ok(match tag { 50 | 0 => Self::InitEscrow { 51 | amount: Self::unpack_amount(rest)?, 52 | }, 53 | 1 => Self::Exchange { 54 | amount: Self::unpack_amount(rest)? 55 | }, 56 | _ => return Err(InvalidInstruction.into()), 57 | }) 58 | } 59 | 60 | fn unpack_amount(input: &[u8]) -> Result { 61 | let amount = input 62 | .get(..8) 63 | .and_then(|slice| slice.try_into().ok()) 64 | .map(u64::from_le_bytes) 65 | .ok_or(InvalidInstruction)?; 66 | 67 | Ok(amount) 68 | } 69 | } -------------------------------------------------------------------------------- /solana/escrow/program/src/lib.rs: -------------------------------------------------------------------------------- 1 | // registering modules 2 | 3 | pub mod error; 4 | pub mod instruction; 5 | pub mod processor; 6 | pub mod state; 7 | 8 | #[cfg(not(feature = "no-entrypoint"))] 9 | pub mod entrypoint; -------------------------------------------------------------------------------- /solana/escrow/program/src/processor.rs: -------------------------------------------------------------------------------- 1 | // program logic 2 | 3 | use solana_program::{ 4 | account_info::{ next_account_info, AccountInfo }, 5 | entrypoint::ProgramResult, 6 | program_error::ProgramError, 7 | msg, 8 | pubkey::Pubkey, 9 | program::{invoke}, 10 | program_pack::{Pack, IsInitialized }, 11 | sysvar::{ rent::Rent, Sysvar }, 12 | }; 13 | 14 | use crate::{instruction::EscrowInstruction, error::EscrowError, state::Escrow}; 15 | 16 | pub struct Processor; 17 | impl Processor { 18 | // pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { 19 | 20 | pub fn process( 21 | program_id: &Pubkey, 22 | accounts: &[AccountInfo], 23 | instruction_data: &[u8] 24 | ) -> ProgramResult { 25 | let instruction = EscrowInstruction::unpack(instruction_data)?; 26 | 27 | match instruction { 28 | EscrowInstruction::InitEscrow { amount } => { 29 | msg!("Instruction: InitEscrow"); 30 | Self::process_init_escrow(accounts, amount, program_id) 31 | }, 32 | EscrowInstruction::Exchange { amount } => { 33 | msg!("Instruction: Exchange"); 34 | Self::process_exchange(accounts, amount, program_id) 35 | } 36 | } 37 | } 38 | 39 | fn process_init_escrow( 40 | accounts: &[AccountInfo], 41 | amount: u64, 42 | program_id: &Pubkey, 43 | ) -> ProgramResult { 44 | let account_info_iter = &mut accounts.iter(); 45 | let initializer = next_account_info(account_info_iter)?; 46 | 47 | if !initializer.is_signer { 48 | return Err(ProgramError::MissingRequiredSignature); 49 | } 50 | 51 | let temp_token_account = next_account_info(account_info_iter)?; 52 | 53 | let token_to_receive_account = next_account_info(account_info_iter)?; 54 | if *token_to_receive_account.owner != spl_token::id() { 55 | return Err(ProgramError::IncorrectProgramId); 56 | } 57 | 58 | let escrow_account = next_account_info(account_info_iter)?; 59 | let rent = &Rent::from_account_info( 60 | next_account_info(account_info_iter)? 61 | )?; 62 | 63 | if !rent.is_exempt(escrow_account.lamports(), escrow_account.data_len()) { 64 | return Err(EscrowError::NotRentExempt.into()); 65 | } 66 | 67 | let mut escrow_info = Escrow::unpack_unchecked(&escrow_account.data.borrow())?; 68 | if escrow_info.is_initialized() { 69 | return Err(ProgramError::AccountAlreadyInitialized); 70 | } 71 | 72 | escrow_info.is_initialized = true; 73 | escrow_info.initializer_pubkey = *initializer.key; 74 | escrow_info.temp_token_account_pubkey = *temp_token_account.key; 75 | escrow_info.initializer_token_to_receive_account_pubkey = *token_to_receive_account.key; 76 | escrow_info.expected_amount = amount; 77 | 78 | Escrow::pack( 79 | escrow_info, 80 | &mut escrow_account.try_borrow_mut_data()? 81 | )?; 82 | 83 | let (pda, _bump_seed) = Pubkey::find_program_address( 84 | &[b"escrow"], 85 | program_id 86 | ); 87 | 88 | 89 | let token_program = next_account_info(account_info_iter)?; 90 | let owner_change_ix = spl_token::instruction::set_authority( 91 | token_program.key, 92 | temp_token_account.key, 93 | Some(&pda), 94 | spl_token::instruction::AuthorityType::AccountOwner, 95 | initializer.key, 96 | &[&initializer.key], 97 | )?; 98 | 99 | msg!("Calling the token program to transfer token account ownership..."); 100 | invoke( 101 | &owner_change_ix, 102 | &[ 103 | temp_token_account.clone(), 104 | initializer.clone(), 105 | token_program.clone(), 106 | ], 107 | )?; 108 | 109 | Ok(()) 110 | } 111 | 112 | fn process_exchange( 113 | accounts: &[AccountInfo], 114 | amount_expected_by_taker: u64, 115 | program_id: &Pubkey, 116 | ) -> ProgramResult { 117 | let account_info_iter = &mut accounts.iter(); 118 | let taker = next_account_info(account_info_iter)?; 119 | 120 | if !taker.is_signer { 121 | return Err(ProgramError::MissingRequiredSignature); 122 | } 123 | 124 | let takers_sending_token_account = next_account_info(account_info_iter)?; 125 | 126 | let takers_token_to_receive_account = next_account_info(account_info_iter)?; 127 | 128 | let pdas_temp_token_account = next_account_info(account_info_iter)?; 129 | let pdas_temp_token_account_info = 130 | TokenAccount::unpack(&pdas_temp_token_account.try_borrow_data()?)?; 131 | let (pda, bump_seed) = Pubkey::find_program_address(&[b"escrow"], program_id); 132 | 133 | if amount_expected_by_taker != pdas_temp_token_account_info.amount { 134 | return Err(EscrowError::ExpectedAmountMismatch.into()); 135 | } 136 | 137 | let initializers_main_account = next_account_info(account_info_iter)?; 138 | let initializers_token_to_receive_account = next_account_info(account_info_iter)?; 139 | let escrow_account = next_account_info(account_info_iter)?; 140 | 141 | let escrow_info = Escrow::unpack(&escrow_account.try_borrow_data()?)?; 142 | 143 | if escrow_info.temp_token_account_pubkey != *pdas_temp_token_account.key { 144 | return Err(ProgramError::InvalidAccountData); 145 | } 146 | 147 | if escrow_info.initializer_pubkey != *initializers_main_account.key { 148 | return Err(ProgramError::InvalidAccountData); 149 | } 150 | 151 | if escrow_info.initializer_token_to_receive_account_pubkey != *initializers_token_to_receive_account.key { 152 | return Err(ProgramError::InvalidAccountData); 153 | } 154 | 155 | let token_program = next_account_info(account_info_iter)?; 156 | 157 | let transfer_to_initializer_ix = spl_token::instruction::transfer( 158 | token_program.key, 159 | takers_sending_token_account.key, 160 | initializers_token_to_receive_account.key, 161 | taker.key, 162 | &[&taker.key], 163 | escrow_info.expected_amount, 164 | )?; 165 | msg!("Calling the token program to transfer tokens to the escrow's initializer..."); 166 | invoke( 167 | &transfer_to_initializer_ix, 168 | &[ 169 | takers_sending_token_account.clone(), 170 | initializers_token_to_receive_account.clone(), 171 | taker.clone(), 172 | token_program.clone(), 173 | ], 174 | )?; 175 | 176 | 177 | let pda_account = next_account_info(account_info_iter)?; 178 | 179 | let transfer_to_taker_ix = spl_token::instruction::transfer( 180 | token_program.key, 181 | pdas_temp_token_account.key, 182 | takers_token_to_receive_account.key, 183 | &pda, 184 | &[&pda], 185 | pdas_temp_token_account_info.amount, 186 | )?; 187 | msg!("Calling the token program to transfer tokens to the taker..."); 188 | invoke_signed( 189 | &transfer_to_taker_ix, 190 | &[ 191 | pdas_temp_token_account.clone(), 192 | takers_token_to_receive_account.clone(), 193 | pda_account.clone(), 194 | token_program.clone(), 195 | ], 196 | &[&[&b"escrow"[..], &[bump_seed]]], 197 | )?; 198 | 199 | let close_pdas_temp_acc_ix = spl_token::instruction::close_account( 200 | token_program.key, 201 | pdas_temp_token_account.key, 202 | initializers_main_account.key, 203 | &pda, 204 | &[&pda] 205 | )?; 206 | msg!("Calling the token program to close pda's temp account..."); 207 | invoke_signed( 208 | &close_pdas_temp_acc_ix, 209 | &[ 210 | pdas_temp_token_account.clone(), 211 | initializers_main_account.clone(), 212 | pda_account.clone(), 213 | token_program.clone(), 214 | ], 215 | &[&[&b"escrow"[..], &[bump_seed]]], 216 | )?; 217 | 218 | msg!("Closing the escrow account..."); 219 | **initializers_main_account.lamports.borrow_mut() = initializers_main_account.lamports() 220 | .checked_add(escrow_account.lamports()) 221 | .ok_or(EscrowError::AmountOverflow)?; 222 | **escrow_account.lamports.borrow_mut() = 0; 223 | *escrow_account.try_borrow_mut_data()? = &mut []; 224 | 225 | Ok(()) 226 | } 227 | } -------------------------------------------------------------------------------- /solana/escrow/program/src/state.rs: -------------------------------------------------------------------------------- 1 | use solana_program::{ 2 | program_pack::{IsInitialized, Pack, Sealed}, 3 | program_error::ProgramError, 4 | pubkey::Pubkey, 5 | }; 6 | 7 | use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; 8 | 9 | pub struct Escrow { 10 | pub is_initialized: bool, 11 | pub initializer_pubkey: Pubkey, 12 | pub temp_token_account_pubkey: Pubkey, 13 | pub initializer_token_to_receive_account_pubkey: Pubkey, 14 | pub expected_amount: u64, 15 | } 16 | 17 | impl Sealed for Escrow { } 18 | 19 | impl IsInitialized for Escrow { 20 | fn is_initialized(&self) -> bool { 21 | self.is_initialized 22 | } 23 | } 24 | 25 | impl Pack for Escrow { 26 | const LEN: usize = 105; 27 | fn unpack_from_slice(src: &[u8]) -> Result { 28 | let src = array_ref![src, 0, Escrow::LEN]; 29 | let ( 30 | is_initialized, 31 | initializer_pubkey, 32 | temp_token_account_pubkey, 33 | initializer_token_to_receive_account_pubkey, 34 | expected_amount 35 | ) = array_refs![src, 1, 32, 32, 32, 8]; 36 | 37 | let is_initialized = match is_initialized { 38 | [0] => false, 39 | [1] => true, 40 | _ => return Err(ProgramError::InvalidAccountData), 41 | }; 42 | 43 | Ok(Escrow { 44 | is_initialized, 45 | initializer_pubkey: Pubkey::new_from_array(*initializer_pubkey), 46 | temp_token_account_pubkey: Pubkey::new_from_array(*temp_token_account_pubkey), 47 | initializer_token_to_receive_account_pubkey: Pubkey::new_from_array(*initializer_token_to_receive_account_pubkey), 48 | expected_amount: u64::from_le_bytes(*expected_amount), 49 | }) 50 | } 51 | 52 | fn pack_into_slice(&self, dst: &mut [u8]) { 53 | let dst = array_mut_ref![dst, 0, Escrow::LEN]; 54 | let ( 55 | is_initialized_dst, 56 | initializer_pubkey_dst, 57 | temp_token_account_pubkey_dst, 58 | initializer_token_to_receive_account_pubkey_dst, 59 | expected_amount_dst, 60 | ) = mut_array_refs![dst, 1, 32, 32, 32, 8]; 61 | 62 | let Escrow { 63 | is_initialized, 64 | initializer_pubkey, 65 | temp_token_account_pubkey, 66 | initializer_token_to_receive_account_pubkey, 67 | expected_amount 68 | } = self; 69 | 70 | is_initialized_dst[0] = *is_initialized as u8; 71 | initializer_pubkey_dst.copy_from_slice( 72 | initializer_pubkey.as_ref() 73 | ); 74 | temp_token_account_pubkey_dst.copy_from_slice( 75 | temp_token_account_pubkey.as_ref() 76 | ); 77 | initializer_token_to_receive_account_pubkey_dst.copy_from_slice( 78 | initializer_token_to_receive_account_pubkey.as_ref() 79 | ); 80 | *expected_amount_dst = expected_amount.to_le_bytes(); 81 | } 82 | } -------------------------------------------------------------------------------- /solana/helloWorld/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | *.sw[po] 3 | /.cargo 4 | /dist 5 | .env 6 | src/client/util/store 7 | test-ledger/ 8 | .DS_Store -------------------------------------------------------------------------------- /solana/helloWorld/README.md: -------------------------------------------------------------------------------- 1 | # Solana program - basic 2 | 3 | This project demonstrates how to use the [Solana Javascript 4 | API](https://github.com/solana-labs/solana-web3.js) to 5 | interact with programs on the Solana blockchain. 6 | 7 | The project comprises of: 8 | 9 | - An on-chain hello world program 10 | - A client that can send a "hello" to an account and get back the number of 11 | times "hello" has been sent 12 | 13 | ## Table of Contents 14 | 15 | - [Hello world on Solana](#hello-world-on-solana) 16 | - [Table of Contents](#table-of-contents) 17 | - [Quick Start](#quick-start) 18 | - [Configure CLI](#configure-cli) 19 | - [Start local Solana cluster](#start-local-solana-cluster) 20 | - [Install npm dependencies](#install-npm-dependencies) 21 | - [Build the on-chain program](#build-the-on-chain-program) 22 | - [Deploy the on-chain program](#deploy-the-on-chain-program) 23 | - [Run the JavaScript client](#run-the-javascript-client) 24 | - [Expected output](#expected-output) 25 | - [Not seeing the expected output?](#not-seeing-the-expected-output) 26 | - [Customizing the Program](#customizing-the-program) 27 | - [Learn about Solana](#learn-about-solana) 28 | - [Learn about the client](#learn-about-the-client) 29 | - [Entrypoint](#entrypoint) 30 | - [Establish a connection to the cluster](#establish-a-connection-to-the-cluster) 31 | - [Load the helloworld on-chain program if not already loaded](#load-the-helloworld-on-chain-program-if-not-already-loaded) 32 | - [Send a "Hello" transaction to the on-chain program](#send-a-hello-transaction-to-the-on-chain-program) 33 | - [Query the Solana account used in the "Hello" transaction](#query-the-solana-account-used-in-the-hello-transaction) 34 | - [Learn about the on-chain program](#learn-about-the-on-chain-program) 35 | - [Programming on Solana](#programming-on-Solana) 36 | - [Pointing to a public Solana cluster](#pointing-to-a-public-solana-cluster) 37 | - [Expand your skills with advanced examples](#expand-your-skills-with-advanced-examples) 38 | 39 | ## Quick Start 40 | 41 | The following dependencies are required to build and run this example, depending 42 | on your OS, they may already be installed: 43 | 44 | - Install node (v14 recommended) 45 | - Install npm 46 | - Install the latest Rust stable from https://rustup.rs/ 47 | - Install Solana v1.7.11 or later from 48 | https://docs.solana.com/cli/install-solana-cli-tools 49 | 50 | If this is your first time using Rust, these [Installation 51 | Notes](README-installation-notes.md) might be helpful. 52 | 53 | ### Configure CLI 54 | 55 | > If you're on Windows, it is recommended to use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) to run these commands 56 | 57 | 1. Set CLI config url to localhost cluster 58 | 59 | ```bash 60 | solana config set --url localhost 61 | ``` 62 | 63 | 2. Create CLI Keypair 64 | 65 | If this is your first time using the Solana CLI, you will need to generate a new keypair: 66 | 67 | ```bash 68 | solana-keygen new 69 | ``` 70 | 71 | ### Start local Solana cluster 72 | 73 | This example connects to a local Solana cluster by default. 74 | 75 | Start a local Solana cluster: 76 | 77 | ```bash 78 | solana-test-validator 79 | ``` 80 | 81 | > **Note**: You may need to do some [system tuning](https://docs.solana.com/running-validator/validator-start#system-tuning) (and restart your computer) to get the validator to run 82 | 83 | Listen to transaction logs: 84 | 85 | ```bash 86 | solana logs 87 | ``` 88 | 89 | ### Install npm dependencies 90 | 91 | ```bash 92 | npm install 93 | ``` 94 | 95 | ### Build the on-chain program 96 | 97 | There is a Rust version of the on-chain program, whichever is built 98 | last will be the one used when running the example. 99 | 100 | ```bash 101 | npm run build:program-rust 102 | ``` 103 | 104 | ### Deploy the on-chain program 105 | 106 | ```bash 107 | solana program deploy dist/program/helloworld.so 108 | ``` 109 | 110 | ### Run the JavaScript client 111 | 112 | ```bash 113 | npm run start 114 | ``` 115 | 116 | ### Expected output 117 | 118 | Public key values will differ: 119 | 120 | ```bash 121 | Let's say hello to a Solana account... 122 | Connection to cluster established: http://localhost:8899 { 'feature-set': 2045430982, 'solana-core': '1.7.8' } 123 | Using account AiT1QgeYaK86Lf9kudqKthQPCWwpG8vFA1bAAioBoF4X containing 0.00141872 SOL to pay for fees 124 | Using program Dro9uk45fxMcKWGb1eWALujbTssh6DW8mb4x8x3Eq5h6 125 | Creating account 8MBmHtJvxpKdYhdw6yPpedp6X6y2U9dCpdYaZJdmwV3A to say hello to 126 | Saying hello to 8MBmHtJvxpKdYhdw6yPpedp6X6y2U9dCpdYaZJdmwV3A 127 | 8MBmHtJvxpKdYhdw6yPpedp6X6y2U9dCpdYaZJdmwV3A has been greeted 1 times 128 | Success 129 | ``` 130 | 131 | #### Not seeing the expected output? 132 | 133 | - Ensure you've [started the local cluster](#start-local-solana-cluster), 134 | [built the on-chain program](#build-the-on-chain-program) and [deployed the program to the cluster](#deploy-the-on-chain-program). 135 | - Inspect the program logs by running `solana logs` to see why the program failed. 136 | - ```bash 137 | Transaction executed in slot 5621: 138 | Signature: 4pya5iyvNfAZj9sVWHzByrxdKB84uA5sCxLceBwr9UyuETX2QwnKg56MgBKWSM4breVRzHmpb1EZQXFPPmJnEtsJ 139 | Status: Error processing Instruction 0: Program failed to complete 140 | Log Messages: 141 | Program G5bbS1ipWzqQhekkiCLn6u7Y1jJdnGK85ceSYLx2kKbA invoke [1] 142 | Program log: Hello World Rust program entrypoint 143 | Program G5bbS1ipWzqQhekkiCLn6u7Y1jJdnGK85ceSYLx2kKbA consumed 200000 of 200000 compute units 144 | Program failed to complete: exceeded maximum number of instructions allowed (200000) at instruction #334 145 | Program G5bbS1ipWzqQhekkiCLn6u7Y1jJdnGK85ceSYLx2kKbA failed: Program failed to complete 146 | ``` 147 | 148 | ### Customizing the Program 149 | 150 | To customize the example, make changes to the files under `/src`. If you change 151 | any files under `/src/program-rust` you will need to 152 | [rebuild the on-chain program](#build-the-on-chain-program) and [redeploy the program](#deploy-the-on-chain-program). 153 | 154 | Now when you rerun `npm run start`, you should see the results of your changes. 155 | 156 | ## Learn about Solana 157 | 158 | More information about how Solana works is available in the [Solana 159 | documentation](https://docs.solana.com/) and all the source code is available on 160 | [github](https://github.com/solana-labs/solana) 161 | 162 | Further questions? Visit us on [Discord](https://discordapp.com/invite/pquxPsq) 163 | 164 | ## Learn about the client 165 | 166 | The client in this example is written in TypeScript using: 167 | 168 | - [Solana web3.js SDK](https://github.com/solana-labs/solana-web3.js) 169 | - [Solana web3 API](https://solana-labs.github.io/solana-web3.js) 170 | 171 | ### Entrypoint 172 | 173 | The [client's 174 | entrypoint](https://github.com/solana-labs/example-helloworld/blob/ad52dc719cdc96d45ad8e308e8759abf4792b667/src/client/main.ts#L13) 175 | does five things. 176 | 177 | ### Establish a connection to the cluster 178 | 179 | The client establishes a connection with the cluster by calling 180 | [`establishConnection`](https://github.com/solana-labs/example-helloworld/blob/ad52dc719cdc96d45ad8e308e8759abf4792b667/src/client/hello_world.ts#L92). 181 | 182 | ### Establish an account to pay for transactions 183 | 184 | The client ensures there is an account available to pay for transactions, 185 | and creates one if there is not, by calling 186 | [`establishPayer`](https://github.com/solana-labs/example-helloworld/blob/ad52dc719cdc96d45ad8e308e8759abf4792b667/src/client/hello_world.ts#L102). 187 | 188 | ### Check if the helloworld on-chain program has been deployed 189 | 190 | In [`checkProgram`](https://github.com/solana-labs/example-helloworld/blob/ad52dc719cdc96d45ad8e308e8759abf4792b667/src/client/hello_world.ts#L144), 191 | the client loads the keypair of the deployed program from `./dist/program/helloworld-keypair.json` and uses 192 | the public key for the keypair to fetch the program account. If the program doesn't exist, the client halts 193 | with an error. If the program does exist, it will create a new account with the program assigned as its owner 194 | to store program state (number of hello's processed). 195 | 196 | ### Send a "Hello" transaction to the on-chain program 197 | 198 | The client then constructs and sends a "Hello" transaction to the program by 199 | calling 200 | [`sayHello`](https://github.com/solana-labs/example-helloworld/blob/ad52dc719cdc96d45ad8e308e8759abf4792b667/src/client/hello_world.ts#L209). 201 | The transaction contains a single very simple instruction that primarily carries 202 | the public key of the helloworld program account to call and the "greeter" 203 | account to which the client wishes to say "Hello" to. 204 | 205 | ### Query the Solana account used in the "Hello" transaction 206 | 207 | Each time the client says "Hello" to an account, the program increments a 208 | numerical count in the "greeter" account's data. The client queries the 209 | "greeter" account's data to discover the current number of times the account has 210 | been greeted by calling 211 | [`reportGreetings`](https://github.com/solana-labs/example-helloworld/blob/ad52dc719cdc96d45ad8e308e8759abf4792b667/src/client/hello_world.ts#L226). 212 | 213 | ## Learn about the on-chain program 214 | 215 | The [on-chain helloworld program](/src/program-rust/Cargo.toml) is a Rust program 216 | compiled to [Berkeley Packet Filter 217 | (BPF)](https://en.wikipedia.org/wiki/Berkeley_Packet_Filter) bytecode and stored as an 218 | [Executable and Linkable Format (ELF) shared 219 | object](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format). 220 | 221 | The program is written using: 222 | 223 | - [Solana Rust SDK](https://github.com/solana-labs/solana/tree/master/sdk) 224 | 225 | ### Programming on Solana 226 | 227 | To learn more about Solana programming model refer to the [Programming Model 228 | Overview](https://docs.solana.com/developing/programming-model/overview). 229 | 230 | To learn more about developing programs on Solana refer to the [On-Chain 231 | Programs Overview](https://docs.solana.com/developing/on-chain-programs/overview) 232 | 233 | ## Pointing to a public Solana cluster 234 | 235 | Solana maintains three public clusters: 236 | 237 | - `devnet` - Development cluster with airdrops enabled 238 | - `testnet` - Tour De Sol test cluster without airdrops enabled 239 | - `mainnet-beta` - Main cluster 240 | 241 | Use the Solana CLI to configure which cluster to connect to. 242 | 243 | To point to `devnet`: 244 | 245 | ```bash 246 | solana config set --url devnet 247 | ``` 248 | 249 | To point back to the local cluster: 250 | 251 | ```bash 252 | solana config set --url localhost 253 | ``` 254 | 255 | ## Expand your skills with advanced examples 256 | 257 | There is lots more to learn; The following examples demonstrate more advanced 258 | features like custom errors, advanced account handling, suggestions for data 259 | serialization, benchmarking, etc... 260 | 261 | - [Programming 262 | Examples](https://github.com/solana-labs/solana-program-library/tree/master/examples) 263 | - [Token 264 | Program](https://github.com/solana-labs/solana-program-library/tree/master/token) 265 | - [Token Swap 266 | Program](https://github.com/solana-labs/solana-program-library/tree/master/token-swap) 267 | -------------------------------------------------------------------------------- /solana/helloWorld/cluster-devnet.env: -------------------------------------------------------------------------------- 1 | LIVE=1 2 | CLUSTER=devnet 3 | -------------------------------------------------------------------------------- /solana/helloWorld/cluster-mainnet-beta.env: -------------------------------------------------------------------------------- 1 | LIVE=1 2 | CLUSTER=mainnet-beta 3 | -------------------------------------------------------------------------------- /solana/helloWorld/cluster-testnet.env: -------------------------------------------------------------------------------- 1 | LIVE=1 2 | CLUSTER=testnet 3 | -------------------------------------------------------------------------------- /solana/helloWorld/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solana-basic", 3 | "version": "0.0.1", 4 | "description": "", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/phanlancer/solana-basic" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT", 12 | "scripts": { 13 | "start": "ts-node src/client/main.ts", 14 | "start-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health start", 15 | "lint": "eslint --ext .ts src/client/* && prettier --check \"src/client/**/*.ts\"", 16 | "lint:fix": "eslint --ext .ts src/client/* --fix && prettier --write \"src/client/**/*.ts\"", 17 | "clean": "npm run clean:program-c && npm run clean:program-rust", 18 | "build:program-rust": "cargo build-bpf --manifest-path=./src/program-rust/Cargo.toml --bpf-out-dir=dist/program", 19 | "clean:program-rust": "cargo clean --manifest-path=./src/program-rust/Cargo.toml && rm -rf ./dist", 20 | "test:program-rust": "cargo test-bpf --manifest-path=./src/program-rust/Cargo.toml", 21 | "pretty": "prettier --write '{,src/**/}*.ts'" 22 | }, 23 | "dependencies": { 24 | "@solana/web3.js": "^1.7.0", 25 | "borsh": "^0.6.0", 26 | "mz": "^2.7.0", 27 | "yaml": "^1.10.2" 28 | }, 29 | "devDependencies": { 30 | "@tsconfig/recommended": "^1.0.1", 31 | "@types/eslint": "^7.2.4", 32 | "@types/eslint-plugin-prettier": "^3.1.0", 33 | "@types/mz": "^2.7.2", 34 | "@types/prettier": "^2.1.5", 35 | "@types/yaml": "^1.9.7", 36 | "@typescript-eslint/eslint-plugin": "^4.6.0", 37 | "@typescript-eslint/parser": "^4.6.0", 38 | "eslint": "^7.12.1", 39 | "eslint-config-prettier": "^6.15.0", 40 | "eslint-plugin-prettier": "^4.0.0", 41 | "prettier": "^2.1.2", 42 | "start-server-and-test": "^1.11.6", 43 | "ts-node": "^10.0.0", 44 | "typescript": "^4.0.5" 45 | }, 46 | "engines": { 47 | "node": ">=14.0.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /solana/helloWorld/src/client/hello_world.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 3 | 4 | import { 5 | Keypair, 6 | Connection, 7 | PublicKey, 8 | LAMPORTS_PER_SOL, 9 | SystemProgram, 10 | TransactionInstruction, 11 | Transaction, 12 | sendAndConfirmTransaction, 13 | } from '@solana/web3.js'; 14 | import fs from 'mz/fs'; 15 | import path from 'path'; 16 | import * as borsh from 'borsh'; 17 | 18 | import {getPayer, getRpcUrl, createKeypairFromFile} from './utils'; 19 | 20 | /** 21 | * Connection to the network 22 | */ 23 | let connection: Connection; 24 | 25 | /** 26 | * Keypair associated to the fees' payer 27 | */ 28 | let payer: Keypair; 29 | 30 | /** 31 | * Hello world's program id 32 | */ 33 | let programId: PublicKey; 34 | 35 | /** 36 | * The public key of the account we are saying hello to 37 | */ 38 | let greetedPubkey: PublicKey; 39 | 40 | /** 41 | * Path to program files 42 | */ 43 | const PROGRAM_PATH = path.resolve(__dirname, '../../dist/program'); 44 | 45 | /** 46 | * Path to program shared object file which should be deployed on chain. 47 | * This file is created when running either: 48 | * - `npm run build:program-c` 49 | * - `npm run build:program-rust` 50 | */ 51 | const PROGRAM_SO_PATH = path.join(PROGRAM_PATH, 'helloworld.so'); 52 | 53 | /** 54 | * Path to the keypair of the deployed program. 55 | * This file is created when running `solana program deploy dist/program/helloworld.so` 56 | */ 57 | const PROGRAM_KEYPAIR_PATH = path.join(PROGRAM_PATH, 'helloworld-keypair.json'); 58 | 59 | /** 60 | * The state of a greeting account managed by the hello world program 61 | */ 62 | class GreetingAccount { 63 | counter = 0; 64 | constructor(fields: {counter: number} | undefined = undefined) { 65 | if (fields) { 66 | this.counter = fields.counter; 67 | } 68 | } 69 | } 70 | 71 | /** 72 | * Borsh schema definition for greeting accounts 73 | */ 74 | const GreetingSchema = new Map([ 75 | [GreetingAccount, {kind: 'struct', fields: [['counter', 'u32']]}], 76 | ]); 77 | 78 | /** 79 | * The expected size of each greeting account. 80 | */ 81 | const GREETING_SIZE = borsh.serialize( 82 | GreetingSchema, 83 | new GreetingAccount(), 84 | ).length; 85 | 86 | /** 87 | * Establish a connection to the cluster 88 | */ 89 | export async function establishConnection(): Promise { 90 | const rpcUrl = await getRpcUrl(); 91 | connection = new Connection(rpcUrl, 'confirmed'); 92 | const version = await connection.getVersion(); 93 | console.log('Connection to cluster established:', rpcUrl, version); 94 | } 95 | 96 | /** 97 | * Establish an account to pay for everything 98 | */ 99 | export async function establishPayer(): Promise { 100 | let fees = 0; 101 | if (!payer) { 102 | const {feeCalculator} = await connection.getRecentBlockhash(); 103 | 104 | // Calculate the cost to fund the greeter account 105 | fees += await connection.getMinimumBalanceForRentExemption(GREETING_SIZE); 106 | 107 | // Calculate the cost of sending transactions 108 | fees += feeCalculator.lamportsPerSignature * 100; // wag 109 | 110 | payer = await getPayer(); 111 | } 112 | 113 | let lamports = await connection.getBalance(payer.publicKey); 114 | if (lamports < fees) { 115 | // If current balance is not enough to pay for fees, request an airdrop 116 | const sig = await connection.requestAirdrop( 117 | payer.publicKey, 118 | fees - lamports, 119 | ); 120 | await connection.confirmTransaction(sig); 121 | lamports = await connection.getBalance(payer.publicKey); 122 | } 123 | 124 | console.log( 125 | 'Using account', 126 | payer.publicKey.toBase58(), 127 | 'containing', 128 | lamports / LAMPORTS_PER_SOL, 129 | 'SOL to pay for fees', 130 | ); 131 | } 132 | 133 | /** 134 | * Check if the hello world BPF program has been deployed 135 | */ 136 | export async function checkProgram(): Promise { 137 | // Read program id from keypair file 138 | try { 139 | const programKeypair = await createKeypairFromFile(PROGRAM_KEYPAIR_PATH); 140 | programId = programKeypair.publicKey; 141 | } catch (err) { 142 | const errMsg = (err as Error).message; 143 | throw new Error( 144 | `Failed to read program keypair at '${PROGRAM_KEYPAIR_PATH}' due to error: ${errMsg}. Program may need to be deployed with \`solana program deploy dist/program/helloworld.so\``, 145 | ); 146 | } 147 | 148 | // Check if the program has been deployed 149 | const programInfo = await connection.getAccountInfo(programId); 150 | if (programInfo === null) { 151 | if (fs.existsSync(PROGRAM_SO_PATH)) { 152 | throw new Error( 153 | 'Program needs to be deployed with `solana program deploy dist/program/helloworld.so`', 154 | ); 155 | } else { 156 | throw new Error('Program needs to be built and deployed'); 157 | } 158 | } else if (!programInfo.executable) { 159 | throw new Error(`Program is not executable`); 160 | } 161 | console.log(`Using program ${programId.toBase58()}`); 162 | 163 | // Derive the address (public key) of a greeting account from the program so that it's easy to find later. 164 | const GREETING_SEED = 'hello'; 165 | greetedPubkey = await PublicKey.createWithSeed( 166 | payer.publicKey, 167 | GREETING_SEED, 168 | programId, 169 | ); 170 | 171 | // Check if the greeting account has already been created 172 | const greetedAccount = await connection.getAccountInfo(greetedPubkey); 173 | if (greetedAccount === null) { 174 | console.log( 175 | 'Creating account', 176 | greetedPubkey.toBase58(), 177 | 'to say hello to', 178 | ); 179 | const lamports = await connection.getMinimumBalanceForRentExemption( 180 | GREETING_SIZE, 181 | ); 182 | 183 | const transaction = new Transaction().add( 184 | SystemProgram.createAccountWithSeed({ 185 | fromPubkey: payer.publicKey, 186 | basePubkey: payer.publicKey, 187 | seed: GREETING_SEED, 188 | newAccountPubkey: greetedPubkey, 189 | lamports, 190 | space: GREETING_SIZE, 191 | programId, 192 | }), 193 | ); 194 | await sendAndConfirmTransaction(connection, transaction, [payer]); 195 | } 196 | } 197 | 198 | /** 199 | * Say hello 200 | */ 201 | export async function sayHello(): Promise { 202 | console.log('Saying hello to', greetedPubkey.toBase58()); 203 | const instruction = new TransactionInstruction({ 204 | keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}], 205 | programId, 206 | data: Buffer.alloc(0), // All instructions are hellos 207 | }); 208 | await sendAndConfirmTransaction( 209 | connection, 210 | new Transaction().add(instruction), 211 | [payer], 212 | ); 213 | } 214 | 215 | /** 216 | * Report the number of times the greeted account has been said hello to 217 | */ 218 | export async function reportGreetings(): Promise { 219 | const accountInfo = await connection.getAccountInfo(greetedPubkey); 220 | if (accountInfo === null) { 221 | throw 'Error: cannot find the greeted account'; 222 | } 223 | const greeting = borsh.deserialize( 224 | GreetingSchema, 225 | GreetingAccount, 226 | accountInfo.data, 227 | ); 228 | console.log( 229 | greetedPubkey.toBase58(), 230 | 'has been greeted', 231 | greeting.counter, 232 | 'time(s)', 233 | ); 234 | } 235 | -------------------------------------------------------------------------------- /solana/helloWorld/src/client/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Hello world 3 | */ 4 | 5 | import { 6 | establishConnection, 7 | establishPayer, 8 | checkProgram, 9 | sayHello, 10 | reportGreetings, 11 | } from './hello_world'; 12 | 13 | async function main() { 14 | console.log("Let's say hello to a Solana account..."); 15 | 16 | // Establish connection to the cluster 17 | await establishConnection(); 18 | 19 | // Determine who pays for the fees 20 | await establishPayer(); 21 | 22 | // Check if the program has been deployed 23 | await checkProgram(); 24 | 25 | // Say hello to an account 26 | await sayHello(); 27 | 28 | // Find out how many times that account has been greeted 29 | await reportGreetings(); 30 | 31 | console.log('Success'); 32 | } 33 | 34 | main().then( 35 | () => process.exit(), 36 | err => { 37 | console.error(err); 38 | process.exit(-1); 39 | }, 40 | ); 41 | -------------------------------------------------------------------------------- /solana/helloWorld/src/client/utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 3 | /* eslint-disable @typescript-eslint/no-unsafe-return */ 4 | 5 | import os from 'os'; 6 | import fs from 'mz/fs'; 7 | import path from 'path'; 8 | import yaml from 'yaml'; 9 | import {Keypair} from '@solana/web3.js'; 10 | 11 | /** 12 | * @private 13 | */ 14 | async function getConfig(): Promise { 15 | // Path to Solana CLI config file 16 | const CONFIG_FILE_PATH = path.resolve( 17 | os.homedir(), 18 | '.config', 19 | 'solana', 20 | 'cli', 21 | 'config.yml', 22 | ); 23 | const configYml = await fs.readFile(CONFIG_FILE_PATH, {encoding: 'utf8'}); 24 | return yaml.parse(configYml); 25 | } 26 | 27 | /** 28 | * Load and parse the Solana CLI config file to determine which RPC url to use 29 | */ 30 | export async function getRpcUrl(): Promise { 31 | try { 32 | const config = await getConfig(); 33 | if (!config.json_rpc_url) throw new Error('Missing RPC URL'); 34 | return config.json_rpc_url; 35 | } catch (err) { 36 | console.warn( 37 | 'Failed to read RPC url from CLI config file, falling back to localhost', 38 | ); 39 | return 'http://localhost:8899'; 40 | } 41 | } 42 | 43 | /** 44 | * Load and parse the Solana CLI config file to determine which payer to use 45 | */ 46 | export async function getPayer(): Promise { 47 | try { 48 | const config = await getConfig(); 49 | if (!config.keypair_path) throw new Error('Missing keypair path'); 50 | return await createKeypairFromFile(config.keypair_path); 51 | } catch (err) { 52 | console.warn( 53 | 'Failed to create keypair from CLI config file, falling back to new random keypair', 54 | ); 55 | return Keypair.generate(); 56 | } 57 | } 58 | 59 | /** 60 | * Create a Keypair from a secret key stored in file as bytes' array 61 | */ 62 | export async function createKeypairFromFile( 63 | filePath: string, 64 | ): Promise { 65 | const secretKeyString = await fs.readFile(filePath, {encoding: 'utf8'}); 66 | const secretKey = Uint8Array.from(JSON.parse(secretKeyString)); 67 | return Keypair.fromSecretKey(secretKey); 68 | } 69 | -------------------------------------------------------------------------------- /solana/helloWorld/src/program-rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /solana/helloWorld/src/program-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "solana-basic" 4 | version = "1.0.0" 5 | description = "basic solana program written in Rust" 6 | authors = ["commonlot"] 7 | repository = "https://github.com/phanlancer/solana-basic" 8 | license = "Apache-2.0" 9 | homepage = "https://phanlancerduk.com/" 10 | edition = "2018" 11 | 12 | [features] 13 | no-entrypoint = [] 14 | 15 | [dependencies] 16 | borsh = "0.9.1" 17 | borsh-derive = "0.9.1" 18 | solana-program = "=1.7.9" 19 | 20 | [dev-dependencies] 21 | solana-program-test = "=1.7.9" 22 | solana-sdk = "=1.7.9" 23 | 24 | [lib] 25 | name = "helloworld" 26 | crate-type = ["cdylib", "lib"] 27 | -------------------------------------------------------------------------------- /solana/helloWorld/src/program-rust/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] -------------------------------------------------------------------------------- /solana/helloWorld/src/program-rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | use borsh::{BorshDeserialize, BorshSerialize}; 2 | use solana_program::{ 3 | account_info::{next_account_info, AccountInfo}, 4 | entrypoint, 5 | entrypoint::ProgramResult, 6 | msg, 7 | program_error::ProgramError, 8 | pubkey::Pubkey, 9 | }; 10 | 11 | /// Define the type of state stored in accounts 12 | #[derive(BorshSerialize, BorshDeserialize, Debug)] 13 | pub struct GreetingAccount { 14 | /// number of greetings 15 | pub counter: u32, 16 | } 17 | 18 | // Declare and export the program's entrypoint 19 | entrypoint!(process_instruction); 20 | 21 | // Program entrypoint's implementation 22 | pub fn process_instruction( 23 | program_id: &Pubkey, // Public key of the account the hello world program was loaded into 24 | accounts: &[AccountInfo], // The account to say hello to 25 | _instruction_data: &[u8], // Ignored, all helloworld instructions are hellos 26 | ) -> ProgramResult { 27 | msg!("Hello World Rust program entrypoint"); 28 | 29 | // Iterating accounts is safer then indexing 30 | let accounts_iter = &mut accounts.iter(); 31 | 32 | // Get the account to say hello to 33 | let account = next_account_info(accounts_iter)?; 34 | 35 | // The account must be owned by the program in order to modify its data 36 | if account.owner != program_id { 37 | msg!("Greeted account does not have the correct program id"); 38 | return Err(ProgramError::IncorrectProgramId); 39 | } 40 | 41 | // Increment and store the number of times the account has been greeted 42 | let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?; 43 | greeting_account.counter += 1; 44 | greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?; 45 | 46 | msg!("Greeted {} time(s)!", greeting_account.counter); 47 | 48 | Ok(()) 49 | } 50 | 51 | -------------------------------------------------------------------------------- /solana/helloWorld/src/program-rust/tests/lib.rs: -------------------------------------------------------------------------------- 1 | use borsh::BorshDeserialize; 2 | use helloworld::{process_instruction, GreetingAccount}; 3 | use solana_program_test::*; 4 | use solana_sdk::{ 5 | account::Account, 6 | instruction::{AccountMeta, Instruction}, 7 | pubkey::Pubkey, 8 | signature::Signer, 9 | transaction::Transaction, 10 | }; 11 | use std::mem; 12 | 13 | #[tokio::test] 14 | async fn test_helloworld() { 15 | let program_id = Pubkey::new_unique(); 16 | let greeted_pubkey = Pubkey::new_unique(); 17 | 18 | let mut program_test = ProgramTest::new( 19 | "helloworld", // Run the BPF version with `cargo test-bpf` 20 | program_id, 21 | processor!(process_instruction), // Run the native version with `cargo test` 22 | ); 23 | program_test.add_account( 24 | greeted_pubkey, 25 | Account { 26 | lamports: 5, 27 | data: vec![0_u8; mem::size_of::()], 28 | owner: program_id, 29 | ..Account::default() 30 | }, 31 | ); 32 | let (mut banks_client, payer, recent_blockhash) = program_test.start().await; 33 | 34 | // Verify account has zero greetings 35 | let greeted_account = banks_client 36 | .get_account(greeted_pubkey) 37 | .await 38 | .expect("get_account") 39 | .expect("greeted_account not found"); 40 | assert_eq!( 41 | GreetingAccount::try_from_slice(&greeted_account.data) 42 | .unwrap() 43 | .counter, 44 | 0 45 | ); 46 | 47 | // Greet once 48 | let mut transaction = Transaction::new_with_payer( 49 | &[Instruction::new_with_bincode( 50 | program_id, 51 | &[0], // ignored but makes the instruction unique in the slot 52 | vec![AccountMeta::new(greeted_pubkey, false)], 53 | )], 54 | Some(&payer.pubkey()), 55 | ); 56 | transaction.sign(&[&payer], recent_blockhash); 57 | banks_client.process_transaction(transaction).await.unwrap(); 58 | 59 | // Verify account has one greeting 60 | let greeted_account = banks_client 61 | .get_account(greeted_pubkey) 62 | .await 63 | .expect("get_account") 64 | .expect("greeted_account not found"); 65 | assert_eq!( 66 | GreetingAccount::try_from_slice(&greeted_account.data) 67 | .unwrap() 68 | .counter, 69 | 1 70 | ); 71 | 72 | // Greet again 73 | let mut transaction = Transaction::new_with_payer( 74 | &[Instruction::new_with_bincode( 75 | program_id, 76 | &[1], // ignored but makes the instruction unique in the slot 77 | vec![AccountMeta::new(greeted_pubkey, false)], 78 | )], 79 | Some(&payer.pubkey()), 80 | ); 81 | transaction.sign(&[&payer], recent_blockhash); 82 | banks_client.process_transaction(transaction).await.unwrap(); 83 | 84 | // Verify account has two greetings 85 | let greeted_account = banks_client 86 | .get_account(greeted_pubkey) 87 | .await 88 | .expect("get_account") 89 | .expect("greeted_account not found"); 90 | assert_eq!( 91 | GreetingAccount::try_from_slice(&greeted_account.data) 92 | .unwrap() 93 | .counter, 94 | 2 95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /solana/helloWorld/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/recommended/tsconfig.json", 3 | "ts-node": { 4 | "compilerOptions": { 5 | "module": "commonjs" 6 | } 7 | }, 8 | "compilerOptions": { 9 | "declaration": true, 10 | "moduleResolution": "node", 11 | "module": "es2015" 12 | }, 13 | "include": ["src/client/**/*"], 14 | "exclude": ["node_modules"] 15 | } 16 | --------------------------------------------------------------------------------