Skip to content
Arch-NetworkPublic

About

Go SDK for building, signing, and sending transactions on Arch Network

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Arch Go SDK

A Go SDK for building, signing, and sending transactions on the Arch Network. It is a faithful port of the arch-typescript-sdk and the Rust arch_sdk, byte-for-byte compatible with the node's wire formats: message serialization and hashing are pinned against fixtures generated by the TypeScript SDK, the JSON-RPC request/response shapes against the node's own wire-format tests, and BIP-322 signing against the test vector of the exact bip322 crate the node uses.

Installation

go get github.com/Arch-Network/arch-go

Coming from Solana?

Arch's programming model is Solana-derived (accounts, instructions, compiled messages), but the wire is not Solana-compatible. The differences this SDK handles for you:

  • RPC: JSON-RPC 2.0 with Arch method names (read_account_info, get_best_block_hash, ...) and byte arrays as JSON number arrays — not base64/base58 blobs, and no commitment levels.
  • Serialization: message binary layout uses u32 little-endian counts, not compact-u16 ("shortvec").
  • Signing: keys are secp256k1 x-only (Bitcoin Taproot), not ed25519. The signing digest is the 64 ASCII bytes of hex(sha256(hex(sha256(serialize(message))))), signed with BIP-322 (taproot key-spend, SIGHASH_ALL), transported as the first 64 bytes of the witness signature.
  • Addresses: an account's on-chain address is a taproot output controlled by the network's distributed signing key, not by the account key itself. Derive it offline with AccountAddress(networkKey, accountKey, params) (network key from Client.GetNetworkPubkey + NetworkPubkeyFromHex), or ask the node with Client.GetAccountAddress. P2TRAddress is the plain taproot address of a key — what BIP-322 signatures commit to — and is not the account address.

Quickstart: transfer lamports

package main

import (
	"fmt"
	"log"

	arch "github.com/Arch-Network/arch-go"
)

