Generate Go clients from anchor IDLs for Solana blockchain programs

usage

anchor-go --src=/path/to/idl.json

Generated Code will be generated and saved to ./generated/.

TODO

  • instructions
  • accounts
  • types
  • events
  • errors
  • handle tuple types

what is anchor-go?

anchor-go generates Go clients for Solana programs (smart contracts) written using the anchor framework.

what is anchor?

Link: https://github.com/project-serum/anchor

Anchor is a framework for Solana's Sealevel runtime providing several convenient developer tools for writing smart contracts.

I have an anchor program; how do I generate a Go client for it? (step by step)

example 1: metaplex nft candy machine

git clone https://github.com/metaplex-foundation/metaplex.git
cd metaplex
anchor idl parse -f rust/nft-candy-machine/src/lib.rs -o nft_candy_machine.json
anchor-go --src=nft_candy_machine.json
Comments
  • Hacky fix

    Hacky fix

    If a method ends in a number "doSomething2" then the AnchorTypeIDEncoding is wrong as it treats it as do_something_2 instead of the required do_something2

    I only needed to generate once so I didn't really solve this correctly as it's across multiple repos

  • IDLs with 'account' suffixes cause duplicate methods

    IDLs with 'account' suffixes cause duplicate methods

    Assume an IDL with this structure:

    {
      "version": "0.0.0",
      "name": "program",
      "instructions": [
          "name": "bar",
          "accounts": [
            {
              "name": "foo",
              "isMut": false,
              "isSigner": true
            },
            {
              "name": "fooAccount",
              "isMut": true,
              "isSigner": false
            }
          ]
      ]
    }
    

    As Account is stripped from the latter name, duplicate GetFoo and SetFoo receiver methods are created. I'm running into this in the real world with an existing project.

    Happy to contribute a fix but not sure what the solution should be. Possibilities:

    • Don't strip Account entirely
    • Drop strip if conflict exists.
    • Add extra suffix if conflict exists
    • Error out and don't generate code
  • Issue with complex enum type

    Issue with complex enum type

    Hello! I'm trying to utilize anchor-go to generate code for some of the newer metaplex instructions.

    Running into an issue with a new field they added to NFTs called CollectionDetails.

    I used shank from metaplex to generate IDL from the token-metadata program, and the CollectionDetails field looks like:

        {
          "name": "CollectionDetails",
          "type": {
            "kind": "enum",
            "variants": [
              {
                "name": "V1",
                "fields": [
                  {
                    "name": "size",
                    "type": "u64"
                  }
                ]
              }
            ]
          }
        },
    

    Which results in the following go code generated from anchor-go:

    type CollectionDetails interface {
    	isCollectionDetails()
    }
    ​
    type collectionDetailsContainer struct {
    	Enum ag_binary.BorshEnum `borsh_enum:"true"`
    	V1   CollectionDetailsV1
    }
    ​
    type CollectionDetailsV1 struct {
    	Size uint64
    }
    ​
    func (obj CollectionDetailsV1) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) {
    	// Serialize `Size` param:
    	err = encoder.Encode(obj.Size)
    	if err != nil {
    		return err
    	}
    	return nil
    }
    ​
    func (obj *CollectionDetailsV1) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) {
    	// Deserialize `Size`:
    	err = decoder.Decode(&obj.Size)
    	if err != nil {
    		return err
    	}
    	return nil
    }
    ​
    func (_ *CollectionDetailsV1) isCollectionDetails() {}
    

    Is that correct? What's the purpose of the isCollectionDetails() function? (sorry, new to anchor)

    Then in my own code i'm trying to do something like:

           collectionDetails := token_metadata.CollectionDetailsV1{
    		Size: 0,
    	}
    	var details *token_metadata.CollectionDetails
    	details = &collectionDetails
    

    And the compiler is getting upset saying that CollectionDetailsV1 can't be assigned to CollectionDetails even tho CollectionDetailsV1 definitely implements that interface.

    Any help would be greatly appreciated. Thanks so much for these libraries!

  • Transaction failed to simulate

    Transaction failed to simulate

    "Allocate: account Address { address: 5VqTqhqpSRWk3zLpWnqq2N93F5gpEsfF8XEEmEtNeT9i, base: None } already in use",

    It appears to me that this happens to due to Associated Account. Now it says it's already in use, but the wallet is generated new, so how could it be?

    
    mint := solana.NewWallet()
    
    
    var associatedAddress solana.PublicKey
    associatedAddress, err = getAtaForMint(mint.PublicKey(), userKeyPair.PublicKey())
    if err != nil {
        panic(associatedAddress)
    }
    
    func getAssociatedAddress(mint solana.PublicKey, buyer solana.PublicKey) (solana.PublicKey, error) {
    	addr, _, err := solana.FindProgramAddress(
    		[][]byte{
    			buyer.Bytes(),
    			solana.TokenProgramID.Bytes(),
    			mint.Bytes(),
    		},
    		solana.SPLAssociatedTokenAccountProgramID,
    	)
    	fmt.Println("geT ATA: ", solana.TokenProgramID)
    	return addr, err
    }
    
    	instructions = append(instructions,
    		nft_candy_machine.NewMintEeInstructionBuilder().
    			SetCandyMachineAccount(candyMachineAddress).
    			SetPayerAccount(userKeyPair.PublicKey()).
    			SetExpect(cm.Data.Price).
    			SetWalletAccount(cm.Wallet).
    			SetWallet2Account(wallet2).
    			SetTotalMintsAccount(totalMints).
    			SetProof(proof).
    			SetMintAccount(mint.PublicKey()).
    			SetMetadataAccount(metadataAddress).
    			SetMasterEditionAccount(masterEdition).
    			SetTokenMetadataProgramAccount(token_metadata.ProgramID).
    			SetTokenProgramAccount(token.ProgramID).
    			SetSystemProgramAccount(system.ProgramID).
    			SetAssociatedTokenProgramAccount(solana.SPLAssociatedTokenAccountProgramID).
    			SetAssociatedAccount(associatedAddress).
    			SetRentAccount(solana.SysVarRentPubkey).
    			SetClockAccount(solana.SysVarClockPubkey).
    			Build(),
    	)
    

    My JS code I would say is 1:1 with the Go code I have here. I am not sure what I am missing out on. I am not sure if you faced this issue?

  • How to specify remainingAccounts?

    How to specify remainingAccounts?

    We use an instruction that uses ctx.remaining_accounts internally. How do we specify those remaining accounts? They're passed as a separate field in anchor.js.

  • add serum idl (back?)

    add serum idl (back?)

    Huge fan of your Solana toolchains @gagliardetto! Could we add a serum IDL again? I found it in the history but I'm not sure why it was removed. Was it manually constructed?

    https://github.com/gagliardetto/anchor-go/commit/3f23d3a7b89f83ad1e71ea470a21a9e1a58eca7a

  • Method issue when includes 0-9

    Method issue when includes 0-9

    If a method ends in a number "doSomething2" then the AnchorTypeIDEncoding is wrong as it treats it as do_something_2 instead of the required do_something2

    I only needed to generate once so I didn't really solve this correctly as it's across multiple repos

    https://github.com/gagliardetto/anchor-go/pull/3

  • Conditionally strip account suffix from accessors

    Conditionally strip account suffix from accessors

    Fixes #1

    This doesn't implement the fix for failing when there is a conflict, but it does solve the immediate issue of generating invalid code by introducing the new flag.

  • anchor error code 101

    anchor error code 101

    i use anchor-go get the client but i have the question, just like : Error Code: InstructionFallbackNotFound. Error Number: 101. Error Message: Fallback functions are not supported

  • Anchor error -- InstructionDidNotDeserialize

    Anchor error -- InstructionDidNotDeserialize

    I'm getting back a weird error on my transaction --

    (*solana.Transaction)(0x14000124070)(   
       ├─ Signatures[len=1]
       │    └─ 3UpnFHfN9d9ZTHsiaL2JswkFxBkzYDRHAB8CwKYdhYJQiAmuP83vYST7XKY9XMyCzd6AP7thHBjisBBBzKSYb39N
       ├─ Message
       │    ├─ RecentBlockhash: 752C7hsnWjHbz6diYE29tm2MfeQnMA1U1w3WMSQUCzin
       │    ├─ AccountKeys[len=4]
       │    │    ├─ 4GBYBnYu2Nk7yP4zuNX5kpWoienPdu5TAvutLcJHf1YL
       │    │    ├─ ADxdmsoW5xhtBJ44cmoPyQpvBj2rWSvgnSTS78SSBvBQ
       │    │    ├─ 11111111111111111111111111111111
       │    │    └─ EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9
       │    └─ Header
       │       ├─ NumRequiredSignatures: 1
       │       ├─ NumReadonlySignedAccounts: 0
       │       └─ NumReadonlyUnsignedAccounts: 2
       └─ Instructions[len=1]
          └─ Program: Worknet EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9
             └─ Instruction: UpdateDevice
                ├─ Params[len=1]
                │    └─ Device: (worknet.Device) {
                │                Ipv4: ([4]uint8) (len=4 cap=4) {
                │                 00000000  00 00 00 00                                       |....|
                │                },
                │                Hostname: (string) "",
                │                Bump: (uint8) 255,
                │                Status: (worknet.DeviceStatus) Registered,
                │                DeviceAuthority: (solana.PublicKey) (len=32 cap=32) 4GBYBnYu2Nk7yP4zuNX5kpWoienPdu5TAvutLcJHf1YL
                │               }
                └─ Accounts[len=3]
                   ├─ deviceAuthority: 4GBYBnYu2Nk7yP4zuNX5kpWoienPdu5TAvutLcJHf1YL [WRITE, SIGN] 
                   ├─          device: ADxdmsoW5xhtBJ44cmoPyQpvBj2rWSvgnSTS78SSBvBQ [WRITE] 
                   └─   systemProgram: 11111111111111111111111111111111 [] 
    )
    daoctl: error: (*jsonrpc.RPCError)(0x14000119200)({
                    Code: (int) -32002,
                    Message: (string) (len=89) "Transaction simulation failed: Error processing Instruction 0: custom program error: 0x66",
                    Data: (map[string]interface {}) (len=4) {
                     (string) (len=8) "accounts": (interface {}) <nil>,
                     (string) (len=3) "err": (map[string]interface {}) (len=1) {
                      (string) (len=16) "InstructionError": ([]interface {}) (len=2 cap=2) {
                       (json.Number) (len=1) "0",
                       (map[string]interface {}) (len=1) {
                        (string) (len=6) "Custom": (json.Number) (len=3) "102"
                       }
                      }
                     },
                     (string) (len=4) "logs": ([]interface {}) (len=5 cap=8) {
                      (string) (len=63) "Program EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9 invoke [1]",
                      (string) (len=38) "Program log: Instruction: UpdateDevice",
                      (string) (len=167) "Program log: AnchorError occurred. Error Code: InstructionDidNotDeserialize. Error Number: 102. Error Message: The program could not deserialize the given instruction.",
                      (string) (len=90) "Program EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9 consumed 3587 of 200000 compute units",
                      (string) (len=87) "Program EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9 failed: custom program error: 0x66"
                     },
                     (string) (len=13) "unitsConsumed": (json.Number) (len=1) "0"
                    }
                   })
    

    the transaction message:

    (solana.Message) {
     AccountKeys: ([]solana.PublicKey) (len=4 cap=4) {
      (solana.PublicKey) (len=32 cap=32) 4GBYBnYu2Nk7yP4zuNX5kpWoienPdu5TAvutLcJHf1YL,
      (solana.PublicKey) (len=32 cap=32) ADxdmsoW5xhtBJ44cmoPyQpvBj2rWSvgnSTS78SSBvBQ,
      (solana.PublicKey) (len=32 cap=32) 11111111111111111111111111111111,
      (solana.PublicKey) (len=32 cap=32) EdUCoDdRnT5HsQ2Ejy3TWMTQP8iUyMQB4WzoNh45pNX9
     },
     Header: (solana.MessageHeader) {
      NumRequiredSignatures: (uint8) 1,
      NumReadonlySignedAccounts: (uint8) 0,
      NumReadonlyUnsignedAccounts: (uint8) 2
     },
     RecentBlockhash: (solana.Hash) (len=32 cap=32) 752C7hsnWjHbz6diYE29tm2MfeQnMA1U1w3WMSQUCzin,
     Instructions: ([]solana.CompiledInstruction) (len=1 cap=1) {
      (solana.CompiledInstruction) {
       ProgramIDIndex: (uint16) 3,
       Accounts: ([]uint16) (len=3 cap=3) {
        (uint16) 0,
        (uint16) 1,
        (uint16) 2
       },
       Data: (solana.Base58) (len=58 cap=64) H9VJg8bfvmqXHgp785X5L6pv2W1DSNfH1GW48BqQ8rU8feZcSosi67B44b1oUJyW9rZrZFdaBh8DHiG
      }
     }
    }
    

    It's peculiar. I have the Go bindings working just fine with the RegisterDevice instruction, which is barely different from UpdateDevice that's throwing off the error here.

    #[account]
    #[derive(Default)]
    pub struct Device {
        pub ipv4: [u8; 4],
        pub hostname: String,
        pub bump: u8,
        pub status: DeviceStatus,
        pub device_authority: Pubkey,
    }
    
    #[derive(Accounts)]
    pub struct UpdateDevice<'info> {
        pub device_authority: Signer<'info>,
    
        #[account(mut,
            has_one = device_authority,
            seeds = [device.device_authority.key().as_ref()],
            bump,
        )]
        pub device: Box<Account<'info, Device>>,
    
        pub system_program: Program<'info, System>,
    }
    
    #[derive(Accounts)]
    #[instruction(device_authority: Pubkey)]
    pub struct RegisterDevice<'info> {
        #[account(mut)]
        pub authority: Signer<'info>,
    
        #[account(init,
            space = 128,
            seeds = [device_authority.as_ref()],
            bump,
            payer = authority,
        )]
        pub device: Box<Account<'info, Device>>,
    
        pub system_program: Program<'info, System>,
    }
    
    
    

    My Anchor test for the same endpoint works fine 🤷

    it("updates device info", async () => {
            const [devicePDA, _devicePDABump] =
                await anchor.web3.PublicKey.findProgramAddress(
                    [deviceKey.publicKey.toBuffer()],
                    program.programId
                );
            await program.methods
                .updateDevice({
                    ipv4: [10, 10, 10, 10],
                    hostname: "testhost",
                    status: { registered: {} }, // TODO: better way to get enum?
                    deviceAuthority: deviceKey.publicKey,
                })
                .accounts({
                    deviceAuthority: deviceKey.publicKey,
                    device: devicePDA,
                    systemProgram: SystemProgram.programId,
                })
                .signers([deviceKey])
                .rpc();
        });
    

    Any ideas?

  • Out-of-order signer accounts can cause issues with generated Anchor bindings

    Out-of-order signer accounts can cause issues with generated Anchor bindings

    I ran into this issue using the code gen because in my Rust struct, the authority which I used to sign a transaction was not listed as the zeroth account, which is what the Solana runtime expects for verification (since the signature is in the zeroth index of the list of sigs). So anchor-go complained about sigs, yet, it worked in the Anchor JS client.

    Anchor JS bindings, I think were doing some magic behind the scenes (https://github.com/project-serum/anchor/blob/master/ts/src/program/accounts-resolver.ts#L42 ?) to make the ordering work correctly, perhaps the generated Go code could do something like this as well. But that would also probably complicate the code, so I can definitely see it as unnecessary as well.

  • Tuple support

    Tuple support

    Any idea when tuple support may be coming? I have some IDLs that I'm unable to convert due to the lack of support. I'd happily offer payment for the addition of this feature and any others that may be required to convert more advanced IDLs.

go-i18n is a Go package and a command that helps you translate Go programs into multiple languages.

go-i18n is a Go package and a command that helps you translate Go programs into multiple languages.

Jan 2, 2023
libraries for various programming languages that make it easy to generate per-process trace files that can be loaded into chrome://tracing
libraries for various programming languages that make it easy to generate per-process trace files that can be loaded into chrome://tracing

chrometracing: chrome://tracing trace_event files The chrometracing directory contains libraries for various programming languages that make it easy t

Oct 6, 2022
go generate based graphql server library
go generate based graphql server library

gqlgen What is gqlgen? gqlgen is a Go library for building GraphQL servers without any fuss. gqlgen is based on a Schema first approach — You get to D

Dec 29, 2022
Go package to generate and manage color palettes & schemes 🎨
Go package to generate and manage color palettes & schemes 🎨

Go package to generate and manage color palettes & schemes

Dec 29, 2022
Utilities to generate (reference) documentation for the docker CLI

About This is a library containing utilities to generate (reference) documentation for the docker CLI on docs.docker.com. Disclaimer This library is i

Dec 28, 2022
generate random data like name, email, uuid, address, images and etc.

gg-rand generate random data like name, email, uuid, address, images and etc. build and install: make run: gg-rand $ gg-rand SillyName : Knavesa

Nov 16, 2022
Generate simple CHANGELOG

changelog Generate simple CHANGELOG Install Download the one that suits your environment and place it in a location that is in your PATH. https://gith

Oct 10, 2021
Generate some random data

fake-data-generator-api generate some random data installing and using

Dec 2, 2022
A utility to generate SPDX-compliant Bill of Materials manifests

Kubernetes Template Project The Kubernetes Template Project is a template for starting new projects in the GitHub organizations owned by Kubernetes. A

Jan 1, 2023
Generate signal for a thing for golang

go_kafka_signal generate signal for a thing go build producer.go ./producer -f ~

Dec 24, 2021
Protoc-gen-fieldmask - Generate FieldMask utility functions for protobuf

protoc-gen-fieldmask Generate FieldMask utility functions for protobuf Generated

Aug 20, 2022
An application written in Go to generate fractals like the Mandelbrot set and the Julia set.
An application written in Go to generate fractals like the Mandelbrot set and the Julia set.

Fractals An application written in Go to generate fractals like the Mandelbrot set and the Julia set. Screenshots Mandelbrot set Julia set Prerequisit

May 9, 2022
Personal-Solana-Wallet - Create your personal wallet on Solana blockchain

Personal Wallet on Solana using Go ♾️ Setting up environment Installation of Cob

Nov 9, 2022
Go clients for the Metaplex Solana programs

metaplex-go A suite of Go clients for the 5 metaplex contracts. This is an alpha version. For usage examples, you can get inspired by their Rust/Types

Nov 25, 2022
Go SDK library for the Solana Blockchain
Go SDK library for the Solana Blockchain

Solana SDK library for Go Go library to interface with Solana JSON RPC and WebSocket interfaces. Clients for Solana native programs, Solana Program Li

Jan 9, 2023
Terra client in golang with multiple protocol implementation (anchor, astroport, prism, ...)

Terra A terra client with some protocol partial implementations (anchor, prism, terraswap type routers, ...) To be able to compile, you need to add th

Apr 11, 2022
Using Mailchain, blockchain users can now send and receive rich-media HTML messages with attachments via a blockchain address.

Mailchain Introduction Mailchain enables blockchain-based email-like messaging with plain or rich text and attachment capabilities. Using blockchain p

Dec 28, 2022
A reference implementation of blockchain in Go to demonstrate how blockchain works. For education purpose.
A reference implementation of blockchain in Go to demonstrate how blockchain works. For education purpose.

Mini-Blockchain Mini-Blockchain is a reference design for a blockchain system to demostate a full end2end flow in current blockchain technology. There

Nov 18, 2022
Our aim is to expand the capabilities of blockchain and make a secure way for transferring NFT between RMRK and MOVR blockchain.

remov Inspiration Our aim is to expand the capabilities of blockchain and make a secure way for transferring NFT between RMRK and MOVR blockchain. The

Jul 25, 2022
Blockchain-go - A repository that houses a blockchain implemented in Go

blockchain-go This is a repository that houses a blockchain implemented in Go. F

May 1, 2022