From 1c79402d5cf12039d5c125ffdbcbc041781ec1b8 Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Wed, 18 Feb 2026 03:14:44 +0300 Subject: [PATCH] feat: track nomination pool members as active stakers Add handlePoolBonded/handlePoolUnbonded handlers to Asset Hub SubQuery. When a user bonds to a nomination pool, they are saved as an ActiveStaker with networkId=AssetHub and stakingType=nomination-pool. This fixes the wallet showing INACTIVE for HEZ stakers. --- pezkuwi-assethub.yaml | 12 +++++++ src/index.ts | 1 + src/mappings/PoolStakers.ts | 68 +++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/mappings/PoolStakers.ts diff --git a/pezkuwi-assethub.yaml b/pezkuwi-assethub.yaml index 9f25c0c..94c251d 100644 --- a/pezkuwi-assethub.yaml +++ b/pezkuwi-assethub.yaml @@ -34,6 +34,18 @@ dataSources: filter: module: nominationPools method: PaidOut + # Pool member bonded (track active stakers) + - handler: handlePoolBonded + kind: substrate/EventHandler + filter: + module: nominationPools + method: Bonded + # Pool member unbonded (cleanup active stakers) + - handler: handlePoolUnbonded + kind: substrate/EventHandler + filter: + module: nominationPools + method: Unbonded # Pool bonded slash - handler: handlePoolBondedSlash kind: substrate/EventHandler diff --git a/src/index.ts b/src/index.ts index 051f51c..2e700e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,4 +4,5 @@ export * from "./mappings/Rewards"; export * from "./mappings/PoolRewards"; export * from "./mappings/Transfers"; export * from "./mappings/NewEra"; +export * from "./mappings/PoolStakers"; import "@pezkuwi/api-augment"; diff --git a/src/mappings/PoolStakers.ts b/src/mappings/PoolStakers.ts new file mode 100644 index 0000000..c22e39c --- /dev/null +++ b/src/mappings/PoolStakers.ts @@ -0,0 +1,68 @@ +import { SubstrateEvent } from "@subql/types"; +import { ActiveStaker } from "../types"; +import { Option } from "@pezkuwi/types"; +import { + PEZKUWI_ASSET_HUB_GENESIS, + STAKING_TYPE_NOMINATION_POOL, +} from "./constants"; + +/** + * Handle nominationPools.Bonded event + * Fired when a member bonds (joins or adds more) to a nomination pool. + * Creates an ActiveStaker entry for this address. + * + * Event data: [member: AccountId, pool_id: u32, bonded: Balance, joined: bool] + */ +export async function handlePoolBonded( + event: SubstrateEvent, +): Promise { + const { + event: { + data: [memberEncoded], + }, + } = event; + + const address = memberEncoded.toString(); + + const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_NOMINATION_POOL}-${address}`; + const staker = ActiveStaker.create({ + id: stakerId, + networkId: PEZKUWI_ASSET_HUB_GENESIS, + stakingType: STAKING_TYPE_NOMINATION_POOL, + address, + }); + await staker.save(); + + logger.info(`Pool staker added: ${address}`); +} + +/** + * Handle nominationPools.Unbonded event + * Fired when a member unbonds from a nomination pool. + * If the member has no remaining points, remove the ActiveStaker entry. + * + * Event data: [member: AccountId, pool_id: u32, balance: Balance, points: Balance, era: u32] + */ +export async function handlePoolUnbonded( + event: SubstrateEvent, +): Promise { + const { + event: { + data: [memberEncoded], + }, + } = event; + + const address = memberEncoded.toString(); + + // Check if member still has points in the pool + const memberData = (await api.query.nominationPools.poolMembers( + address, + )) as Option; + + if (memberData.isNone || memberData.unwrap().points.toBigInt() === BigInt(0)) { + // Member fully left the pool - remove active staker + const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_NOMINATION_POOL}-${address}`; + await ActiveStaker.remove(stakerId); + logger.info(`Pool staker removed: ${address}`); + } +}