func main() {
	client := arch.NewClient("http://localhost:9002", nil)

	// Your key. Any *btcec.PrivateKey works; wallets that already handle
	// Bitcoin Taproot keys can reuse them directly.
	priv, err := arch.NewPrivateKey()
	if err != nil {
		log.Fatal(err)
	}
	payer := arch.XOnlyPubkey(priv)

	var to arch.Pubkey // recipient account pubkey
	instruction := arch.Transfer(payer, to, 1_000)

	blockhashHex, err := client.GetBestBlockHash()
	if err != nil {
		log.Fatal(err)
	}
	blockhash, err := arch.HashFromHex(blockhashHex)
	if err != nil {
		log.Fatal(err)
	}

	message, err := arch.NewSanitizedMessage([]arch.Instruction{instruction}, &payer, blockhash)
	if err != nil {
		log.Fatal(err)
	}

	tx, err := arch.BuildAndSignTransaction(message, priv)
	if err != nil {
		log.Fatal(err)
	}

	txid, err := client.SendTransaction(tx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("txid:", txid)
}

Signing with an external wallet

If the wallet signs BIP-322 itself, build the message, hand it the digest, and normalize the returned signature:

digest := message.Hash() // 64 ASCII bytes; sign these with BIP-322

// walletSig is 64, 66, or 67 bytes depending on the wallet's encoding.
sig, err := arch.AdjustSignature(walletSig)

tx := arch.RuntimeTransaction{
	Version:    arch.RuntimeTxVersion,
	Signatures: []arch.Signature{sig},
	Message:    message,
}

You can verify any signature locally with arch.VerifyMessageBIP322.

RPC client

Client covers the node's full JSON-RPC surface:

Method RPC
SendTransaction / SendTransactions send_transaction / send_transactions
ReadAccountInfo / GetMultipleAccounts read_account_info / get_multiple_accounts
GetAccountAddress get_account_address
GetBestBlockHash / GetBestFinalizedBlockHash get_best_block_hash / get_best_finalized_block_hash
GetBlock / GetBlockByHeight / GetFullBlockByHash / GetFullBlockByHeight get_block / get_block_by_height
GetBlockCount / GetBlockHash get_block_count / get_block_hash
GetProcessedTransaction / GetTransactionStatus get_processed_transaction / get_transaction_status
GetTransactionsByBlock / GetTransactionsByIds / RecentTransactions get_transactions_by_block / get_transactions_by_ids / recent_transactions
GetProgramAccounts get_program_accounts (with DataSizeFilter / DataContentFilter)
RequestAirdrop / CreateAccountWithFaucet request_airdrop / create_account_with_faucet (regtest/testnet)
GetNetworkPubkey / CheckPreAnchorConflict get_network_pubkey / check_pre_anchor_conflict
CreateNewAccount helper: fresh keypair + node-derived address

Lookups return nil (not an error) when the node reports not-found; all other node errors surface as *arch.RPCError with the node's code and message.

System program instructions

Builders for every system instruction: CreateAccount, CreateAccountWithAnchor (anchor an account to a Bitcoin UTXO), Assign, Anchor, SignInput, Transfer, Allocate, CreateAccountWithSeed, AllocateWithSeed, AssignWithSeed, TransferWithSeed. Program-ID constants for the token, associated-token, BPF-loader, vote, stake, compute-budget, and native-loader programs are in constants.go.

Token instructions (APL token)

Builders for the APL token program (an SPL Token fork) and the associated token account program: TokenInitializeMint(2), TokenInitializeAccount (2/3), TokenInitializeMultisig, TokenTransfer, TokenApprove, TokenRevoke, TokenSetAuthority, TokenMintTo, TokenBurn, TokenCloseAccount, TokenFreezeAccount, TokenThawAccount, the *Checked variants, TokenSyncNative, and CreateAssociatedTokenAccount (WithAnchor). Pass nil multisig signers for a single-key authority, or the signer set when the authority is a multisig account.

ata, _, err := arch.AssociatedTokenAddress(wallet, mint)
ix := arch.TokenTransfer(sourceATA, destATA, owner, nil, 1_000_000)

Program-derived addresses use FindProgramAddress / CreateProgramAddress. Note Arch's derivation differs from Solana's — the hash is sha256(seeds || bump || program_id) with no domain-separator string — so Solana PDA code will produce wrong addresses here.

Loader instructions

Builders for deploying programs: LoaderWrite, LoaderTruncate, LoaderDeploy, LoaderRetract, LoaderTransferAuthority, LoaderFinalize, and ExtendBytesMaxLen (the largest LoaderWrite payload that fits in one transaction, for chunking ELF uploads).

WebSocket subscriptions

The validator pushes events over a plain WebSocket endpoint (default port 10081). Topics: block, transaction, account_update, rolledback_transactions, reapplied_transactions, dkg, with optional server-side field filters:

ws, err := arch.DialWebSocket(ctx, "ws://localhost:10081")
if err != nil {
	log.Fatal(err)
}
defer ws.Close()

// nil filter receives every event on the topic; a filter matches event
// fields exactly (array values match if the event's array contains them).
sub, err := ws.Subscribe(ctx, arch.TopicTransaction, arch.EventFilter{
	"status": map[string]any{"type": "processed"},
})
if err != nil {
	log.Fatal(err)
}

for ev := range sub.Events() {
	txEv, err := ev.Transaction()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("processed:", txEv.Hash, "at height", txEv.BlockHeight)
}

Subscription.Unsubscribe cancels one subscription; closing the client closes every subscription's event channel.

Testing

go test ./...

The suite pins:

  • message serialization and the signing digest against fixtures generated by the TypeScript SDK;
  • JSON request envelopes, parameter shapes, and response decoding against the node's rpc_wire_format.rs fixtures, via a mock HTTP node;
  • BIP-322 signing against the rust bip322 crate's taproot test vector, plus sign/verify round trips;
  • key compilation (payer first, then writable signers, readonly signers, writable non-signers, readonly non-signers, byte-sorted within groups) against the Rust compiled_keys semantics;
  • system, token, and loader instruction data layouts byte-for-byte;
  • PDA derivation (including the associated token address) against addresses generated by running the Rust arch_program derivation;
  • the WebSocket client against a mock server speaking the validator's exact subscription wire format;
  • account-address derivation against an address captured live from the testnet node.

A live integration test runs the whole stack — reads, offline address derivation, the faucet flow, and SDK-built, BIP-322-signed transactions — against a real network:

ARCH_RPC_URL=https://rpc.testnet.arch.network go test -run TestLive -v -timeout 600s

Note the faucet flow: CreateAccountWithFaucet returns a transaction signed by the faucet that the created account must co-sign (append SignMessageBIP322(priv, tx.Message.Hash()) to tx.Signatures) before sending.

License

MIT

About

Go SDK for building, signing, and sending transactions on Arch Network

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages