Skip to Content
SDKQuery API

Query API

MACI SDK provides rich query functions to retrieve various information.

Round Queries

Query Round by ID

// Method 1: Use getRoundInfo (recommended) const round = await client.getRoundInfo({ contractAddress: 'dora1contractaddress...' }); // Method 2: Use indexer query const round = await client.indexer.getRoundById('dora1contractaddress...'); console.log('Round info:', { title: round.title, description: round.description, status: round.status, coordinatorPubkey: round.coordinatorPubkey, numSignups: round.numSignups, votingTime: round.votingTime, voteOptionMap: round.voteOptionMap });

Get Round List

const rounds = await client.getRounds('first', 10); rounds.forEach(round => { console.log(`- ${round.title} (${round.status})`); });

Query by Status

const processingRounds = await client.indexer.getRoundsByStatus( 'Processing', 'first', 10 ); // Available statuses: 'Pending', 'Processing', 'Tallying', 'Ended' // Note: 'Voting' state was removed. User signup and voting happen during 'Pending' // state while the time window is open.

Query by Operator

const operatorRounds = await client.indexer.getRoundsByOperator( 'dora1operator...', 'first', 10 );

Query by Circuit Type

// Query all QV votes const qvRounds = await client.indexer.getRoundsByCircuitName( 'amaci-qv', 'first', 10 ); // Query all 1P1V votes const p1vRounds = await client.indexer.getRoundsByCircuitName( 'amaci-1p1v', 'first', 10 );

Operator Queries

Query Operator by Address

const operator = await client.indexer.getOperatorByAddress('dora1operator...'); console.log('Operator info:', { address: operator.address, pubkey: operator.pubkey, identity: operator.identity, isActive: operator.isActive });

Get Operator List

const operators = await client.indexer.getOperators('first', 10); operators.forEach(op => { console.log(`- ${op.identity || op.address}`); });

Circuit Queries

Query Circuit by Name

const circuit = await client.indexer.getCircuitByName('amaci-qv'); console.log('Circuit info:', { name: circuit.name, type: circuit.type, description: circuit.description });

Get All Circuits

const circuits = await client.indexer.getCircuits(); circuits.forEach(circuit => { console.log(`- ${circuit.name}: ${circuit.description}`); });

Transaction Queries

Query Transaction by Hash

const tx = await client.indexer.getTransactionByHash('ABCD1234...'); console.log('Transaction info:', { hash: tx.hash, height: tx.height, success: tx.success, gasUsed: tx.gasUsed, timestamp: tx.timestamp });

Query Transactions by Contract Address

const transactions = await client.indexer.getTransactionsByContractAddress( 'dora1contract...', 'first', 10 ); transactions.forEach(tx => { console.log(`- ${tx.type} at block ${tx.height}`); });

Get Transaction List

const recentTxs = await client.indexer.getTransactions('first', 20);

Proof Queries

Query Proof Information

const proof = await client.indexer.getProofByContractAddress('dora1contract...'); console.log('Proof info:', { contractAddress: proof.contractAddress, hasProcessMessagesProof: proof.hasProcessMessagesProof, hasTallyProof: proof.hasTallyProof, processedAt: proof.processedAt, talliedAt: proof.talliedAt });

AMACI-Specific Queries

Get Poll ID

Each AMACI contract is assigned a unique Poll ID by the Registry:

const pollId = await client.getPollId({ contractAddress: amaciAddress }); console.log('Poll ID:', pollId); // e.g., 42

Registration Status (replaces isWhitelisted / queryWhitelistBalanceOf)

A unified query that works across all registration modes:

// For SignUpWithStaticWhitelist mode const status = await client.queryRegistrationStatus({ contractAddress: amaciAddress, address: userAddress }); // For SignUpWithOracle mode (with certificate) const status = await client.queryRegistrationStatus({ contractAddress: amaciAddress, pubkey: { x: pubkeyX, y: pubkeyY }, certificate: oracleCertificate, amount: '150' // for Dynamic VC mode }); console.log('can_sign_up:', status.can_sign_up); // Whether user can register console.log('is_register:', status.is_register); // Whether already registered console.log('balance:', status.balance); // Voice credits if registered

State Index and Voice Credits

// Get a user's state index (returns increment counter) const stateIdx = await client.getStateIdxInc({ contractAddress: amaciAddress, address: userAddress }); // Get voice credit balance by state index const balance = await client.getVoiceCreditBalance({ contractAddress: amaciAddress, index: stateIdx.toString() });

Registration Configuration

// Get the round's registration and VC configuration const config = await client.getRegistrationConfig({ contractAddress: amaciAddress }); console.log('deactivate_enabled:', config.deactivate_enabled); console.log('voice_credit_mode:', config.voice_credit_mode); console.log('registration_mode:', config.registration_mode); // Check if deactivate is enabled const deactivateEnabled = await client.getDeactivateEnabled({ contractAddress: amaciAddress });

Tally Results

// Get all option results at once const results = await client.getAllResults({ contractAddress: amaciAddress }); console.log('Vote counts per option:', results); // Vec<Uint256>

Balance Queries

Query Account Balance

const balance = await client.indexer.balanceOf('dora1address...'); console.log('Balance:', { amount: balance.amount, denom: balance.denom });

Advanced Queries

Pagination

All list queries support pagination:

// First page const page1 = await client.getRounds('first', 10); // Next page (use last item's ID) const lastId = page1[page1.length - 1].id; const page2 = await client.getRounds('after', 10, lastId); // Previous page const page0 = await client.getRounds('before', 10, page1[0].id);

Combined Query Example

async function findActiveQVRounds(client: MaciClient) { const qvRounds = await client.indexer.getRoundsByCircuitName('amaci-qv', 'first', 100); // Voting happens during 'Pending' status while the time window is open. // There is no separate 'Voting' state — check both status and time window. const activeRounds = qvRounds.filter(round => { const now = Date.now() / 1000; return ( round.status === 'Pending' && round.votingTime.startTime <= now && round.votingTime.endTime >= now ); }); activeRounds.sort((a, b) => b.numSignups - a.numSignups); return activeRounds; } const popularRounds = await findActiveQVRounds(client); console.log(`Found ${popularRounds.length} active QV rounds`);

Monitor Round Status

async function waitForRoundToFinish( client: MaciClient, contractAddress: string ): Promise<void> { while (true) { const round = await client.getRoundInfo({ contractAddress }); if (round.status === 'Tallied') { console.log('Round finished'); break; } console.log(`Current status: ${round.status}`); await new Promise(resolve => setTimeout(resolve, 10000)); } }

Error Handling

async function safeQuery<T>( queryFn: () => Promise<T> ): Promise<T | null> { try { return await queryFn(); } catch (error) { console.error('Query failed:', error); return null; } } const round = await safeQuery(() => client.getRoundInfo({ contractAddress: 'dora1contract...' }) ); if (round) { console.log('Round:', round.title); } else { console.log('Round not found or query failed'); }

Cached Queries

For frequently queried data, implement simple caching:

class CachedMaciClient { private cache = new Map<string, { data: any; expiry: number }>(); private cacheDuration = 60000; constructor(private client: MaciClient) {} async getRoundById(contractAddress: string) { const key = `round:${contractAddress}`; const cached = this.cache.get(key); if (cached && cached.expiry > Date.now()) { return cached.data; } const data = await this.client.getRoundInfo({ contractAddress }); this.cache.set(key, { data, expiry: Date.now() + this.cacheDuration }); return data; } } const cachedClient = new CachedMaciClient(client); const round1 = await cachedClient.getRoundById('dora1...'); const round2 = await cachedClient.getRoundById('dora1...'); // Uses cache
Last updated on