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., 42Registration 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 registeredState 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 cacheRelated Documentation
- Advanced Features - Explore more SDK features
- Complete Examples - View practical application code
- SDK Overview - Return to SDK documentation homepage
Last updated on