Getting Started with the TypeScript SDK
This guide will walk you through setting up and using the Arch Network TypeScript SDK (developed by Saturn) to build your first application.
Note: The Arch TypeScript SDK is a low-level SDK that provides direct RPC access to Arch nodes. It does not include high-level abstractions like transaction builders or wallet management.
Prerequisites
- Node.js 16+ and npm or yarn
- Basic understanding of blockchain concepts and JavaScript/TypeScript
- Arch Network node running locally or access to a remote node
Installation
Create a New Project
# Create a new project
mkdir my-arch-app
cd my-arch-app
npm init -y
# Install the Saturn TypeScript SDK
npm install @saturnbtcio/arch-sdk
# Install TypeScript (optional but recommended)
npm install -D typescript @types/node
npx tsc --init
For Existing Projects
# Using npm
npm install @saturnbtcio/arch-sdk
# Using yarn
yarn add @saturnbtcio/arch-sdk
# Using pnpm
pnpm add @saturnbtcio/arch-sdk
Your First Connection
Create a file named connect.ts
(or connect.js
for JavaScript):
import { RpcConnection } from '@saturnbtcio/arch-sdk';
async function main() {
// Connect to local validator
const connection = new RpcConnection('http://localhost:9002');
try {
console.log('π Connecting to Arch node at http://localhost:9002...\n');
// Get current block count
const blockCount = await connection.getBlockCount();
console.log('β Current block count:', blockCount);
// Get best block hash
const bestBlockHash = await connection.getBestBlockHash();
console.log('β Best block hash:', bestBlockHash);
// Get block hash for a specific height
if (blockCount > 0) {
const blockHeight = blockCount - 1;
const blockHash = await connection.getBlockHash(blockHeight);
console.log(`β Block hash at height ${blockHeight}:`, blockHash);
}
console.log('\nβ
Successfully connected to Arch node!');
console.log('π Network is active with', blockCount, 'blocks');
} catch (error) {
console.error('β Error connecting to Arch node:', error);
console.log('\nπ‘ Make sure your Arch node is running at http://localhost:9002');
console.log(' You can start it with: arch-node --network=testnet');
}
}
// Run the main function
main().catch(console.error);
Run the script:
# TypeScript
npx ts-node connect.ts
# JavaScript
node connect.js
Example output:
π Connecting to Arch node at http://localhost:9002...
β Current block count: 57230
β Best block hash: 349e8a42cdc98d05d427ba8fe8efcfd13e875591f1f1f111960a991f3add8105
β Block hash at height 57229: 349e8a42cdc98d05d427ba8fe8efcfd13e875591f1f1f111960a991f3add8105
β
Successfully connected to Arch node!
π Network is active with 57230 blocks
Creating Accounts
The SDK provides utilities for creating accounts using secp256k1 cryptography:
import { RpcConnection, ArchConnection } from '@saturnbtcio/arch-sdk';
async function createAccount() {
const connection = new RpcConnection('http://localhost:9002');
const arch = ArchConnection(connection);
// Create a new account
const account = await arch.createNewAccount();
console.log('π New Account Created:');
console.log('Private Key:', account.privkey);
console.log('Public Key:', account.pubkey);
console.log('Address:', account.address);
return account;
}
createAccount().catch(console.error);
Create Account with Faucet Funding
import { RpcConnection } from '@saturnbtcio/arch-sdk';
import { randomBytes } from 'node:crypto';
// Helper function to wait for a specified time
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
async function createAndFundAccount() {
const connection = new RpcConnection('http://localhost:9002');
try {
console.log('π Connecting to Arch node...\n');
// Check the current block height
const initialBlockCount = await connection.getBlockCount();
console.log('π Current block height:', initialBlockCount);
// Generate a random 32-byte public key
const pubkey = randomBytes(32);
console.log('π Generated public key:', pubkey.toString('hex'));
// Create account with faucet
console.log('\nπ° Step 1: Creating account with faucet...');
await connection.createAccountWithFaucet(pubkey);
console.log('β
Faucet account creation initiated');
// Get the Arch address
const archAddress = await connection.getAccountAddress(pubkey);
console.log('π Arch address:', archAddress);
// Request airdrop to fund the account
console.log('\nπ° Step 2: Requesting airdrop...');
await connection.requestAirdrop(pubkey);
console.log('β
Airdrop requested');
// Wait for account to be created and funded
console.log('\nβ³ Waiting for account to be confirmed on chain...');
console.log(' (This may take 5-10 seconds)');
let accountFound = false;
const maxAttempts = 6;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const waitTime = attempt * 2000; // Increase wait time each attempt
console.log(`\nπ Attempt ${attempt}/${maxAttempts}: Waiting ${waitTime / 1000} seconds...`);
await wait(waitTime);
// Check block progress
const currentBlockCount = await connection.getBlockCount();
console.log(`π Blocks produced: ${currentBlockCount - initialBlockCount}`);
try {
const accountInfo = await connection.readAccountInfo(pubkey);
console.log('\nβ
Account successfully created and funded!');
console.log('\nπ Account Details:');
console.log(' Address:', archAddress);
console.log(' Full info:', JSON.stringify(accountInfo, null, 2));
// Access properties safely
const info = accountInfo as any;
if (info.lamports !== undefined) {
console.log(' Balance:', info.lamports, 'lamports');
}
if (info.owner) {
console.log(' Owner:', Buffer.from(Object.values(info.owner)).toString('hex'));
}
if (info.utxo) {
console.log(' UTXO:', info.utxo);
}
if (info.is_executable !== undefined) {
console.log(' Executable:', info.is_executable);
}
accountFound = true;
break;
} catch (error) {
if (attempt === maxAttempts) {
console.log('β Account not found after maximum attempts');
} else {
console.log('β³ Account not ready yet, continuing to wait...');
}
}
}
if (accountFound) {
console.log('\nπ Success! Your Arch account is ready to use.');
console.log('π‘ You can now:');
console.log(' - Send transactions from this account');
console.log(' - Interact with Arch programs');
console.log(' - Deploy smart contracts');
console.log('\nπ Save these for future reference:');
console.log(' Pubkey:', pubkey.toString('hex'));
console.log(' Address:', archAddress);
}
} catch (error) {
console.error('β Error:', error);
console.log('\nπ‘ Troubleshooting:');
console.log(' - Make sure your Arch node is running at http://localhost:9002');
console.log(' - Ensure the node has faucet functionality enabled');
console.log(' - Check that the node is syncing and producing blocks');
}
}
createAndFundAccount();
Reading Account Information
import { RpcConnection } from '@saturnbtcio/arch-sdk';
async function readAccount() {
const connection = new RpcConnection('http://localhost:9002');
// Example: System program pubkey (32 zero bytes with last byte as 1)
const systemProgramPubkey = new Uint8Array(32);
systemProgramPubkey[31] = 1;
try {
const accountInfo = await connection.readAccountInfo(systemProgramPubkey);
console.log('Account Info:', accountInfo);
// Get account address
const address = await connection.getAccountAddress(systemProgramPubkey);
console.log('Account Address:', address);
} catch (error) {
console.error('Error reading account:', error);
}
}
Working with Messages and Instructions
The SDK uses a low-level message format for transactions:
import { RpcConnection, InstructionUtil, MessageUtil, PubkeyUtil } from '@saturnbtcio/arch-sdk';
import type { Message, Instruction } from '@saturnbtcio/arch-sdk';
// Create a simple instruction
const instruction: Instruction = {
program_id: PubkeyUtil.systemProgram(), // Returns system program pubkey
accounts: [
{
pubkey: new Uint8Array(32), // Your account pubkey
is_signer: true,
is_writable: true,
},
],
data: new Uint8Array([1, 2, 3, 4]), // Instruction data
};
// Create a message
const message: Message = {
signers: [new Uint8Array(32)], // Array of signer pubkeys
instructions: [instruction],
};
// Serialize the message for sending
const serializedMessage = MessageUtil.serialize(message);
console.log('Serialized message:', serializedMessage);
Sending Transactions
To send transactions, you need to create a RuntimeTransaction
:
import { RpcConnection } from '@saturnbtcio/arch-sdk';
import type { RuntimeTransaction, SanitizedMessage } from '@saturnbtcio/arch-sdk';
async function sendTransaction() {
const connection = new RpcConnection('http://localhost:9002');
// Note: Creating valid transactions requires proper message construction
// and cryptographic signatures. This is a simplified example.
const sanitizedMessage: SanitizedMessage = {
header: {
num_required_signatures: 1,
num_readonly_signed_accounts: 0,
num_readonly_unsigned_accounts: 0,
},
account_keys: [
new Uint8Array(32), // Signer pubkey
PubkeyUtil.systemProgram(), // System program
],
recent_blockhash: new Uint8Array(32), // Recent blockhash
instructions: [
{
program_id_index: 1, // Index into account_keys
accounts: [0], // Indexes into account_keys
data: new Uint8Array([1, 2, 3, 4]),
},
],
};
const transaction: RuntimeTransaction = {
version: 0,
signatures: [new Uint8Array(64)], // 64-byte signatures
message: sanitizedMessage,
};
try {
const txId = await connection.sendTransaction(transaction);
console.log('Transaction sent:', txId);
} catch (error) {
console.error('Error sending transaction:', error);
}
}
Querying Blocks
import { RpcConnection } from '@saturnbtcio/arch-sdk';
async function queryBlocks() {
const connection = new RpcConnection('http://localhost:9002');
try {
// Get the latest block
const bestBlockHash = await connection.getBestBlockHash();
const block = await connection.getBlock(bestBlockHash);
if (block) {
console.log('Block:', block);
console.log('Number of transactions:', block.transactions?.length || 0);
}
} catch (error) {
console.error('Error querying blocks:', error);
}
}
Get Processed Transaction
import { RpcConnection } from '@saturnbtcio/arch-sdk';
async function getTransaction(txId: string) {
const connection = new RpcConnection('http://localhost:9002');
try {
const processedTx = await connection.getProcessedTransaction(txId);
if (processedTx) {
console.log('Transaction found:', processedTx);
console.log('Status:', processedTx.status);
} else {
console.log('Transaction not found');
}
} catch (error) {
console.error('Error getting transaction:', error);
}
}
Get Program Accounts
import { RpcConnection } from '@saturnbtcio/arch-sdk';
async function getProgramAccounts() {
const connection = new RpcConnection('http://localhost:9002');
// Example: Get all accounts owned by a program
const programId = new Uint8Array(32); // Your program ID
try {
const accounts = await connection.getProgramAccounts(programId);
console.log(`Found ${accounts.length} accounts for program`);
accounts.forEach((account, index) => {
console.log(`Account ${index}:`, account);
});
} catch (error) {
console.error('Error getting program accounts:', error);
}
}
Utility Functions
The SDK provides several utility modules for working with Arch data structures:
import {
PubkeyUtil,
MessageUtil,
InstructionUtil,
AccountUtil,
SignatureUtil
} from '@saturnbtcio/arch-sdk';
// Get system program pubkey
const systemProgram = PubkeyUtil.systemProgram();
// Work with public keys
const pubkeyBytes = new Uint8Array(32);
const pubkeyHex = PubkeyUtil.toHex(pubkeyBytes);
const pubkeyFromHex = PubkeyUtil.fromHex(pubkeyHex);
// Serialize/deserialize messages
const serializedMsg = MessageUtil.serialize(message);
const deserializedMsg = MessageUtil.deserialize(serializedMsg);
Error Handling
The SDK provides custom error types:
import { RpcConnection, ArchRpcError } from '@saturnbtcio/arch-sdk';
async function handleErrors() {
const connection = new RpcConnection('http://localhost:9002');
try {
await connection.getBlock('invalid-hash');
} catch (error) {
if (error instanceof ArchRpcError) {
console.error('RPC Error:', error.error);
console.error('Error code:', error.error.code);
console.error('Error message:', error.error.message);
} else {
console.error('Unexpected error:', error);
}
}
}
Complete Example
Hereβs a complete example showing how to connect and query the network:
import { RpcConnection, ArchConnection } from '@saturnbtcio/arch-sdk';
async function completeExample() {
// 1. Setup connection
const connection = new RpcConnection('http://localhost:9002');
const arch = ArchConnection(connection);
try {
// 2. Get network info
console.log('π Network Information:');
const blockCount = await connection.getBlockCount();
console.log('Block count:', blockCount);
const bestBlockHash = await connection.getBestBlockHash();
console.log('Best block hash:', bestBlockHash);
// 3. Create a new account
console.log('\nπ Creating new account...');
const account = await arch.createNewAccount();
console.log('Address:', account.address);
// 4. Get block information
if (blockCount > 0) {
const blockHash = await connection.getBlockHash(blockCount - 1);
const block = await connection.getBlock(blockHash);
if (block) {
console.log('\nπ¦ Latest block:');
console.log('Hash:', blockHash);
console.log('Transactions:', block.transactions?.length || 0);
}
}
console.log('\nβ
Example completed successfully!');
} catch (error) {
console.error('β Error:', error);
}
}
completeExample().catch(console.error);
Important Notes
-
Low-Level SDK: This SDK provides low-level RPC access. High-level features like transaction building, wallet management, and program deployment helpers are not included.
-
Message Construction: Creating valid transactions requires proper understanding of Archβs message format and cryptographic signatures.
-
Type Safety: The SDK is written in TypeScript and provides type definitions for all data structures.
-
Error Handling: Always wrap RPC calls in try-catch blocks as network operations can fail.
Next Steps
- Learn about Archβs account model
- Understand message and instruction formats
- Explore the RPC API for all available methods
- Check the TypeScript SDK source for implementation details
Resources
- NPM Package: @saturnbtcio/arch-sdk
- GitHub Repository: saturnbtc/arch-typescript-sdk
- Discord: Arch Network Discord