mirror of
https://github.com/pezkuwichain/pezkuwi-subquery.git
synced 2026-06-14 22:51:10 +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 { Option } from "@pezkuwi/types";
|
||||||
import {
|
import {
|
||||||
PEZKUWI_ASSET_HUB_GENESIS,
|
PEZKUWI_ASSET_HUB_GENESIS,
|
||||||
STAKING_TYPE_NOMINATION_POOL,
|
STAKING_TYPE_RELAYCHAIN,
|
||||||
} from "./constants";
|
} 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
|
* Block handler: on the FIRST block processed, query the live chain state
|
||||||
* for all current nomination pool members and save them as ActiveStakers.
|
* for all bonded pools and save their stash accounts as ActiveStakers.
|
||||||
* This ensures existing pool members are captured even if their Bonded
|
*
|
||||||
* events were in pruned blocks.
|
* 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> {
|
export async function handleBlock(block: SubstrateBlock): Promise<void> {
|
||||||
if (poolMembersInitialized) return;
|
if (poolStakersInitialized) return;
|
||||||
poolMembersInitialized = true;
|
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;
|
let count = 0;
|
||||||
|
|
||||||
for (const [key, memberOpt] of members) {
|
for (const [key, poolOpt] of pools) {
|
||||||
const member = (memberOpt as Option<any>);
|
const pool = poolOpt as Option<any>;
|
||||||
if (member.isNone) continue;
|
if (pool.isNone) continue;
|
||||||
|
|
||||||
const unwrapped = member.unwrap();
|
const unwrapped = pool.unwrap();
|
||||||
if (unwrapped.points.toBigInt() === BigInt(0)) continue;
|
if (unwrapped.points.toBigInt() === BigInt(0)) continue;
|
||||||
|
|
||||||
const address = key.args[0].toString();
|
const poolId = (key.args[0] as any).toNumber();
|
||||||
const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_NOMINATION_POOL}-${address}`;
|
const stashAddress = derivePoolStash(poolId);
|
||||||
|
|
||||||
|
const stakerId = `${PEZKUWI_ASSET_HUB_GENESIS}-${STAKING_TYPE_RELAYCHAIN}-${stashAddress}`;
|
||||||
const staker = ActiveStaker.create({
|
const staker = ActiveStaker.create({
|
||||||
id: stakerId,
|
id: stakerId,
|
||||||
networkId: PEZKUWI_ASSET_HUB_GENESIS,
|
networkId: PEZKUWI_ASSET_HUB_GENESIS,
|
||||||
stakingType: STAKING_TYPE_NOMINATION_POOL,
|
stakingType: STAKING_TYPE_RELAYCHAIN,
|
||||||
address,
|
address: stashAddress,
|
||||||
});
|
});
|
||||||
await staker.save();
|
await staker.save();
|
||||||
count++;
|
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
|
* Handle nominationPools.Bonded event
|
||||||
* Fired when a member bonds (joins or adds more) to a nomination pool.
|
* When a member bonds to a pool, ensure the pool's stash account is
|
||||||
* Creates an ActiveStaker entry for this address.
|
* saved as an ActiveStaker with relaychain type.
|
||||||
*
|
*
|
||||||
* Event data: [member: AccountId, pool_id: u32, bonded: Balance, joined: bool]
|
* Event data: [member: AccountId, pool_id: u32, bonded: Balance, joined: bool]
|
||||||
*/
|
*/
|
||||||
@@ -58,28 +83,29 @@ export async function handlePoolBonded(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {
|
const {
|
||||||
event: {
|
event: {
|
||||||
data: [memberEncoded],
|
data: [, poolIdEncoded],
|
||||||
},
|
},
|
||||||
} = event;
|
} = 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({
|
const staker = ActiveStaker.create({
|
||||||
id: stakerId,
|
id: stakerId,
|
||||||
networkId: PEZKUWI_ASSET_HUB_GENESIS,
|
networkId: PEZKUWI_ASSET_HUB_GENESIS,
|
||||||
stakingType: STAKING_TYPE_NOMINATION_POOL,
|
stakingType: STAKING_TYPE_RELAYCHAIN,
|
||||||
address,
|
address: stashAddress,
|
||||||
});
|
});
|
||||||
await staker.save();
|
await staker.save();
|
||||||
|
|
||||||
logger.info(`Pool staker added: ${address}`);
|
logger.info(`Pool ${poolId} stash saved: ${stashAddress}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle nominationPools.Unbonded event
|
* Handle nominationPools.Unbonded event
|
||||||
* Fired when a member unbonds from a nomination pool.
|
* If the pool has no remaining points after unbond, remove the stash
|
||||||
* If the member has no remaining points, remove the ActiveStaker entry.
|
* from ActiveStakers.
|
||||||
*
|
*
|
||||||
* Event data: [member: AccountId, pool_id: u32, balance: Balance, points: Balance, era: u32]
|
* Event data: [member: AccountId, pool_id: u32, balance: Balance, points: Balance, era: u32]
|
||||||
*/
|
*/
|
||||||
@@ -88,21 +114,21 @@ export async function handlePoolUnbonded(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {
|
const {
|
||||||
event: {
|
event: {
|
||||||
data: [memberEncoded],
|
data: [, poolIdEncoded],
|
||||||
},
|
},
|
||||||
} = event;
|
} = event;
|
||||||
|
|
||||||
const address = memberEncoded.toString();
|
const poolId = (poolIdEncoded as any).toNumber();
|
||||||
|
|
||||||
// Check if member still has points in the pool
|
// Check if pool still has points
|
||||||
const memberData = (await api.query.nominationPools.poolMembers(
|
const poolData = (await api.query.nominationPools.bondedPools(
|
||||||
address,
|
poolId,
|
||||||
)) as Option<any>;
|
)) as Option<any>;
|
||||||
|
|
||||||
if (memberData.isNone || memberData.unwrap().points.toBigInt() === BigInt(0)) {
|
if (poolData.isNone || poolData.unwrap().points.toBigInt() === BigInt(0)) {
|
||||||
// Member fully left the pool - remove active staker
|
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}`;
|
||||||
await ActiveStaker.remove(stakerId);
|
await ActiveStaker.remove(stakerId);
|
||||||
logger.info(`Pool staker removed: ${address}`);
|
logger.info(`Pool ${poolId} stash removed: ${stashAddress}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user