mirror of
https://github.com/pezkuwichain/pezkuwi-subquery.git
synced 2026-04-21 23:37:56 +00:00
fix: save pool stash accounts with relaychain type for wallet compat
The wallet queries activeStakers with the pool's bonded stash address and stakingType="relaychain" (unwrapped from nomination-pool). Derive pool stash accounts from bondedPools and save with correct type.
This commit is contained in:
+63
-37
@@ -3,53 +3,78 @@ import { ActiveStaker } from "../types";
|
||||
import { Option } from "@pezkuwi/types";
|
||||
import {
|
||||
PEZKUWI_ASSET_HUB_GENESIS,
|
||||
STAKING_TYPE_NOMINATION_POOL,
|
||||
STAKING_TYPE_RELAYCHAIN,
|
||||
} from "./constants";
|
||||
|
||||
let poolMembersInitialized = false;
|
||||
let poolStakersInitialized = false;
|
||||
|
||||
/**
|
||||
* Derive the bonded (stash) account for a nomination pool.
|
||||
* Formula: PalletId("py/nopls") + encode((AccountType::Bonded=0, poolId)) padded to 32 bytes
|
||||
* This matches Substrate's PalletId::into_sub_account_truncating
|
||||
*/
|
||||
function derivePoolStash(poolId: number): string {
|
||||
const buf = new Uint8Array(32);
|
||||
// PalletId: "py/nopls" (8 bytes)
|
||||
const palletId = [0x70, 0x79, 0x2f, 0x6e, 0x6f, 0x70, 0x6c, 0x73];
|
||||
for (let i = 0; i < 8; i++) buf[i] = palletId[i];
|
||||
// AccountType::Bonded = 0
|
||||
buf[8] = 0;
|
||||
// Pool ID as u32 LE
|
||||
buf[9] = poolId & 0xff;
|
||||
buf[10] = (poolId >> 8) & 0xff;
|
||||
buf[11] = (poolId >> 16) & 0xff;
|
||||
buf[12] = (poolId >> 24) & 0xff;
|
||||
// Remaining bytes are already 0 (padding)
|
||||
return api.registry.createType("AccountId", buf).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Block handler: on the FIRST block processed, query the live chain state
|
||||
* for all current nomination pool members and save them as ActiveStakers.
|
||||
* This ensures existing pool members are captured even if their Bonded
|
||||
* events were in pruned blocks.
|
||||
* for all bonded pools and save their stash accounts as ActiveStakers.
|
||||
*
|
||||
* The wallet queries activeStakers with:
|
||||
* - address: pool stash (bonded) account
|
||||
* - stakingType: "relaychain" (unwrapped from nomination-pool)
|
||||
* - networkId: AH genesis
|
||||
*/
|
||||
export async function handleBlock(block: SubstrateBlock): Promise<void> {
|
||||
if (poolMembersInitialized) return;
|
||||
poolMembersInitialized = true;
|
||||
if (poolStakersInitialized) return;
|
||||
poolStakersInitialized = true;
|
||||
|
||||
logger.info("Initializing active pool stakers from live chain state...");
|
||||
logger.info("Initializing pool stash accounts from live chain state...");
|
||||
|
||||
const members = await api.query.nominationPools.poolMembers.entries();
|
||||
const pools = await api.query.nominationPools.bondedPools.entries();
|
||||
let count = 0;
|
||||
|
||||
for (const [key, memberOpt] of members) {
|
||||
const member = (memberOpt as Option<any>);
|
||||
if (member.isNone) continue;
|
||||
for (const [key, poolOpt] of pools) {
|
||||
const pool = poolOpt as Option<any>;
|
||||
if (pool.isNone) continue;
|
||||
|
||||
const unwrapped = member.unwrap();
|
||||
const unwrapped = pool.unwrap();
|
||||
if (unwrapped.points.toBigInt() === BigInt(0)) continue;
|
||||
|
||||
const address = key.args[0].toString();
|
||||
const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_NOMINATION_POOL}-${address}`;
|
||||
const poolId = (key.args[0] as any).toNumber();
|
||||
const stashAddress = derivePoolStash(poolId);
|
||||
|
||||
const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_RELAYCHAIN}-${stashAddress}`;
|
||||
const staker = ActiveStaker.create({
|
||||
id: stakerId,
|
||||
networkId: PEZKUWI_ASSET_HUB_GENESIS,
|
||||
stakingType: STAKING_TYPE_NOMINATION_POOL,
|
||||
address,
|
||||
stakingType: STAKING_TYPE_RELAYCHAIN,
|
||||
address: stashAddress,
|
||||
});
|
||||
await staker.save();
|
||||
count++;
|
||||
}
|
||||
|
||||
logger.info(`Initialized ${count} active pool stakers from chain state`);
|
||||
logger.info(`Initialized ${count} pool stash accounts as active stakers`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle nominationPools.Bonded event
|
||||
* Fired when a member bonds (joins or adds more) to a nomination pool.
|
||||
* Creates an ActiveStaker entry for this address.
|
||||
* When a member bonds to a pool, ensure the pool's stash account is
|
||||
* saved as an ActiveStaker with relaychain type.
|
||||
*
|
||||
* Event data: [member: AccountId, pool_id: u32, bonded: Balance, joined: bool]
|
||||
*/
|
||||
@@ -58,28 +83,29 @@ export async function handlePoolBonded(
|
||||
): Promise<void> {
|
||||
const {
|
||||
event: {
|
||||
data: [memberEncoded],
|
||||
data: [, poolIdEncoded],
|
||||
},
|
||||
} = event;
|
||||
|
||||
const address = memberEncoded.toString();
|
||||
const poolId = (poolIdEncoded as any).toNumber();
|
||||
const stashAddress = derivePoolStash(poolId);
|
||||
|
||||
const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_NOMINATION_POOL}-${address}`;
|
||||
const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_RELAYCHAIN}-${stashAddress}`;
|
||||
const staker = ActiveStaker.create({
|
||||
id: stakerId,
|
||||
networkId: PEZKUWI_ASSET_HUB_GENESIS,
|
||||
stakingType: STAKING_TYPE_NOMINATION_POOL,
|
||||
address,
|
||||
stakingType: STAKING_TYPE_RELAYCHAIN,
|
||||
address: stashAddress,
|
||||
});
|
||||
await staker.save();
|
||||
|
||||
logger.info(`Pool staker added: ${address}`);
|
||||
logger.info(`Pool ${poolId} stash saved: ${stashAddress}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle nominationPools.Unbonded event
|
||||
* Fired when a member unbonds from a nomination pool.
|
||||
* If the member has no remaining points, remove the ActiveStaker entry.
|
||||
* If the pool has no remaining points after unbond, remove the stash
|
||||
* from ActiveStakers.
|
||||
*
|
||||
* Event data: [member: AccountId, pool_id: u32, balance: Balance, points: Balance, era: u32]
|
||||
*/
|
||||
@@ -88,21 +114,21 @@ export async function handlePoolUnbonded(
|
||||
): Promise<void> {
|
||||
const {
|
||||
event: {
|
||||
data: [memberEncoded],
|
||||
data: [, poolIdEncoded],
|
||||
},
|
||||
} = event;
|
||||
|
||||
const address = memberEncoded.toString();
|
||||
const poolId = (poolIdEncoded as any).toNumber();
|
||||
|
||||
// Check if member still has points in the pool
|
||||
const memberData = (await api.query.nominationPools.poolMembers(
|
||||
address,
|
||||
// Check if pool still has points
|
||||
const poolData = (await api.query.nominationPools.bondedPools(
|
||||
poolId,
|
||||
)) as Option<any>;
|
||||
|
||||
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}`;
|
||||
if (poolData.isNone || poolData.unwrap().points.toBigInt() === BigInt(0)) {
|
||||
const stashAddress = derivePoolStash(poolId);
|
||||
const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_RELAYCHAIN}-${stashAddress}`;
|
||||
await ActiveStaker.remove(stakerId);
|
||||
logger.info(`Pool staker removed: ${address}`);
|
||||
logger.info(`Pool ${poolId} stash removed: ${stashAddress}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user