Account
Accounts are the fundamental data storage unit in the Arch Network. They hold state, store data, and define ownership relationships. Understanding accounts is crucial for building applications on Arch.
Overview
Every piece of data on the Arch Network is stored in an account. Accounts can hold:
- Program code (executable accounts)
- Application data (data accounts)
- User balances (token accounts)
- Configuration settings (configuration accounts)
Account Structure
AccountInfo
The AccountInfo
struct provides a view into an account during program execution:
#![allow(unused)] fn main() { #[derive(Clone)] #[repr(C)] pub struct AccountInfo<'a> { pub key: &'a Pubkey, // Account's public key (address) pub utxo: &'a UtxoMeta, // Associated UTXO metadata pub data: Rc<RefCell<&'a mut [u8]>>, // Account data pub owner: &'a Pubkey, // Program that owns this account pub is_signer: bool, // Whether account signed the transaction pub is_writable: bool, // Whether account data can be modified pub is_executable: bool, // Whether account contains executable code } }
AccountMeta
The AccountMeta
struct describes how an account is used in a transaction:
#![allow(unused)] fn main() { #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] #[repr(C)] pub struct AccountMeta { pub pubkey: Pubkey, // Account's public key pub is_signer: bool, // Must the account sign the transaction? pub is_writable: bool, // Can the account's data be modified? } }
Creating Accounts
Using the SDK
import { Connection, Keypair, SystemProgram } from '@saturnbtcio/arch-sdk';
// Generate a new account keypair
const newAccount = Keypair.generate();
// Create account instruction
const createAccountInstruction = SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: newAccount.publicKey,
lamports: 1000000, // Rent-exempt balance
space: 256, // Account data size
programId: myProgramId
});
// Send transaction
const transaction = new Transaction()
.add(createAccountInstruction);
const signature = await connection.sendAndConfirmTransaction(
transaction,
[payer, newAccount]
);
Using the Faucet (Development)
// Create and fund account with faucet (testnet/devnet only)
const newAccount = Keypair.generate();
const transaction = await connection.createAccountWithFaucet(
newAccount.publicKey
);
console.log('Account created and funded:', newAccount.publicKey.toBase58());
Reading Account Data
Get Account Information
// Get basic account info
const accountInfo = await connection.getAccountInfo(publicKey);
if (accountInfo) {
console.log('Owner:', accountInfo.owner.toBase58());
console.log('Balance:', accountInfo.lamports);
console.log('Data length:', accountInfo.data.length);
console.log('Is executable:', accountInfo.executable);
}
Get Multiple Accounts
// Get multiple accounts at once
const accounts = await connection.getMultipleAccountsInfo([
publicKey1,
publicKey2,
publicKey3
]);
accounts.forEach((account, index) => {
if (account) {
console.log(`Account ${index}:`, account.owner.toBase58());
} else {
console.log(`Account ${index}: Not found`);
}
});
Query Program Accounts
// Get all accounts owned by a program
const programAccounts = await connection.getProgramAccounts(programId);
// With filters
const filteredAccounts = await connection.getProgramAccounts(programId, {
filters: [
{
dataSize: 165 // Only accounts with exactly 165 bytes
},
{
memcmp: {
offset: 0,
bytes: '3Mc6vR' // Base58 encoded bytes to match at offset 0
}
}
]
});
Account Ownership
System Program Accounts
By default, all accounts are owned by the System Program:
import { SystemProgram } from '@saturnbtcio/arch-sdk';
// Check if account is owned by system program
const isSystemAccount = accountInfo.owner.equals(SystemProgram.programId);
Program-Owned Accounts
Programs can own accounts to store their data:
// Check if account is owned by your program
const isOwnedByMyProgram = accountInfo.owner.equals(myProgramId);
// Transfer ownership (only the current owner can do this)
const transferInstruction = SystemProgram.assign({
accountPubkey: accountPublicKey,
programId: newOwnerProgramId
});
Working with Account Data
Serialization
// Serialize data to store in account
import { serialize, deserialize } from 'borsh';
// Define your data structure
class MyAccountData {
constructor(public value: number, public name: string) {}
static schema = new Map([
[MyAccountData, {
kind: 'struct',
fields: [
['value', 'u64'],
['name', 'string']
]
}]
]);
}
// Serialize for storage
const data = new MyAccountData(42, 'Hello');
const serialized = serialize(MyAccountData.schema, data);
// Deserialize from account
const deserialized = deserialize(MyAccountData.schema, accountData);
Updating Account Data
#![allow(unused)] fn main() { // In your program use arch_sdk::{AccountInfo, ProgramResult}; pub fn update_account_data( account_info: &AccountInfo, new_value: u64 ) -> ProgramResult { // Check ownership if account_info.owner != program_id { return Err(ProgramError::IncorrectProgramId); } // Check if writable if !account_info.is_writable { return Err(ProgramError::InvalidAccountData); } // Update data let mut data = account_info.data.borrow_mut(); // ... update data Ok(()) } }
Account Security
Validation
// Always validate account ownership
function validateAccountOwnership(
account: AccountInfo,
expectedOwner: Pubkey
): boolean {
return account.owner.equals(expectedOwner);
}
// Check account signatures
function validateAccountSignature(
account: AccountInfo,
requiredSigner: Pubkey
): boolean {
return account.is_signer && account.key.equals(requiredSigner);
}
Access Control
#![allow(unused)] fn main() { // Program-side validation pub fn process_instruction( program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8] ) -> ProgramResult { let account_info = &accounts[0]; // Verify ownership if account_info.owner != program_id { return Err(ProgramError::IncorrectProgramId); } // Verify signer if !account_info.is_signer { return Err(ProgramError::MissingRequiredSignature); } // Verify writable if !account_info.is_writable { return Err(ProgramError::InvalidAccountData); } // Process the instruction Ok(()) } }
Common Patterns
Account Initialization
// Initialize account with default data
const initializeInstruction = new Instruction({
programId: myProgramId,
accounts: [
{ pubkey: newAccount.publicKey, isSigner: true, isWritable: true },
{ pubkey: payer.publicKey, isSigner: true, isWritable: false }
],
data: Buffer.from([0]) // Initialize instruction
});
Account Closure
// Close account and reclaim rent
const closeInstruction = new Instruction({
programId: myProgramId,
accounts: [
{ pubkey: accountToClose.publicKey, isSigner: true, isWritable: true },
{ pubkey: destination.publicKey, isSigner: false, isWritable: true }
],
data: Buffer.from([255]) // Close instruction
});
Error Handling
try {
const accountInfo = await connection.getAccountInfo(publicKey);
if (!accountInfo) {
throw new Error('Account not found');
}
if (!accountInfo.executable) {
throw new Error('Account is not executable');
}
} catch (error) {
if (error instanceof AccountNotFoundError) {
console.error('Account does not exist');
} else {
console.error('Error fetching account:', error);
}
}
Best Practices
Security
- Always validate ownership: Check that accounts are owned by expected programs
- Verify signatures: Ensure required accounts have signed the transaction
- Check permissions: Verify accounts have appropriate read/write permissions
- Validate data: Always validate account data before processing
Performance
- Batch account queries: Use
getMultipleAccountsInfo
for multiple accounts - Use filters: Apply filters when querying program accounts
- Cache account data: Cache frequently accessed account information
- Monitor account changes: Subscribe to account changes for real-time updates
Development
- Use TypeScript: Take advantage of type safety for account structures
- Document account layouts: Clearly document your account data structures
- Test edge cases: Test with empty accounts, invalid data, etc.
- Handle errors gracefully: Provide meaningful error messages
Examples
For complete examples working with accounts, see:
- Account Management - Creating and managing accounts
- Data Storage - Storing and retrieving data
- Token Accounts - Working with token accounts
Source Code
The account implementation is available in the Arch Examples Repository.