getActiveValidators method

Future<List<ValidatorInfo>> getActiveValidators({
  1. int? epochId,
  2. int first = 50,
  3. String? after,
})

Active validators for the current (or given) epoch, parsed from each validator's on-chain 0x3::validator::Validator contents.

Note: annualized APY is not a stored field — it must be derived from each staking pool's exchange-rate history (a separate traversal). The raw stakingPoolSuiBalance / poolTokenBalance needed for that are exposed.

Implementation

Future<List<ValidatorInfo>> getActiveValidators({
  int? epochId,
  int first = 50,
  String? after,
}) async {
  // The endpoint caps page size at 50; paginate internally to gather all.
  const q = r'''
    query ($id: UInt53, $first: Int!, $after: String) {
      epoch(epochId: $id) {
        validatorSet {
          activeValidators(first: $first, after: $after) {
            pageInfo { hasNextPage endCursor }
            nodes { contents { json } }
          }
        }
      }
    }
  ''';
  final pageSize = first > 50 ? 50 : first;
  final out = <ValidatorInfo>[];
  String? cursor = after;
  while (true) {
    final data = await transport.query(q, variables: {
      if (epochId != null) 'id': epochId,
      'first': pageSize,
      if (cursor != null) 'after': cursor,
    });
    final vs = (((data['epoch'] as Map)['validatorSet']
        as Map)['activeValidators'] as Map);
    out.addAll((vs['nodes'] as List).map((n) => ValidatorInfo._fromJson(
        (n as Map<String, dynamic>)['contents']['json']
            as Map<String, dynamic>)));
    final pageInfo = vs['pageInfo'] as Map<String, dynamic>;
    if (pageInfo['hasNextPage'] != true) break;
    cursor = pageInfo['endCursor'] as String?;
    if (cursor == null) break;
  }
  return out;
}