Escrow program implemented in Anchor

Overview

Anchor Example: Escrow Program

  • See this doc for more implementation details

Overview

Since this program is extended from the original Escrow Program, I assumed you have gone through the original blog post at least once.

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.

Initialize

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.

Cancel

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.

Exchange

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.

Build, Deploy and Test

Let's run the test once to see what happens.

First, install dependencies:

$ npm install

$ npm install -g mocha

Make sure you have your local solana validator running if you want to deploy the program locally:

$ solana-test-validator

If you are on Apple Sillicon M1 chip, you will have to build Solana from the source. See this document for more details

Next, we will build and deploy the program via Anchor.

First, let's build the program:

$ anchor build

Deploy the program:

$ anchor deploy

Finally, run the test:

$ anchor test

Make sure to terminate the solana-test-validator before you run the test command

Comments
  • custom program error: 0x0

    custom program error: 0x0

    Hello, when i run anchor test, it fails initializing escrow account with error "custom program error: 0x0". afaik, it means one account lack sols. But i don't know which one... I have enough SOL in the payer account for sure. I even have SOL in escrow account... WHat could it be ? Anyone has an idea ?

    opened by easydeveloppement 5
  • Need some help

    Need some help

    Hello i'm new to anchor, and I would like to understand something.

    The Vault account key & authority seeds are constant, so only 1 account can be create for this program ? I'm trying to create multiple vault from the same Initializer and for the same Token, so the way to do it would be to pass an UUID as seed ?

    Thank you

    opened by aneopsy 5
  • Failing test

    Failing test

    Before anything, I would like to appreciate this code and the article related.

    I'm following line by line the article but I'm getting this error when running the second test Initialize program state

     anchor-escrow
        ✔ Initialize program state (5011ms)
    Transaction simulation failed: Error processing Instruction 1: custom program error: 0xbc4
        Program 11111111111111111111111111111111 invoke [1]
        Program 11111111111111111111111111111111 success
        Program CL5TkCxmuTq47U2WGtTGhp3rPEVsYgDewXUyRJfqACzK invoke [1]
        Program log: Instruction: Initialize
        Program log: Custom program error: 0xbc4
        Program CL5TkCxmuTq47U2WGtTGhp3rPEVsYgDewXUyRJfqACzK consumed 6964 of 200000 compute units
        Program CL5TkCxmuTq47U2WGtTGhp3rPEVsYgDewXUyRJfqACzK failed: custom program error: 0xbc4
        1) Initialize escrow
        ✔ Exchange escrow state
        ✔ Initialize escrow and cancel escrow
    
    
      3 passing (5s)
      1 failing
    
      1) anchor-escrow
           Initialize escrow:
         Error: 3012: The program expected this account to be already initialized
          at Function.parse (node_modules/@project-serum/anchor/src/error.ts:41:14)
          at Object.rpc [as initialize] (node_modules/@project-serum/anchor/src/program/namespace/rpc.ts:32:42)
          at processTicksAndRejections (internal/process/task_queues.js:95:5)
    

    Any help would be much appreciated.

    opened by teseo 4
  • NFT purchase ?

    NFT purchase ?

    Hi ! I would like to write a rust contract for a NFT purchase action, using this example as a base. My questions :

    1. For the NFT's royalties, do we need to code it manually or is it implicit in an instruction ?
    2. If i understand well, instead of using spl_token::instruction::transfer for transfering Tokens, there is another instruction to be used to transfer SOL ? (or should i use SOL as an SPL Token ?) Thanks !
    opened by easydeveloppement 4
  • Bump to Anchor v0.24.2

    Bump to Anchor v0.24.2

    Hi @ironaddicteddog, Anchor recently identified a security issue which was fixed in Anchor v0.24.2 and using a version lower than that will cause this error in Cargo.toml

    failed to select a version for the requirement anchor-lang = "^0.22.0" candidate versions found which didn't match: 0.24.2 location searched: crates.io index

    The current escrow program is at Anchor v0.22.0 and it's best to bump to Anchor v0.24.2.

    Can make a PR if you want.

    opened by 0xcatrovacer 2
  • Deploying and Initializing anchor-escrow contract on devnet

    Deploying and Initializing anchor-escrow contract on devnet

    hi @ironaddicteddog, this is more of a question than an actual issue I want to use the contract to exchange tokens. My plan is as follows, however I'm unsuccessful initializing the contract.

    logs: [
        'Program 11111111111111111111111111111111 invoke [1]',
        'Create Account: account Address { address: 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ, base: None } already in use',
        'Program 11111111111111111111111111111111 failed: custom program error: 0x0'
      ]
    
    

    Note: 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ is the contract address in the lib.rs

    1. Deploy program ✅ 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ
    2. Create Token A DhS6x9pTrfCeY8iwkRdGstxUuphbeHqddT2vZSWRw3d2 and Token B HpdLmjjxD8YZav2S1aastqyLsKDUb1ToLDfq4hPsLBoc
    3. Create token accounts Token A account Cj6cMp4xfAaCFegMg9G7GQaZWqYbQqgmu7Vjd4BbGYHh, Token B account HpdLmjjxD8YZav2S1aastqyLsKDUb1ToLDfq4hPsLBoc
    4. Initialize contract ❌.
    5. Exchange tokens

    I've written following script to initialize the contract.

    import * as anchor from '@project-serum/anchor';
    import { PublicKey, SystemProgram, Transaction, Connection, Commitment } from '@solana/web3.js';
    import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token";
    import { 
        escrowAccount, 
        initializerMainAccount, 
        initializerTokenAccountA, 
        initializerTokenAccountB, 
        mintAPublicKey, 
        mintBPublicKey } from './accounts'
    import NodeWallet from '@project-serum/anchor/dist/cjs/nodewallet';
    
    const takerAmount = 1000;
    const initializerAmount = 500;
    
    const commitment: Commitment = 'processed';
    const connection = new Connection('https://api.devnet.solana.com', { commitment, wsEndpoint: 'wss://api.devnet.solana.com/' });
    // const connection = new Connection('http://127.0.0.1:8899', { commitment, wsEndpoint: 'wss://127.0.0.1:8899/' });
    const options = anchor.Provider.defaultOptions();
    const wallet = new NodeWallet(initializerMainAccount);
    const provider = new anchor.Provider(connection, wallet, options);
    
    anchor.setProvider(provider);
    // Read the generated IDL.
    const idl = JSON.parse(
        require("fs").readFileSync("./tests/keypair/anchor_escrow.json", "utf8")
      );
      
    // Address of the deployed program.
    const programId = new anchor.web3.PublicKey("2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ");
    
    // Generate the program client from IDL.
    const program = new anchor.Program(idl, programId);
      
    const initEscrow = async () => {
        const [_vault_account_pda, _vault_account_bump] = await PublicKey.findProgramAddress(
            [Buffer.from(anchor.utils.bytes.utf8.encode("token-seed"))],
            program.programId,
        );
        const vault_account_pda = _vault_account_pda;
        const vault_account_bump = _vault_account_bump;
    
        const [_vault_authority_pda, _vault_authority_bump] = await PublicKey.findProgramAddress(
            [Buffer.from(anchor.utils.bytes.utf8.encode("escrow"))],
            program.programId,
        );
        // DEBUG BEGIN
        // console.info(`initializerMainAccount: ` + JSON.stringify(initializerMainAccount, null, 2));
        // console.info(`Escrow account: ` + JSON.stringify(escrowAccount));
        console.info(`Mint A: ` + mintAPublicKey.toBase58());
        console.info(`Mint B: ` + mintBPublicKey.toBase58());
        console.info(`TOKEN_PROGRAM_ID: ` + TOKEN_PROGRAM_ID);
        console.info(`SYSVAR_RENT_PUBKEY: ` + anchor.web3.SYSVAR_RENT_PUBKEY);
    
        // DEBUG CONSOLE END
        await program.rpc.initialize(
            vault_account_bump,
            new anchor.BN(initializerAmount),
            new anchor.BN(takerAmount),
            {
                accounts: {
                    initializer: initializerMainAccount.publicKey,
                    mint: mintAPublicKey,
                    vaultAccount: vault_account_pda,
                    initializerDepositTokenAccount: initializerTokenAccountA,
                    initializerReceiveTokenAccount: initializerTokenAccountB,
                    escrowAccount: escrowAccount.publicKey,
                    systemProgram: anchor.web3.SystemProgram.programId,
                    rent: anchor.web3.SYSVAR_RENT_PUBKEY,
                    tokenProgram: TOKEN_PROGRAM_ID,
                },
                instructions: [
                    await program.account.escrowAccount.createInstruction(escrowAccount),
                ],
                signers: [escrowAccount, initializerMainAccount],
            }
        );
    }
    
    initEscrow();
    

    Long version of the error output is

    ➜  anchor-escrow git:(master) ✗ ts-node tests/init2.ts
    Mint A: DhS6x9pTrfCeY8iwkRdGstxUuphbeHqddT2vZSWRw3d2
    Mint B: HpdLmjjxD8YZav2S1aastqyLsKDUb1ToLDfq4hPsLBoc
    TOKEN_PROGRAM_ID: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
    SYSVAR_RENT_PUBKEY: SysvarRent111111111111111111111111111111111
    Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0 
        Program 11111111111111111111111111111111 invoke [1]
        Create Account: account Address { address: 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ, base: None } already in use
        Program 11111111111111111111111111111111 failed: custom program error: 0x0
    /Users/tuncatunc/git/anchor-escrow/node_modules/@solana/web3.js/src/connection.ts:3961
          throw new SendTransactionError(
                ^
    SendTransactionError: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0
        at Connection.sendEncodedTransaction (/Users/tuncatunc/git/anchor-escrow/node_modules/@solana/web3.js/src/connection.ts:3961:13)
        at processTicksAndRejections (node:internal/process/task_queues:96:5)
        at async Connection.sendRawTransaction (/Users/tuncatunc/git/anchor-escrow/node_modules/@solana/web3.js/src/connection.ts:3918:20)
        at async sendAndConfirmRawTransaction (/Users/tuncatunc/git/anchor-escrow/node_modules/@solana/web3.js/src/util/send-and-confirm-raw-transaction.ts:27:21)
        at async Provider.send (/Users/tuncatunc/git/anchor-escrow/node_modules/@project-serum/anchor/src/provider.ts:118:18)
        at async Object.rpc [as initialize] (/Users/tuncatunc/git/anchor-escrow/node_modules/@project-serum/anchor/src/program/namespace/rpc.ts:25:23) {
      logs: [
        'Program 11111111111111111111111111111111 invoke [1]',
        'Create Account: account Address { address: 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ, base: None } already in use',
        'Program 11111111111111111111111111111111 failed: custom program error: 0x0'
      ]
    }
    
    

    This is a long post Thx a lot in advance for writing this contract to guide solana contract devs.

    BR

    opened by tuncatunc 2
  • Failing Test Cases - Initialize Program State

    Failing Test Cases - Initialize Program State

    Hi - Thank you for the Anchor Escrow article. I was able to build the program successfully but am failing all the test cases. The first test initialize program state fails with this error "Transaction simulation failed: Attempt to debit an account but found no record of a prior credit." I am attempting to run this on devnet with anchor-cli 0.20.1

    opened by maitreya2048 2
  • anchor test is not working.

    anchor test is not working.

    I tried to test this smart contract using "anchor test" command.

      /home/remote3/development/solana/anchor-escrow/target/deploy/anchor_escrow-keypair.json
    
    Found a 'test' script in the Anchor.toml. Running it as a test suite!
    
    Running test suite: "/home/remote3/development/solana/anchor-escrow/Anchor.toml"
    
    Unable to get latest blockhash. Test validator does not look started. Check .anchor/test-ledger/test-ledger-log.txt for errors. Consider increasing [test.startup_wait] in Anchor.toml.
    
    

    I am using anchor-0.25.0.

    Please help me.

    opened by nightfury1111111111 1
  • Multiple exchange transactions

    Multiple exchange transactions

    Hi - This is more of a question than an issue. If I wanted to start multiple such exchanges as described in your blog, I am assuming multiple Escrow accounts and vault accounts would be created. Does this statement lend to creating multiple vault accounts?

    let (vault_authority, _vault_authority_bump) = Pubkey::find_program_address(&[ESCROW_PDA_SEED], ctx.program_id);

    Since the ESCROW_PDA_SEED is fixed as "escrow", and the program_id is fixed, would it connect to the same vault in all such exchanges? Something like this - seeds = [b"escrow", user.key().as_ref()]

    Do we need to add, lets say the first user as part of the seed, so it creates and finds those vault accounts specific to those users when called?

    opened by maitreya2048 1
  • Error with anchor 0.21.0:

    Error with anchor 0.21.0: "bump targets should not be provided with init"

    When running anchor test:

    thread 'main' panicked at 'Code not parseable: Error("bump targets should not be provided with init. Please use bump without a target.")', lang/syn/src/idl/file.rs:353:58
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    

    Seems due to the breaking changes: https://github.com/project-serum/anchor/blob/master/CHANGELOG.md

    opened by aaronvg 1
  • Calling the script from JS

    Calling the script from JS

    Hello, I'm able to run the script with CLI. But i'm not able to find a way to call the program with classic javascript, could we find somewhere an example of that ? Thank you in advance !

    opened by easydeveloppement 1
  • Set Authority for Vault Account

    Set Authority for Vault Account

    Hello, I'm working on a separate (non-escrow) project, and been using this as a reference.

    I am facing an issue when trying to transfer the Token Account authority. In this codebase, this is relevant to the authority transfer of vault_account on Line 45 of lib.rs.

    Note: I am using different versions of anchor-spl, version 0.25.0, however I haven't found reason to believe this is the source of the issue.

    In my example, the equivalent to Line 156 of test.ts fails as the _vault.owner address is the Token Program, not the program PDA.

    Any help is appreciated, thanks.

    opened by brendan-mccaffrey 1
Owner
ironaddicteddog
BUIDL on Solana
ironaddicteddog
Ah Fuck Anchor

Ah Fuck Anchor

Deep Mehta 24 Dec 14, 2022
Anchor, a Rust Klipper protocol implementation

Anchor Anchor is an implementation of the Klipper protocol. You can use Anchor to create custom Klipper MCUs. It's written in Rust and provides only t

Annex-Engineering 20 Mar 21, 2023
Generates an Anchor CPI crate from a JSON IDL.

anchor-gen Generates a crate for cross-program invocations to an Anchor program from a JSON IDL. Usage In a new crate, write: anchor_gen::generate_cpi

Saber 44 Jul 3, 2023
A quality of life package for Anchor.

anchor-cereal A quality of life package for Anchor made to support the Solana ecosystem. Currently, it allows you to serialize a struct containing an

✨ amelia chen ✨ 24 Feb 9, 2023
A simple program for C program IO testing. Written in Rust

A simple program for C program IO testing. Written in Rust, using concurrency to speed up valgrind testing. Make sure to update settings at your first run of the program!

null 1 Feb 22, 2022
That program use on platform windows. And if you write any text on uncorrect keyboard layout, that program for that.

?? This program is designed to translate text into the correct layout when typing is incorrect. ?? Example ghbdtn -> привет Just (by default) pressing

Gest Se 5 Jan 26, 2023
A small subset of PHP implemented in Rust. 🐘

microphp A small subset of PHP implemented in Rust. About This project aims to implement a small subset of PHP's feature-set using a Rust powered pars

Ryan Chandler 5 Feb 7, 2022
Simple console input macros with the goal of being implemented in the standard library.

Simple console input macros with the goal of being implemented in the standard library.

undersquire 2 Feb 10, 2022
Prefix tree implemented in Rust.

Prefix tree implemented in rust. A simple library that provides a prefix tree (trie) implementation. It uses generic types for both keys and values. p

Bailey 0 Dec 8, 2021
A Unix shell written and implemented in rust 🦀

vsh A Blazingly fast shell made in Rust ?? Installation Copy and paste the following command and choose the appropriate installtion method for you. Yo

XMantle 89 Dec 18, 2022
A fork of V8Js re-implemented in Rust

PHP-V8Js PHP-V8Js is a PHP extension for the V8 JavaScript engine. It is a re-implementation of the fantastic (though unmaintained) V8Js PHP extension

Joe Hoyle 13 Jan 2, 2023
A small CLI tool to query ArcGIS REST API services, implemented in Rust. The server response is returned as pretty JSON.

A small CLI tool to query ArcGIS REST API services, implemented in Rust. The server response is returned as pretty JSON.

Andrew Vitale 2 Apr 25, 2022
Multiple precision floating point numbers implemented purely in Rust.

Multiple precision floating point numbers implemented purely in Rust. Rationale There are several notable implementations of numbers with increased pr

null 11 Nov 23, 2022
Rust Crate that allows to do interruptions in console. Will be implemented to functional terminal customization kit.

termpause Rust Crate that allows to do interruptions in console. Will be implemented to functional terminal customization kit. Usage Add this in your

Just Said 4 Sep 21, 2022
A fast, simple and lightweight Bloom filter library for Python, fully implemented in Rust.

rBloom A fast, simple and lightweight Bloom filter library for Python, fully implemented in Rust. It's designed to be as pythonic as possible, mimicki

Kenan Hanke 91 Feb 4, 2023
An Interpreter for Brainfuck programming language implemented in the Rust programming language with zero dependencies.

Brainfuck Hello, Visitor! Hey there, welcome to my project showcase website! It's great to have you here. I hope you're ready to check out some awesom

Syed Vilayat Ali Rizvi 7 Mar 31, 2023
A version control system implemented from scratch in Rust.

Version Control An experiment to write a version control system from scratch in Rust. CLI Usage Usage: revtool <COMMAND> Commands: init initia

Samuel Schlesinger 3 May 3, 2023
Introduction to Algorithms (implemented in Go, Rust, Erlang, and Clojure)

Algorithms Working through Introduction to Algorithms, 4th Edition and implementing the algorithms in: Go Rust Erlang Clojure While keeping a diary an

Patrick Bucher 7 Aug 14, 2023
A very tiny terminal snake game, purely implemented in Rust.

tiny-snake.rs A very tiny terminal snake game, purely implemented in Rust. Features Optimized binary has only 2760 bytes. No dependencies. Not even li

Rodrigo Batista de Moraes 59 Aug 16, 2023