Migrate to native @pezkuwi packages and remove swap integrations

Replace npm aliases (@polkadot -> @pezkuwi) with direct @pezkuwi package
imports. Add tsconfig path mappings to redirect @polkadot type references
from @subql/types to @pezkuwi equivalents, enabling proper type resolution
without installing @polkadot packages. Remove Polkadot ecosystem swap/bridge
integrations (HydraDx, AssetConversion) as they're incompatible with Pezkuwi.
This commit is contained in:
2026-02-13 01:36:52 +03:00
parent ab2a8acb6d
commit 2f1d6dd316
14 changed files with 9383 additions and 13660 deletions
+11 -9
View File
@@ -19,15 +19,17 @@
"author": "Pezkuwi Team", "author": "Pezkuwi Team",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"lodash": "^4.17.21", "@pezkuwi/api": "^16.5.36",
"@polkadot/api": "npm:@pezkuwi/api@^16.5.34", "@pezkuwi/api-augment": "^16.5.36",
"@polkadot/api-derive": "npm:@pezkuwi/api-derive@^16.5.34", "@pezkuwi/api-derive": "^16.5.36",
"@polkadot/types": "npm:@pezkuwi/types@^16.5.34", "@pezkuwi/keyring": "^14.0.25",
"@polkadot/types-augment": "npm:@pezkuwi/types-augment@^16.5.34", "@pezkuwi/types": "^16.5.36",
"@polkadot/types-codec": "npm:@pezkuwi/types-codec@^16.5.34", "@pezkuwi/types-augment": "^16.5.36",
"@polkadot/util": "npm:@pezkuwi/util@^14.0.23", "@pezkuwi/types-codec": "^16.5.36",
"@polkadot/util-crypto": "npm:@pezkuwi/util-crypto@^14.0.23", "@pezkuwi/types-create": "^16.5.36",
"@polkadot/keyring": "npm:@pezkuwi/keyring@^14.0.23" "@pezkuwi/util": "^14.0.25",
"@pezkuwi/util-crypto": "^14.0.25",
"lodash": "^4.17.21"
}, },
"devDependencies": { "devDependencies": {
"@subql/cli": "latest", "@subql/cli": "latest",
+1 -2
View File
@@ -4,5 +4,4 @@ export * from "./mappings/Rewards";
export * from "./mappings/PoolRewards"; export * from "./mappings/PoolRewards";
export * from "./mappings/Transfers"; export * from "./mappings/Transfers";
export * from "./mappings/NewEra"; export * from "./mappings/NewEra";
export * from "./mappings/swaps"; import "@pezkuwi/api-augment";
import "@polkadot/api-augment";
+16 -31
View File
@@ -1,16 +1,11 @@
import "@polkadot/types-augment/lookup";
import { SubstrateEvent } from "@subql/types"; import { SubstrateEvent } from "@subql/types";
import { blockNumber } from "./common"; import { blockNumber } from "./common";
import { AccountId } from "@polkadot/types/interfaces"; import { Option } from "@pezkuwi/types";
import { import { RewardDestination, AccountId } from "@pezkuwi/types/interfaces";
PalletStakingRewardDestination,
PalletNominationPoolsPoolMember,
} from "@polkadot/types/lookup";
import { Option } from "@polkadot/types";
// Due to memory consumption optimization `rewardDestinationByAddress` contains only one key // Due to memory consumption optimization `rewardDestinationByAddress` contains only one key
let rewardDestinationByAddress: { let rewardDestinationByAddress: {
[blockId: string]: { [address: string]: PalletStakingRewardDestination }; [blockId: string]: { [address: string]: RewardDestination };
} = {}; } = {};
let controllersByStash: { [blockId: string]: { [address: string]: string } } = let controllersByStash: { [blockId: string]: { [address: string]: string } } =
{}; {};
@@ -18,13 +13,13 @@ let controllersByStash: { [blockId: string]: { [address: string]: string } } =
let parachainStakingRewardEra: { [blockId: string]: number } = {}; let parachainStakingRewardEra: { [blockId: string]: number } = {};
let poolMembers: { let poolMembers: {
[blockId: number]: [string, PalletNominationPoolsPoolMember][]; [blockId: number]: [string, any][];
} = {}; } = {};
export async function cachedRewardDestination( export async function cachedRewardDestination(
accountAddress: string, accountAddress: string,
event: SubstrateEvent, event: SubstrateEvent,
): Promise<PalletStakingRewardDestination> { ): Promise<RewardDestination> {
const blockId = blockNumber(event); const blockId = blockNumber(event);
let cachedBlock = rewardDestinationByAddress[blockId]; let cachedBlock = rewardDestinationByAddress[blockId];
@@ -43,7 +38,7 @@ export async function cachedRewardDestination(
}); });
let destinationByAddress: { let destinationByAddress: {
[address: string]: PalletStakingRewardDestination; [address: string]: RewardDestination;
} = {}; } = {};
let { let {
@@ -58,8 +53,7 @@ export async function cachedRewardDestination(
}, },
} = event; } = event;
let accountAddress = accountId.toString(); let accountAddress = accountId.toString();
destinationByAddress[accountAddress] = destinationByAddress[accountAddress] = destination as unknown as RewardDestination;
destination as PalletStakingRewardDestination;
}); });
} else { } else {
const allAccountsInBlock = allEventsInBlock.map((event) => { const allAccountsInBlock = allEventsInBlock.map((event) => {
@@ -74,26 +68,20 @@ export async function cachedRewardDestination(
// looks like accountAddress not related to events so just try to query payee directly // looks like accountAddress not related to events so just try to query payee directly
if (allAccountsInBlock.length === 0) { if (allAccountsInBlock.length === 0) {
rewardDestinationByAddress[blockId] = {}; rewardDestinationByAddress[blockId] = {};
return (await api.query.staking.payee( return (await api.query.staking.payee(accountAddress)) as unknown as RewardDestination;
accountAddress,
)) as unknown as PalletStakingRewardDestination;
} }
// TODO: Commented code doesn't work now, may be fixed later
// const payees = await api.query.staking.payee.multi(allAccountsInBlock);
const payees = await api.queryMulti( const payees = await api.queryMulti(
allAccountsInBlock.map((account) => [api.query.staking.payee, account]), allAccountsInBlock.map((account) => [api.query.staking.payee, account]),
); );
const rewardDestinations = payees.map((payee) => { const rewardDestinations = payees.map((payee) => {
return payee as PalletStakingRewardDestination; return payee as unknown as RewardDestination;
}); });
// something went wrong, so just query for single accountAddress // something went wrong, so just query for single accountAddress
if (rewardDestinations.length !== allAccountsInBlock.length) { if (rewardDestinations.length !== allAccountsInBlock.length) {
const payee = (await api.query.staking.payee( const payee = (await api.query.staking.payee(accountAddress)) as unknown as RewardDestination;
accountAddress,
)) as unknown as PalletStakingRewardDestination;
destinationByAddress[accountAddress] = payee; destinationByAddress[accountAddress] = payee;
rewardDestinationByAddress[blockId] = destinationByAddress; rewardDestinationByAddress[blockId] = destinationByAddress;
return payee; return payee;
@@ -150,7 +138,7 @@ export async function cachedController(
); );
if (rewardDestination.isController) { if (rewardDestination.isController) {
controllerNeedAccounts.push(accountId as AccountId); controllerNeedAccounts.push(accountId as unknown as AccountId);
} }
} }
@@ -161,8 +149,6 @@ export async function cachedController(
return accountId.toString(); return accountId.toString();
} }
// TODO: Commented code doesn't work now, may be fixed later
// const bonded = await api.query.staking.bonded.multi(controllerNeedAccounts);
const bonded = await api.queryMulti( const bonded = await api.queryMulti(
controllerNeedAccounts.map((account) => [ controllerNeedAccounts.map((account) => [
api.query.staking.bonded, api.query.staking.bonded,
@@ -207,7 +193,7 @@ export async function cachedStakingRewardEraIndex(
api.consts.parachainStaking.rewardPaymentDelay.toHuman(); api.consts.parachainStaking.rewardPaymentDelay.toHuman();
// HACK: used to get data from object // HACK: used to get data from object
const eraIndex = const eraIndex =
(era.toJSON() as { current: any }).current - Number(paymentDelay); (era.toJSON() as { current: number }).current - Number(paymentDelay);
parachainStakingRewardEra = {}; parachainStakingRewardEra = {};
parachainStakingRewardEra[blockId] = eraIndex; parachainStakingRewardEra[blockId] = eraIndex;
@@ -217,22 +203,21 @@ export async function cachedStakingRewardEraIndex(
export async function getPoolMembers( export async function getPoolMembers(
blockId: number, blockId: number,
): Promise<[string, PalletNominationPoolsPoolMember][]> { ): Promise<[string, any][]> {
const cachedMembers = poolMembers[blockId]; const cachedMembers = poolMembers[blockId];
if (cachedMembers != undefined) { if (cachedMembers != undefined) {
return cachedMembers; return cachedMembers;
} }
const members: [string, PalletNominationPoolsPoolMember][] = ( const members: [string, any][] = (
await api.query.nominationPools.poolMembers.entries() await api.query.nominationPools.poolMembers.entries()
) )
.filter( .filter(
([_, member]) => ([_key, member]) => (member as Option<any>).isSome,
(member as Option<PalletNominationPoolsPoolMember>).isSome,
) )
.map(([accountId, member]) => [ .map(([accountId, member]) => [
accountId.args[0].toString(), accountId.args[0].toString(),
(member as Option<PalletNominationPoolsPoolMember>).unwrap(), (member as Option<any>).unwrap(),
]); ]);
poolMembers = {}; poolMembers = {};
poolMembers[blockId] = members; poolMembers[blockId] = members;
+35 -386
View File
@@ -1,67 +1,22 @@
import { SubstrateExtrinsic } from "@subql/types"; import { SubstrateExtrinsic } from "@subql/types";
import { AssetTransfer, HistoryElement, Transfer, Swap } from "../types"; import { HistoryElement, Transfer } from "../types";
import { import {
getAssetIdFromMultilocation,
getEventData,
callFromProxy, callFromProxy,
callsFromBatch, callsFromBatch,
calculateFeeAsString, calculateFeeAsString,
extrinsicIdFromBlockAndIdx, extrinsicIdFromBlockAndIdx,
eventRecordToSubstrateEvent,
isBatch, isBatch,
isProxy, isProxy,
timestamp,
isNativeTransfer, isNativeTransfer,
isAssetTransfer,
isOrmlTransfer,
isSwapExactTokensForTokens,
isSwapTokensForExactTokens,
isNativeTransferAll, isNativeTransferAll,
isOrmlTransferAll, timestamp,
isEvmTransaction,
isEvmExecutedEvent,
isAssetTxFeePaidEvent,
isEquilibriumTransfer,
isHydraOmnipoolBuy,
isHydraOmnipoolSell,
isHydraRouterSell,
isHydraRouterBuy,
convertOrmlCurrencyIdToString,
} from "./common"; } from "./common";
import { CallBase } from "@polkadot/types/types/calls";
import { AnyTuple } from "@polkadot/types/types/codec";
import { u64 } from "@polkadot/types";
import { ethereumEncode } from "@polkadot/util-crypto";
import { u128, u32 } from "@polkadot/types-codec";
import { convertHydraDxTokenIdToString, findHydraDxFeeTyped } from "./swaps";
import { Codec } from "@polkadot/types/types";
type TransferData = { type TransferData = {
isTransferAll: boolean; isTransferAll: boolean;
transfer: Transfer | AssetTransfer | Swap; transfer: Transfer;
}; };
type TransferCallback = (
isTransferAll: boolean,
address: string,
amount: any,
assetId?: string
) => Array<{ isTransferAll: boolean; transfer: Transfer }>;
type AssetHubSwapCallback = (
path: any,
amountId: Codec,
amountOut: Codec,
receiver: Codec
) => Array<{ isTransferAll: boolean; transfer: Swap }>;
type HydraDxSwapCallback = (
assetIn: Codec,
assetOut: Codec,
amountIn: Codec,
amountOut: Codec
) => { isTransferAll: boolean; transfer: Swap };
export async function handleHistoryElement( export async function handleHistoryElement(
extrinsic: SubstrateExtrinsic extrinsic: SubstrateExtrinsic
): Promise<void> { ): Promise<void> {
@@ -74,11 +29,6 @@ export async function handleHistoryElement(
} else { } else {
await saveExtrinsic(extrinsic); await saveExtrinsic(extrinsic);
} }
} else if (
isEvmTransaction(extrinsic.extrinsic.method) &&
extrinsic.success
) {
await saveEvmExtrinsic(extrinsic);
} }
} }
@@ -89,14 +39,14 @@ function createHistoryElement(
hash?: string hash?: string
) { ) {
let extrinsicHash = hash || extrinsic.extrinsic.hash.toString(); let extrinsicHash = hash || extrinsic.extrinsic.hash.toString();
let blockNumber = extrinsic.block.block.header.number.toNumber(); let blockNum = extrinsic.block.block.header.number.toNumber();
let extrinsicIdx = extrinsic.idx; let extrinsicIdx = extrinsic.idx;
let extrinsicId = extrinsicIdFromBlockAndIdx(blockNumber, extrinsicIdx); let extrinsicId = extrinsicIdFromBlockAndIdx(blockNum, extrinsicIdx);
let blockTimestamp = timestamp(extrinsic.block); let blockTimestamp = timestamp(extrinsic.block);
const historyElement = HistoryElement.create({ const historyElement = HistoryElement.create({
id: `${extrinsicId}${suffix}`, id: `${extrinsicId}${suffix}`,
blockNumber, blockNumber: blockNum,
timestamp: blockTimestamp, timestamp: blockTimestamp,
address, address,
}); });
@@ -107,35 +57,17 @@ function createHistoryElement(
return historyElement; return historyElement;
} }
function addTransferToHistoryElement(
element: HistoryElement,
transfer: Transfer | AssetTransfer | Swap
) {
if ("assetIdIn" in transfer) {
element.swap = transfer;
} else if ("assetId" in transfer) {
element.assetTransfer = transfer;
} else {
element.transfer = transfer;
}
}
async function saveFailedTransfers( async function saveFailedTransfers(
transfers: Array<TransferData>, transfers: Array<TransferData>,
extrinsic: SubstrateExtrinsic extrinsic: SubstrateExtrinsic
): Promise<void> { ): Promise<void> {
for (const { isTransferAll, transfer } of transfers) { for (const { isTransferAll, transfer } of transfers) {
const isSwap = "assetIdIn" in transfer; const elementFrom = createHistoryElement(extrinsic, transfer.from, `-from`);
const from = isSwap ? transfer.sender : transfer.from; elementFrom.transfer = transfer;
const to = isSwap ? transfer.receiver : transfer.to;
const elementFrom = createHistoryElement(extrinsic, from, `-from`);
addTransferToHistoryElement(elementFrom, transfer);
// FIXME: Try to find more appropriate way to handle failed transferAll events
if ((!isTransferAll && !isSwap) || from.toString() != to.toString()) {
const elementTo = createHistoryElement(extrinsic, to, `-to`);
addTransferToHistoryElement(elementTo, transfer);
if (!isTransferAll || transfer.from !== transfer.to) {
const elementTo = createHistoryElement(extrinsic, transfer.to, `-to`);
elementTo.transfer = transfer;
await elementTo.save(); await elementTo.save();
} }
@@ -160,35 +92,6 @@ async function saveExtrinsic(extrinsic: SubstrateExtrinsic): Promise<void> {
await element.save(); await element.save();
} }
async function saveEvmExtrinsic(extrinsic: SubstrateExtrinsic): Promise<void> {
const executedEvent = extrinsic.events.find(isEvmExecutedEvent);
if (!executedEvent) {
return;
}
const addressFrom = ethereumEncode(executedEvent.event.data?.[0]?.toString());
const hash = executedEvent.event.data?.[2]?.toString();
const success = !!(executedEvent.event.data?.[3].toJSON() as any).succeed;
const element = createHistoryElement(
extrinsic,
addressFrom,
"-extrinsic",
hash
);
element.extrinsic = {
hash,
module: extrinsic.extrinsic.method.section,
call: extrinsic.extrinsic.method.method,
success,
fee: calculateFeeAsString(extrinsic, addressFrom),
};
await element.save();
}
/// Success Transfer emits Transfer event that is handled at Transfers.ts handleTransfer()
function findFailedTransferCalls( function findFailedTransferCalls(
extrinsic: SubstrateExtrinsic extrinsic: SubstrateExtrinsic
): Array<TransferData> | null { ): Array<TransferData> | null {
@@ -197,121 +100,30 @@ function findFailedTransferCalls(
} }
let sender = extrinsic.extrinsic.signer; let sender = extrinsic.extrinsic.signer;
const transferCallback: TransferCallback = (
isTransferAll,
address,
amount,
assetId?
) => {
const transfer: Transfer = {
amount: amount.toString(),
from: sender.toString(),
to: address,
fee: calculateFeeAsString(extrinsic),
eventIdx: -1,
success: false,
};
if (assetId) {
(transfer as AssetTransfer).assetId = assetId;
}
return [
{
isTransferAll,
transfer,
},
];
};
const assetHubSwapCallback: AssetHubSwapCallback = (
path,
amountIn,
amountOut,
receiver
) => {
let assetIdFee = "native";
let fee = calculateFeeAsString(extrinsic);
let foundAssetTxFeePaid = extrinsic.block.events.find((e) =>
isAssetTxFeePaidEvent(eventRecordToSubstrateEvent(e))
);
if (foundAssetTxFeePaid !== undefined) {
const [who, actual_fee, tip, rawAssetIdFee] = getEventData(
eventRecordToSubstrateEvent(foundAssetTxFeePaid)
);
if ("interior" in rawAssetIdFee) {
assetIdFee = getAssetIdFromMultilocation(rawAssetIdFee);
fee = actual_fee.toString();
}
}
const assetIdIn = getAssetIdFromMultilocation(path[0], true);
const assetIdOut = getAssetIdFromMultilocation(
path[path["length"] - 1],
true
);
if (assetIdIn === undefined || assetIdOut === undefined) {
return [];
}
const swap: Swap = {
assetIdIn: assetIdIn,
amountIn: amountIn.toString(),
assetIdOut: assetIdOut,
amountOut: amountOut.toString(),
sender: sender.toString(),
receiver: receiver.toString(),
assetIdFee: assetIdFee,
fee: fee,
eventIdx: -1,
success: false,
};
return [
{
isTransferAll: false,
transfer: swap,
},
];
};
const hydraDxSwapCallback: HydraDxSwapCallback = (
assetIn: Codec,
assetOut: Codec,
amountIn: Codec,
amountOut: Codec
) => {
let fee = findHydraDxFeeTyped(extrinsic.events);
const assetIdIn = convertHydraDxTokenIdToString(assetIn);
const assetIdOut = convertHydraDxTokenIdToString(assetOut);
const swap: Swap = {
assetIdIn: assetIdIn,
amountIn: amountIn.toString(),
assetIdOut: assetIdOut,
amountOut: amountOut.toString(),
sender: sender.toString(),
receiver: sender.toString(),
assetIdFee: fee.tokenId,
fee: fee.amount,
eventIdx: -1,
success: false,
};
const createTransfer = (
isTransferAll: boolean,
address: string,
amount: bigint
): TransferData => {
return { return {
isTransferAll: false, isTransferAll,
transfer: swap, transfer: {
amount: amount.toString(),
from: sender.toString(),
to: address,
fee: calculateFeeAsString(extrinsic),
eventIdx: -1,
success: false,
},
}; };
}; };
let transferCalls = determineTransferCallsArgs( let transferCalls = determineTransferCallsArgs(
extrinsic.extrinsic.method, extrinsic.extrinsic.method,
transferCallback, createTransfer
assetHubSwapCallback,
hydraDxSwapCallback
); );
if (transferCalls.length == 0) { if (transferCalls.length == 0) {
return null; return null;
} }
@@ -320,186 +132,23 @@ function findFailedTransferCalls(
} }
function determineTransferCallsArgs( function determineTransferCallsArgs(
causeCall: CallBase<AnyTuple>, causeCall: any,
transferCallback: TransferCallback, createTransfer: (isTransferAll: boolean, address: string, amount: bigint) => TransferData
assetHubSwapCallback: AssetHubSwapCallback,
hydraDxSwapCallback: HydraDxSwapCallback
): Array<TransferData> { ): Array<TransferData> {
if (isNativeTransfer(causeCall)) { if (isNativeTransfer(causeCall)) {
return transferCallback(false, ...extractArgsFromTransfer(causeCall)); const [destinationAddress, amount] = causeCall.args;
} else if (isAssetTransfer(causeCall)) { return [createTransfer(false, destinationAddress.toString(), (amount as any).toBigInt())];
return transferCallback(false, ...extractArgsFromAssetTransfer(causeCall));
} else if (isOrmlTransfer(causeCall)) {
return transferCallback(false, ...extractArgsFromOrmlTransfer(causeCall));
} else if (isEquilibriumTransfer(causeCall)) {
return transferCallback(
false,
...extractArgsFromEquilibriumTransfer(causeCall)
);
} else if (isNativeTransferAll(causeCall)) { } else if (isNativeTransferAll(causeCall)) {
return transferCallback(true, ...extractArgsFromTransferAll(causeCall)); const [destinationAddress] = causeCall.args;
} else if (isOrmlTransferAll(causeCall)) { return [createTransfer(true, destinationAddress.toString(), BigInt(0))];
return transferCallback(true, ...extractArgsFromOrmlTransferAll(causeCall));
} else if (isSwapExactTokensForTokens(causeCall)) {
return assetHubSwapCallback(
...extractArgsFromSwapExactTokensForTokens(causeCall)
);
} else if (isSwapTokensForExactTokens(causeCall)) {
return assetHubSwapCallback(
...extractArgsFromSwapTokensForExactTokens(causeCall)
);
} else if (isHydraOmnipoolBuy(causeCall)) {
return [hydraDxSwapCallback(...extractArgsFromHydraOmnipoolBuy(causeCall))];
} else if (isHydraOmnipoolSell(causeCall)) {
return [
hydraDxSwapCallback(...extractArgsFromHydraOmnipoolSell(causeCall)),
];
} else if (isHydraRouterBuy(causeCall)) {
return [hydraDxSwapCallback(...extractArgsFromHydraRouterBuy(causeCall))];
} else if (isHydraRouterSell(causeCall)) {
return [hydraDxSwapCallback(...extractArgsFromHydraRouterSell(causeCall))];
} else if (isBatch(causeCall)) { } else if (isBatch(causeCall)) {
return callsFromBatch(causeCall) return callsFromBatch(causeCall)
.map((call) => { .map((call: any) => determineTransferCallsArgs(call, createTransfer))
return determineTransferCallsArgs(
call,
transferCallback,
assetHubSwapCallback,
hydraDxSwapCallback
).map((value, index, array) => {
return value;
});
})
.flat(); .flat();
} else if (isProxy(causeCall)) { } else if (isProxy(causeCall)) {
let proxyCall = callFromProxy(causeCall); let proxyCall = callFromProxy(causeCall);
return determineTransferCallsArgs( return determineTransferCallsArgs(proxyCall, createTransfer);
proxyCall,
transferCallback,
assetHubSwapCallback,
hydraDxSwapCallback
);
} else { } else {
return []; return [];
} }
} }
function extractArgsFromTransfer(call: CallBase<AnyTuple>): [string, bigint] {
const [destinationAddress, amount] = call.args;
return [destinationAddress.toString(), (amount as u64).toBigInt()];
}
function extractArgsFromAssetTransfer(
call: CallBase<AnyTuple>
): [string, bigint, string] {
const [assetId, destinationAddress, amount] = call.args;
return [
destinationAddress.toString(),
(amount as u64).toBigInt(),
assetId.toString(),
];
}
function extractArgsFromOrmlTransfer(
call: CallBase<AnyTuple>
): [string, bigint, string] {
const [destinationAddress, currencyId, amount] = call.args;
return [
destinationAddress.toString(),
(amount as u64).toBigInt(),
currencyId.toHex().toString(),
];
}
function extractArgsFromEquilibriumTransfer(
call: CallBase<AnyTuple>
): [string, bigint, string] {
const [assetId, destinationAddress, amount] = call.args;
return [
destinationAddress.toString(),
(amount as u64).toBigInt(),
assetId.toString(),
];
}
function extractArgsFromTransferAll(
call: CallBase<AnyTuple>
): [string, bigint] {
const [destinationAddress] = call.args;
return [destinationAddress.toString(), BigInt(0)];
}
function extractArgsFromOrmlTransferAll(
call: CallBase<AnyTuple>
): [string, bigint, string] {
const [destinationAddress, currencyId] = call.args;
return [
destinationAddress.toString(),
BigInt(0),
convertOrmlCurrencyIdToString(currencyId),
];
}
function extractArgsFromSwapExactTokensForTokens(
call: CallBase<AnyTuple>
): [any, Codec, Codec, Codec] {
const [path, amountIn, amountOut, receiver, _] = call.args;
return [path, amountIn, amountOut, receiver];
}
function extractArgsFromSwapTokensForExactTokens(
call: CallBase<AnyTuple>
): [any, Codec, Codec, Codec] {
const [path, amountOut, amountIn, receiver, _] = call.args;
return [path, amountIn, amountOut, receiver];
}
function extractArgsFromHydraRouterSell(
call: CallBase<AnyTuple>
): [Codec, Codec, Codec, Codec] {
const [assetIn, assetOut, amountIn, minAmountOut, _] = call.args;
return [assetIn, assetOut, amountIn, minAmountOut];
}
function extractArgsFromHydraRouterBuy(
call: CallBase<AnyTuple>
): [Codec, Codec, Codec, Codec] {
const [assetIn, assetOut, amountOut, maxAmountIn, _] = call.args;
return [assetIn, assetOut, maxAmountIn, amountOut];
}
function extractArgsFromHydraOmnipoolSell(
call: CallBase<AnyTuple>
): [Codec, Codec, Codec, Codec] {
const [assetIn, assetOut, amount, minBuyAmount, _] = call.args;
return [
assetIn,
assetOut,
amount, // amountIn
minBuyAmount, // amountOut
];
}
function extractArgsFromHydraOmnipoolBuy(
call: CallBase<AnyTuple>
): [Codec, Codec, Codec, Codec] {
const [assetOut, assetIn, amount, maxSellAmount, _] = call.args;
return [
assetIn,
assetOut,
maxSellAmount, // amountIn
amount, // amountOut
];
}
+9 -18
View File
@@ -2,13 +2,8 @@ import { SubstrateEvent } from "@subql/types";
import { eventId } from "./common"; import { eventId } from "./common";
import { EraValidatorInfo } from "../types/models/EraValidatorInfo"; import { EraValidatorInfo } from "../types/models/EraValidatorInfo";
import { IndividualExposure } from "../types"; import { IndividualExposure } from "../types";
import { import { Option } from "@pezkuwi/types";
SpStakingPagedExposureMetadata, import { Exposure } from "@pezkuwi/types/interfaces/staking";
SpStakingExposurePage,
} from "@polkadot/types/lookup";
import { Option } from "@polkadot/types";
import { INumber } from "@polkadot/types-codec/types/interfaces";
import { Exposure } from "@polkadot/types/interfaces";
export async function handleStakersElected( export async function handleStakersElected(
event: SubstrateEvent, event: SubstrateEvent,
@@ -17,7 +12,7 @@ export async function handleStakersElected(
} }
export async function handleNewEra(event: SubstrateEvent): Promise<void> { export async function handleNewEra(event: SubstrateEvent): Promise<void> {
const currentEra = ((await api.query.staking.currentEra()) as Option<INumber>) const currentEra = ((await api.query.staking.currentEra()) as Option<any>)
.unwrap() .unwrap()
.toNumber(); .toNumber();
@@ -65,19 +60,17 @@ async function processEraStakersPaged(
const pages = await api.query.staking.erasStakersPaged.entries(currentEra); const pages = await api.query.staking.erasStakersPaged.entries(currentEra);
interface AccumulatorType { interface AccumulatorType {
[key: string]: any; [key: string]: { [page: number]: IndividualExposure[] };
} }
const othersCounted = pages.reduce( const othersCounted = pages.reduce(
(accumulator: AccumulatorType, [key, exp]) => { (accumulator: AccumulatorType, [key, exp]) => {
const exposure = ( const exposure = (exp as Option<any>).unwrap();
exp as unknown as Option<SpStakingExposurePage>
).unwrap();
const [, validatorId, pageId] = key.args; const [, validatorId, pageId] = key.args;
const pageNumber = (pageId as INumber).toNumber(); const pageNumber = (pageId as any).toNumber();
const validatorIdString = validatorId.toString(); const validatorIdString = validatorId.toString();
const others = exposure.others.map(({ who, value }) => { const others: IndividualExposure[] = exposure.others.map(({ who, value }: any) => {
return { return {
who: who.toString(), who: who.toString(),
value: value.toString(), value: value.toString(),
@@ -93,13 +86,11 @@ async function processEraStakersPaged(
); );
for (const [key, exp] of overview) { for (const [key, exp] of overview) {
const exposure = ( const exposure = (exp as Option<any>).unwrap();
exp as unknown as Option<SpStakingPagedExposureMetadata>
).unwrap();
const [, validatorId] = key.args; const [, validatorId] = key.args;
let validatorIdString = validatorId.toString(); let validatorIdString = validatorId.toString();
let others = []; let others: IndividualExposure[] = [];
for (let i = 0; i < exposure.pageCount.toNumber(); ++i) { for (let i = 0; i < exposure.pageCount.toNumber(); ++i) {
others.push(...othersCounted[validatorIdString][i]); others.push(...othersCounted[validatorIdString][i]);
} }
+26 -40
View File
@@ -12,25 +12,15 @@ import {
eventIdWithAddress, eventIdWithAddress,
blockNumber, blockNumber,
} from "./common"; } from "./common";
import { Codec } from "@polkadot/types/types";
import { u32 } from "@polkadot/types-codec";
import { INumber } from "@polkadot/types-codec/types/interfaces";
import {
PalletNominationPoolsBondedPoolInner,
PalletNominationPoolsPoolMember,
PalletNominationPoolsSubPools,
} from "@polkadot/types/lookup";
import { import {
handleGenericForTxHistory, handleGenericForTxHistory,
updateAccumulatedGenericReward, updateAccumulatedGenericReward,
} from "./Rewards"; } from "./Rewards";
import { getPoolMembers } from "./Cache"; import { getPoolMembers } from "./Cache";
import { Option } from "@polkadot/types"; import { Option } from "@pezkuwi/types";
export async function handlePoolReward( export async function handlePoolReward(
rewardEvent: SubstrateEvent< rewardEvent: SubstrateEvent,
[accountId: Codec, poolId: INumber, reward: INumber]
>,
): Promise<void> { ): Promise<void> {
await handlePoolRewardForTxHistory(rewardEvent); await handlePoolRewardForTxHistory(rewardEvent);
let accumulatedReward = await updateAccumulatedPoolReward(rewardEvent, true); let accumulatedReward = await updateAccumulatedPoolReward(rewardEvent, true);
@@ -42,17 +32,15 @@ export async function handlePoolReward(
await updateAccountPoolRewards( await updateAccountPoolRewards(
rewardEvent, rewardEvent,
accountId.toString(), accountId.toString(),
amount.toBigInt(), (amount as any).toBigInt(),
poolId.toNumber(), (poolId as any).toNumber(),
RewardType.reward, RewardType.reward,
accumulatedReward.amount, accumulatedReward.amount,
); );
} }
async function handlePoolRewardForTxHistory( async function handlePoolRewardForTxHistory(
rewardEvent: SubstrateEvent< rewardEvent: SubstrateEvent,
[accountId: Codec, poolId: INumber, reward: INumber]
>,
): Promise<void> { ): Promise<void> {
const { const {
event: { event: {
@@ -67,7 +55,7 @@ async function handlePoolRewardForTxHistory(
eventIdx: rewardEvent.idx, eventIdx: rewardEvent.idx,
amount: amount.toString(), amount: amount.toString(),
isReward: true, isReward: true,
poolId: poolId.toNumber(), poolId: (poolId as any).toNumber(),
}; };
return element; return element;
}, },
@@ -75,7 +63,7 @@ async function handlePoolRewardForTxHistory(
} }
async function updateAccumulatedPoolReward( async function updateAccumulatedPoolReward(
event: SubstrateEvent<[accountId: Codec, poolId: INumber, reward: INumber]>, event: SubstrateEvent,
isReward: boolean, isReward: boolean,
): Promise<AccumulatedReward> { ): Promise<AccumulatedReward> {
let { let {
@@ -86,7 +74,7 @@ async function updateAccumulatedPoolReward(
return await updateAccumulatedGenericReward( return await updateAccumulatedGenericReward(
AccumulatedPoolReward, AccumulatedPoolReward,
accountId.toString(), accountId.toString(),
amount.toBigInt(), (amount as any).toBigInt(),
isReward, isReward,
); );
} }
@@ -114,62 +102,60 @@ async function updateAccountPoolRewards(
} }
export async function handlePoolBondedSlash( export async function handlePoolBondedSlash(
bondedSlashEvent: SubstrateEvent<[poolId: INumber, slash: INumber]>, bondedSlashEvent: SubstrateEvent,
): Promise<void> { ): Promise<void> {
const { const {
event: { event: {
data: [poolIdEncoded, slash], data: [poolIdEncoded, slash],
}, },
} = bondedSlashEvent; } = bondedSlashEvent;
const poolId = poolIdEncoded.toNumber(); const poolId = (poolIdEncoded as any).toNumber();
const poolOption = (await api.query.nominationPools.bondedPools( const poolOption = (await api.query.nominationPools.bondedPools(
poolId, poolId,
)) as Option<PalletNominationPoolsBondedPoolInner>; )) as Option<any>;
const pool = poolOption.unwrap(); const pool = poolOption.unwrap();
await handleRelaychainPooledStakingSlash( await handleRelaychainPooledStakingSlash(
bondedSlashEvent, bondedSlashEvent,
poolId, poolId,
pool.points.toBigInt(), pool.points.toBigInt(),
slash.toBigInt(), (slash as any).toBigInt(),
(member: PalletNominationPoolsPoolMember): bigint => { (member: any): bigint => {
return member.points.toBigInt(); return member.points.toBigInt();
}, },
); );
} }
export async function handlePoolUnbondingSlash( export async function handlePoolUnbondingSlash(
unbondingSlashEvent: SubstrateEvent< unbondingSlashEvent: SubstrateEvent,
[poolId: INumber, era: INumber, slash: INumber]
>,
): Promise<void> { ): Promise<void> {
const { const {
event: { event: {
data: [poolId, era, slash], data: [poolId, era, slash],
}, },
} = unbondingSlashEvent; } = unbondingSlashEvent;
const poolIdNumber = poolId.toNumber(); const poolIdNumber = (poolId as any).toNumber();
const eraIdNumber = era.toNumber(); const eraIdNumber = (era as any).toNumber();
const unbondingPools = ( const unbondingPools = (
(await api.query.nominationPools.subPoolsStorage( (await api.query.nominationPools.subPoolsStorage(
poolIdNumber, poolIdNumber,
)) as Option<PalletNominationPoolsSubPools> )) as Option<any>
).unwrap(); ).unwrap();
const pool = const pool =
unbondingPools.withEra.get(eraIdNumber as unknown as u32) ?? (unbondingPools.withEra as any).get(eraIdNumber) ??
unbondingPools.noEra; unbondingPools.noEra;
await handleRelaychainPooledStakingSlash( await handleRelaychainPooledStakingSlash(
unbondingSlashEvent, unbondingSlashEvent,
poolIdNumber, poolIdNumber,
pool.points.toBigInt(), pool.points.toBigInt(),
slash.toBigInt(), (slash as any).toBigInt(),
(member: PalletNominationPoolsPoolMember): bigint => { (member: any): bigint => {
return ( return (
member.unbondingEras.get(eraIdNumber as unknown as u32)?.toBigInt() ?? ((member.unbondingEras as any).get(eraIdNumber))?.toBigInt() ??
BigInt(0) BigInt(0)
); );
}, },
@@ -181,7 +167,7 @@ async function handleRelaychainPooledStakingSlash(
poolId: number, poolId: number,
poolPoints: bigint, poolPoints: bigint,
slash: bigint, slash: bigint,
memberPointsCounter: (member: PalletNominationPoolsPoolMember) => bigint, memberPointsCounter: (member: any) => bigint,
): Promise<void> { ): Promise<void> {
if (poolPoints == BigInt(0)) { if (poolPoints == BigInt(0)) {
return; return;
@@ -229,16 +215,16 @@ async function handlePoolSlashForTxHistory(
): Promise<void> { ): Promise<void> {
const extrinsic = slashEvent.extrinsic; const extrinsic = slashEvent.extrinsic;
const block = slashEvent.block; const block = slashEvent.block;
const blockNumber = block.block.header.number.toString(); const blockNum = block.block.header.number.toString();
const blockTimestamp = timestamp(block); const blockTimestamp = timestamp(block);
const eventId = eventIdFromBlockAndIdxAndAddress( const evtId = eventIdFromBlockAndIdxAndAddress(
blockNumber, blockNum,
slashEvent.idx.toString(), slashEvent.idx.toString(),
accountId, accountId,
); );
const element = HistoryElement.create({ const element = HistoryElement.create({
id: eventId, id: evtId,
timestamp: blockTimestamp, timestamp: blockTimestamp,
blockNumber: block.block.header.number.toNumber(), blockNumber: block.block.header.number.toNumber(),
extrinsicHash: extrinsicHash:
+28 -123
View File
@@ -21,64 +21,50 @@ import {
callFromProxy, callFromProxy,
blockNumber, blockNumber,
} from "./common"; } from "./common";
import { CallBase } from "@polkadot/types/types/calls";
import { AnyTuple } from "@polkadot/types/types/codec";
import { EraIndex } from "@polkadot/types/interfaces/staking";
import { Balance, EventRecord } from "@polkadot/types/interfaces";
import { import {
cachedRewardDestination, cachedRewardDestination,
cachedController, cachedController,
cachedStakingRewardEraIndex, cachedStakingRewardEraIndex,
} from "./Cache"; } from "./Cache";
import { Codec } from "@polkadot/types/types";
import { INumber } from "@polkadot/types-codec/types/interfaces";
function isPayoutStakers(call: CallBase<AnyTuple>): boolean { function isPayoutStakers(call: any): boolean {
return call.method == "payoutStakers"; return call.method == "payoutStakers";
} }
function isPayoutStakersByPage(call: CallBase<AnyTuple>): boolean { function isPayoutStakersByPage(call: any): boolean {
return call.method == "payoutStakersByPage"; return call.method == "payoutStakersByPage";
} }
function isPayoutValidator(call: CallBase<AnyTuple>): boolean { function isPayoutValidator(call: any): boolean {
return call.method == "payoutValidator"; return call.method == "payoutValidator";
} }
function extractArgsFromPayoutStakers( function extractArgsFromPayoutStakers(call: any): [string, number] {
call: CallBase<AnyTuple>,
): [string, number] {
const [validatorAddressRaw, eraRaw] = call.args; const [validatorAddressRaw, eraRaw] = call.args;
return [validatorAddressRaw.toString(), (eraRaw as EraIndex).toNumber()]; return [validatorAddressRaw.toString(), (eraRaw as any).toNumber()];
} }
function extractArgsFromPayoutStakersByPage( function extractArgsFromPayoutStakersByPage(call: any): [string, number] {
call: CallBase<AnyTuple>,
): [string, number] {
const [validatorAddressRaw, eraRaw, _] = call.args; const [validatorAddressRaw, eraRaw, _] = call.args;
return [validatorAddressRaw.toString(), (eraRaw as EraIndex).toNumber()]; return [validatorAddressRaw.toString(), (eraRaw as any).toNumber()];
} }
function extractArgsFromPayoutValidator( function extractArgsFromPayoutValidator(
call: CallBase<AnyTuple>, call: any,
sender: string, sender: string,
): [string, number] { ): [string, number] {
const [eraRaw] = call.args; const [eraRaw] = call.args;
return [sender, (eraRaw as EraIndex).toNumber()]; return [sender, (eraRaw as any).toNumber()];
} }
export async function handleRewarded( export async function handleRewarded(rewardEvent: SubstrateEvent): Promise<void> {
rewardEvent: SubstrateEvent<[accountId: Codec, reward: INumber]>,
): Promise<void> {
await handleReward(rewardEvent); await handleReward(rewardEvent);
} }
export async function handleReward( export async function handleReward(rewardEvent: SubstrateEvent): Promise<void> {
rewardEvent: SubstrateEvent<[accountId: Codec, reward: INumber]>,
): Promise<void> {
await handleRewardForTxHistory(rewardEvent); await handleRewardForTxHistory(rewardEvent);
let accumulatedReward = await updateAccumulatedReward(rewardEvent, true); let accumulatedReward = await updateAccumulatedReward(rewardEvent, true);
await updateAccountRewards( await updateAccountRewards(
@@ -86,22 +72,6 @@ export async function handleReward(
RewardType.reward, RewardType.reward,
accumulatedReward.amount, accumulatedReward.amount,
); );
// let rewardEventId = eventId(rewardEvent)
// try {
// let errorOccursOnEvent = await ErrorEvent.get(rewardEventId)
// if (errorOccursOnEvent !== undefined) {
// logger.info(`Skip rewardEvent: ${rewardEventId}`)
// return;
// }
// await handleRewardForTxHistory(rewardEvent)
// await updateAccumulatedReward(rewardEvent, true)
// } catch (error) {
// logger.error(`Got error on reward event: ${rewardEventId}: ${error.toString()}`)
// let saveError = new ErrorEvent(rewardEventId)
// saveError.description = error.toString()
// await saveError.save()
// }
} }
async function handleRewardForTxHistory( async function handleRewardForTxHistory(
@@ -209,7 +179,7 @@ async function handleRewardForTxHistory(
} }
function determinePayoutCallsArgs( function determinePayoutCallsArgs(
causeCall: CallBase<AnyTuple>, causeCall: any,
sender: string, sender: string,
): [string, number][] { ): [string, number][] {
if (isPayoutStakers(causeCall)) { if (isPayoutStakers(causeCall)) {
@@ -220,7 +190,7 @@ function determinePayoutCallsArgs(
return [extractArgsFromPayoutValidator(causeCall, sender)]; return [extractArgsFromPayoutValidator(causeCall, sender)];
} else if (isBatch(causeCall)) { } else if (isBatch(causeCall)) {
return callsFromBatch(causeCall) return callsFromBatch(causeCall)
.map((call) => { .map((call: any) => {
return determinePayoutCallsArgs(call, sender).map( return determinePayoutCallsArgs(call, sender).map(
(value, index, array) => { (value, index, array) => {
return value; return value;
@@ -236,15 +206,11 @@ function determinePayoutCallsArgs(
} }
} }
export async function handleSlashed( export async function handleSlashed(slashEvent: SubstrateEvent): Promise<void> {
slashEvent: SubstrateEvent<[accountId: Codec, slash: INumber]>,
): Promise<void> {
await handleSlash(slashEvent); await handleSlash(slashEvent);
} }
export async function handleSlash( export async function handleSlash(slashEvent: SubstrateEvent): Promise<void> {
slashEvent: SubstrateEvent<[accountId: Codec, slash: INumber]>,
): Promise<void> {
await handleSlashForTxHistory(slashEvent); await handleSlashForTxHistory(slashEvent);
let accumulatedReward = await updateAccumulatedReward(slashEvent, false); let accumulatedReward = await updateAccumulatedReward(slashEvent, false);
await updateAccountRewards( await updateAccountRewards(
@@ -252,29 +218,13 @@ export async function handleSlash(
RewardType.slash, RewardType.slash,
accumulatedReward.amount, accumulatedReward.amount,
); );
// let slashEventId = eventId(slashEvent)
// try {
// let errorOccursOnEvent = await ErrorEvent.get(slashEventId)
// if (errorOccursOnEvent !== undefined) {
// logger.info(`Skip slashEvent: ${slashEventId}`)
// return;
// }
// await handleSlashForTxHistory(slashEvent)
// await updateAccumulatedReward(slashEvent, false)
// } catch (error) {
// logger.error(`Got error on slash event: ${slashEventId}: ${error.toString()}`)
// let saveError = new ErrorEvent(slashEventId)
// saveError.description = error.toString()
// await saveError.save()
// }
} }
async function getValidators(era: number): Promise<Set<string>> { async function getValidators(era: number): Promise<Set<string>> {
const eraStakersInSlashEra = await (api.query.staking.erasStakersClipped const eraStakersInSlashEra = await (api.query.staking.erasStakersClipped
? api.query.staking.erasStakersClipped.keys(era) ? api.query.staking.erasStakersClipped.keys(era)
: api.query.staking.erasStakersOverview.keys(era)); : api.query.staking.erasStakersOverview.keys(era));
const validatorsInSlashEra = eraStakersInSlashEra.map((key) => { const validatorsInSlashEra = eraStakersInSlashEra.map((key: any) => {
let [, validatorId] = key.args; let [, validatorId] = key.args;
return validatorId.toString(); return validatorId.toString();
@@ -347,7 +297,7 @@ async function buildRewardEvents<A>(
amount: string, amount: string,
) => Reward, ) => Reward,
) { ) {
let blockNumber = block.block.header.number.toString(); let blockNum = block.block.header.number.toString();
let blockTimestamp = timestamp(block); let blockTimestamp = timestamp(block);
let innerAccumulator = initialInnerAccumulator; let innerAccumulator = initialInnerAccumulator;
@@ -371,13 +321,13 @@ async function buildRewardEvents<A>(
account.toString(), account.toString(),
); );
const eventId = eventIdFromBlockAndIdx(blockNumber, eventIndex.toString()); const evtId = eventIdFromBlockAndIdx(blockNum, eventIndex.toString());
const accountAddress = account.toString(); const accountAddress = account.toString();
const destinationAddress = accountsMapping[accountAddress]; const destinationAddress = accountsMapping[accountAddress];
const element = new HistoryElement( const element = new HistoryElement(
eventId, evtId,
block.block.header.number.toNumber(), block.block.header.number.toNumber(),
blockTimestamp, blockTimestamp,
destinationAddress !== undefined ? destinationAddress : accountAddress, destinationAddress !== undefined ? destinationAddress : accountAddress,
@@ -407,7 +357,7 @@ async function updateAccumulatedReward(
return await updateAccumulatedGenericReward( return await updateAccumulatedGenericReward(
AccumulatedReward, AccumulatedReward,
accountId.toString(), accountId.toString(),
(amount as unknown as Balance).toBigInt(), (amount as any).toBigInt(),
isReward, isReward,
); );
} }
@@ -425,7 +375,7 @@ async function updateAccountRewards(
accountAddress, accountAddress,
blockNumber(event), blockNumber(event),
timestamp(event.block), timestamp(event.block),
(amount as unknown as Balance).toBigInt(), (amount as any).toBigInt(),
accumulatedAmount, accumulatedAmount,
rewardType, rewardType,
); );
@@ -463,7 +413,7 @@ async function handleParachainRewardForTxHistory(
} }
export async function handleParachainRewarded( export async function handleParachainRewarded(
rewardEvent: SubstrateEvent<[accountId: Codec, reward: INumber]>, rewardEvent: SubstrateEvent,
): Promise<void> { ): Promise<void> {
await handleParachainRewardForTxHistory(rewardEvent); await handleParachainRewardForTxHistory(rewardEvent);
let accumulatedReward = await updateAccumulatedReward(rewardEvent, true); let accumulatedReward = await updateAccumulatedReward(rewardEvent, true);
@@ -474,46 +424,6 @@ export async function handleParachainRewarded(
); );
} }
// ============= Mythos ================
export async function handleMythosRewarded(
rewardEvent: SubstrateEvent<[accountId: Codec, reward: INumber]>,
): Promise<void> {
await handleMythosRewardForTxHistory(rewardEvent);
let accumulatedReward = await updateAccumulatedReward(rewardEvent, true);
await updateAccountRewards(
rewardEvent,
RewardType.reward,
accumulatedReward.amount,
);
}
async function handleMythosRewardForTxHistory(
rewardEvent: SubstrateEvent,
): Promise<void> {
let [account, amount] = decodeDataFromReward(rewardEvent);
await handleGenericForTxHistory(
rewardEvent,
account.toString(),
async (element: HistoryElement) => {
element.reward = {
eventIdx: rewardEvent.idx,
amount: amount.toString(),
isReward: true,
stash: account.toString(),
// Mythos staking rewards are paid manually by the user so each reward
// aggregates multiple payouts, and it is hard to split it into
// individual per-session per-validator pieces
validator: null,
era: null,
};
return element;
},
);
}
// ============= GENERICS ================ // ============= GENERICS ================
interface AccumulatedInterface { interface AccumulatedInterface {
@@ -558,12 +468,12 @@ export async function handleGenericForTxHistory(
): Promise<void> { ): Promise<void> {
const extrinsic = event.extrinsic; const extrinsic = event.extrinsic;
const block = event.block; const block = event.block;
const blockNumber = block.block.header.number.toString(); const blockNum = block.block.header.number.toString();
const blockTimestamp = timestamp(block); const blockTimestamp = timestamp(block);
const eventId = eventIdFromBlockAndIdx(blockNumber, event.idx.toString()); const evtId = eventIdFromBlockAndIdx(blockNum, event.idx.toString());
const element = new HistoryElement( const element = new HistoryElement(
eventId, evtId,
block.block.header.number.toNumber(), block.block.header.number.toNumber(),
blockTimestamp, blockTimestamp,
address, address,
@@ -593,20 +503,15 @@ interface AccountRewardsInterface {
save(): Promise<void>; save(): Promise<void>;
} }
export function eventRecordToSubstrateEvent( export function eventRecordToSubstrateEvent(eventRecord: any): SubstrateEvent {
eventRecord: EventRecord,
): SubstrateEvent {
return eventRecord as unknown as SubstrateEvent; return eventRecord as unknown as SubstrateEvent;
} }
function decodeDataFromReward(event: SubstrateEvent): [Codec, Codec] { function decodeDataFromReward(event: SubstrateEvent): [any, any] {
// In early version staking.Reward data only have 2 parameters [accountId, amount]
// Now rewarded changed to https://polkadot.js.org/docs/substrate/events/#rewardedaccountid32-palletstakingrewarddestination-u128
// And we can direct access property from data
const { const {
event: { data: innerData }, event: { data: innerData },
} = event; } = event;
let account: Codec, amount: Codec; let account: any, amount: any;
if (innerData.length == 2) { if (innerData.length == 2) {
[account, amount] = innerData; [account, amount] = innerData;
} else { } else {
+31 -261
View File
@@ -1,6 +1,4 @@
import { Codec } from "@polkadot/types/types"; import { HistoryElement, Transfer } from "../types";
import { HistoryElement } from "../types";
import { HistoryElementProps } from "../types/models/HistoryElement";
import { SubstrateEvent } from "@subql/types"; import { SubstrateEvent } from "@subql/types";
import { import {
blockNumber, blockNumber,
@@ -8,286 +6,58 @@ import {
calculateFeeAsString, calculateFeeAsString,
timestamp, timestamp,
getEventData, getEventData,
isEvmTransaction,
isEvmExecutedEvent,
isAssetTxFeePaidEvent,
isSwapExecutedEvent,
eventRecordToSubstrateEvent,
getAssetIdFromMultilocation,
BigIntFromCodec,
convertOrmlCurrencyIdToString,
} from "./common"; } from "./common";
type TransferPayload = {
event: SubstrateEvent;
address: Codec;
from: Codec;
to: Codec;
amount: Codec;
suffix: string;
assetId?: string;
};
export async function handleSwap(event: SubstrateEvent): Promise<void> {
const [from, to, path, amountIn, amountOut] = getEventData(event);
let element = await HistoryElement.get(`${eventId(event)}-from`);
if (element !== undefined) {
// already processed swap previously
return;
}
let assetIdFee: string;
let fee: string;
let foundAssetTxFeePaid = event.block.events.find((e) =>
isAssetTxFeePaidEvent(eventRecordToSubstrateEvent(e)),
);
let swaps = event.block.events.filter((e) =>
isSwapExecutedEvent(eventRecordToSubstrateEvent(e)),
);
if (foundAssetTxFeePaid === undefined) {
assetIdFee = "native";
fee = calculateFeeAsString(event.extrinsic, from.toString());
} else {
const [who, actualFee, tip, rawAssetIdFee] = getEventData(
eventRecordToSubstrateEvent(foundAssetTxFeePaid),
);
assetIdFee = getAssetIdFromMultilocation(rawAssetIdFee);
fee = actualFee.toString();
let {
event: {
data: [feeFrom, feeTo, feePath, feeAmountIn, feeAmountOut],
},
} = swaps[0];
swaps = swaps.slice(1);
if (BigIntFromCodec(actualFee) != BigIntFromCodec(feeAmountIn)) {
let {
event: {
data: [
refundFrom,
refundTo,
refundPath,
refundAmountIn,
refundAmountOut,
],
},
} = swaps[swaps.length - 1];
if (
BigIntFromCodec(feeAmountIn) ==
BigIntFromCodec(actualFee) + BigIntFromCodec(refundAmountOut) &&
getAssetIdFromMultilocation((feePath as any)[0]) ==
getAssetIdFromMultilocation(
(refundPath as any)[(refundPath as any)["length"] - 1],
)
) {
swaps = swaps.slice(swaps.length - 1);
// TODO: if fee splitted, than we will process the same block two times
}
}
}
for (const swap of swaps) {
await processSwap(eventRecordToSubstrateEvent(swap), assetIdFee, fee);
}
}
async function processSwap(
event: SubstrateEvent,
assetIdFee: string,
fee: string,
): Promise<void> {
const [from, to, path, amountIn, amountOut] = getEventData(event);
const swap = {
assetIdIn: getAssetIdFromMultilocation((path as any)[0]),
amountIn: amountIn.toString(),
assetIdOut: getAssetIdFromMultilocation(
(path as any)[(path as any)["length"] - 1],
),
amountOut: amountOut.toString(),
sender: from.toString(),
receiver: to.toString(),
assetIdFee: assetIdFee,
fee: fee,
eventIdx: event.idx,
success: true,
};
await createAssetTransmission(event, from.toString(), "-from", {
swap: swap,
});
if (from.toString() != to.toString()) {
await createAssetTransmission(event, to.toString(), "-to", { swap: swap });
}
}
export async function handleTransfer(event: SubstrateEvent): Promise<void> { export async function handleTransfer(event: SubstrateEvent): Promise<void> {
const [from, to, amount] = getEventData(event); const [from, to, amount] = getEventData(event);
await createTransfer({ await createTransfer(
event, event,
address: from, from.toString(),
from, "-from",
to, from.toString(),
suffix: "-from", to.toString(),
amount, amount.toString(),
}); );
await createTransfer({ event, address: to, from, to, suffix: "-to", amount }); await createTransfer(
event,
to.toString(),
"-to",
from.toString(),
to.toString(),
amount.toString(),
);
} }
export async function handleAssetTransfer( async function createTransfer(
event: SubstrateEvent, event: SubstrateEvent,
address: string,
suffix: string,
from: string,
to: string,
amount: string,
): Promise<void> { ): Promise<void> {
const [assetId, from, to, amount] = getEventData(event); const transfer: Transfer = {
amount: amount,
await createTransfer({ from: from,
event, to: to,
address: from, fee: calculateFeeAsString(event.extrinsic, from),
from,
to,
suffix: "-from",
amount,
assetId: assetId.toString(),
});
await createTransfer({
event,
address: to,
from,
to,
suffix: "-to",
amount,
assetId: assetId.toString(),
});
}
export async function handleOrmlTransfer(event: SubstrateEvent): Promise<void> {
const [currencyId, from, to, amount] = getEventData(event);
await createTransfer({
event,
address: from,
from,
to,
suffix: "-from",
amount,
assetId: convertOrmlCurrencyIdToString(currencyId),
});
await createTransfer({
event,
address: to,
from,
to,
suffix: "-to",
amount,
assetId: convertOrmlCurrencyIdToString(currencyId),
});
}
export async function handleEquilibriumTransfer(
event: SubstrateEvent,
): Promise<void> {
const [from, to, assetId, amount] = getEventData(event);
await createTransfer({
event,
address: from,
from,
to,
suffix: "-from",
amount,
assetId: assetId.toString(),
});
await createTransfer({
event,
address: to,
from,
to,
suffix: "-to",
amount,
assetId: assetId.toString(),
});
}
export async function handleTokenTransfer(
event: SubstrateEvent,
): Promise<void> {
await handleOrmlTransfer(event);
}
export async function handleCurrencyTransfer(
event: SubstrateEvent,
): Promise<void> {
await handleOrmlTransfer(event);
}
async function createTransfer({
event,
address,
suffix,
from,
to,
amount,
assetId = null,
}: TransferPayload) {
const transfer = {
amount: amount.toString(),
from: from.toString(),
to: to.toString(),
fee: calculateFeeAsString(event.extrinsic, from.toString()),
eventIdx: event.idx, eventIdx: event.idx,
success: true, success: true,
}; };
let data;
if (assetId) {
data = {
assetTransfer: {
...transfer,
assetId: assetId,
},
};
} else {
data = {
transfer: transfer,
};
}
await createAssetTransmission(event, address, suffix, data);
}
export async function createAssetTransmission(
event: SubstrateEvent,
address: any,
suffix: string,
data: Partial<HistoryElementProps>,
) {
const element = new HistoryElement( const element = new HistoryElement(
`${eventId(event)}${suffix}`, `${eventId(event)}${suffix}`,
blockNumber(event), blockNumber(event),
timestamp(event.block), timestamp(event.block),
address.toString(), address,
); );
if (event.extrinsic !== undefined) {
if (isEvmTransaction(event.extrinsic.extrinsic.method)) {
const executedEvent = event.extrinsic.events.find(isEvmExecutedEvent);
element.extrinsicHash =
executedEvent?.event.data?.[2]?.toString() ||
event.extrinsic.extrinsic.hash.toString();
} else {
element.extrinsicHash = event.extrinsic.extrinsic.hash.toString();
}
if (event.extrinsic !== undefined) {
element.extrinsicHash = event.extrinsic.extrinsic.hash.toString();
element.extrinsicIdx = event.extrinsic.idx; element.extrinsicIdx = event.extrinsic.idx;
} }
for (var key in data) { element.transfer = transfer;
(element[key as keyof HistoryElementProps] as any) =
data[key as keyof HistoryElementProps];
}
await element.save(); await element.save();
} }
+32 -190
View File
@@ -1,127 +1,35 @@
import { SubstrateBlock, SubstrateEvent, TypedEventRecord } from "@subql/types"; import { SubstrateBlock, SubstrateEvent } from "@subql/types";
import { SubstrateExtrinsic } from "@subql/types"; import { SubstrateExtrinsic } from "@subql/types";
import { Balance, EventRecord } from "@polkadot/types/interfaces";
import { CallBase } from "@polkadot/types/types/calls";
import { AnyTuple, Codec } from "@polkadot/types/types/codec";
import { Vec, GenericEventData } from "@polkadot/types";
import { INumber } from "@polkadot/types-codec/types/interfaces";
import { u8aToHex } from "@polkadot/util";
const batchCalls = ["batch", "batchAll", "forceBatch"]; const batchCalls = ["batch", "batchAll", "forceBatch"];
const transferCalls = ["transfer", "transferKeepAlive"]; const transferCalls = ["transfer", "transferKeepAlive"];
const ormlSections = ["currencies", "tokens"];
export function distinct<T>(array: Array<T>): Array<T> { export function distinct<T>(array: Array<T>): Array<T> {
return [...new Set(array)]; return [...new Set(array)];
} }
export function isBatch(call: CallBase<AnyTuple>): boolean { export function isBatch(call: any): boolean {
return call.section == "utility" && batchCalls.includes(call.method); return call.section == "utility" && batchCalls.includes(call.method);
} }
export function isProxy(call: CallBase<AnyTuple>): boolean { export function isProxy(call: any): boolean {
return call.section == "proxy" && call.method == "proxy"; return call.section == "proxy" && call.method == "proxy";
} }
export function isNativeTransfer(call: CallBase<AnyTuple>): boolean { export function isNativeTransfer(call: any): boolean {
return ( return call.section == "balances" && transferCalls.includes(call.method);
(call.section == "balances" && transferCalls.includes(call.method)) ||
(call.section == "currencies" && call.method == "transferNativeCurrency")
);
} }
export function isAssetTransfer(call: CallBase<AnyTuple>): boolean { export function isNativeTransferAll(call: any): boolean {
return call.section == "assets" && transferCalls.includes(call.method);
}
export function isEquilibriumTransfer(call: CallBase<AnyTuple>): boolean {
return call.section == "eqBalances" && transferCalls.includes(call.method);
}
export function isEvmTransaction(call: CallBase<AnyTuple>): boolean {
return call.section === "ethereum" && call.method === "transact";
}
export function isEvmExecutedEvent(event: TypedEventRecord<Codec[]>): boolean {
return (
event.event.section === "ethereum" && event.event.method === "Executed"
);
}
export function isAssetTxFeePaidEvent(event: SubstrateEvent): boolean {
return (
event.event.section === "assetTxPayment" &&
event.event.method === "AssetTxFeePaid"
);
}
export function isCurrencyDepositedEvent(event: SubstrateEvent): boolean {
return (
event.event.section === "currencies" && event.event.method === "Deposited"
);
}
export function isSwapExecutedEvent(event: SubstrateEvent): boolean {
return (
event.event.section === "assetConversion" &&
event.event.method === "SwapExecuted"
);
}
export function isSwapExactTokensForTokens(call: CallBase<AnyTuple>): boolean {
return (
call.section === "assetConversion" &&
call.method === "swapExactTokensForTokens"
);
}
export function isSwapTokensForExactTokens(call: CallBase<AnyTuple>): boolean {
return (
call.section === "assetConversion" &&
call.method === "swapTokensForExactTokens"
);
}
export function isHydraOmnipoolBuy(call: CallBase<AnyTuple>): boolean {
return call.section === "omnipool" && call.method == "buy";
}
export function isHydraOmnipoolSell(call: CallBase<AnyTuple>): boolean {
return call.section === "omnipool" && call.method == "sell";
}
export function isHydraRouterBuy(call: CallBase<AnyTuple>): boolean {
return call.section === "router" && call.method == "buy";
}
export function isHydraRouterSell(call: CallBase<AnyTuple>): boolean {
return call.section === "router" && call.method == "sell";
}
export function isOrmlTransfer(call: CallBase<AnyTuple>): boolean {
return (
ormlSections.includes(call.section) && transferCalls.includes(call.method)
);
}
export function isNativeTransferAll(call: CallBase<AnyTuple>): boolean {
return call.section == "balances" && call.method === "transferAll"; return call.section == "balances" && call.method === "transferAll";
} }
export function isOrmlTransferAll(call: CallBase<AnyTuple>): boolean { export function callsFromBatch(batchCall: any): any[] {
return ormlSections.includes(call.section) && call.method === "transferAll"; return batchCall.args[0] as any[];
} }
export function callsFromBatch( export function callFromProxy(proxyCall: any): any {
batchCall: CallBase<AnyTuple>, return proxyCall.args[2];
): CallBase<AnyTuple>[] {
return batchCall.args[0] as Vec<CallBase<AnyTuple>>;
}
export function callFromProxy(
proxyCall: CallBase<AnyTuple>,
): CallBase<AnyTuple> {
return proxyCall.args[2] as CallBase<AnyTuple>;
} }
export function eventIdWithAddress( export function eventIdWithAddress(
@@ -186,12 +94,6 @@ export function calculateFeeAsString(
const withdrawFee = exportFeeFromBalancesWithdrawEvent(extrinsic, from); const withdrawFee = exportFeeFromBalancesWithdrawEvent(extrinsic, from);
if (withdrawFee !== BigInt(0)) { if (withdrawFee !== BigInt(0)) {
if (isEvmTransaction(extrinsic.extrinsic.method)) {
const feeRefund = exportFeeRefund(extrinsic, from);
return feeRefund
? (withdrawFee - feeRefund).toString()
: withdrawFee.toString();
}
return withdrawFee.toString(); return withdrawFee.toString();
} }
@@ -205,51 +107,29 @@ export function calculateFeeAsString(
} }
} }
export function getEventData(event: SubstrateEvent): GenericEventData { export function getEventData(event: SubstrateEvent): any {
return event.event.data as GenericEventData; return event.event.data;
} }
export function eventRecordToSubstrateEvent( export function eventRecordToSubstrateEvent(eventRecord: any): SubstrateEvent {
eventRecord: EventRecord,
): SubstrateEvent {
return eventRecord as unknown as SubstrateEvent; return eventRecord as unknown as SubstrateEvent;
} }
export function BigIntFromCodec(eventRecord: Codec): bigint { export function BigIntFromCodec(codec: any): bigint {
return (eventRecord as unknown as INumber).toBigInt(); return codec.toBigInt();
} }
export function convertOrmlCurrencyIdToString(currencyId: Codec): string { export function getRewardData(event: SubstrateEvent): [any, any] {
// make sure first we have scale encoded bytes const {
const bytes = currencyId.toU8a(); event: { data: innerData },
} = event;
return u8aToHex(bytes).toString(); let account: any, amount: any;
} if (innerData.length == 2) {
[account, amount] = innerData;
function exportFeeRefund( } else {
extrinsic: SubstrateExtrinsic, [account, , amount] = innerData;
from: string = "",
): bigint {
const extrinsicSigner = from || extrinsic.extrinsic.signer.toString();
const eventRecord = extrinsic.events.find(
(event) =>
event.event.method == "Deposit" &&
event.event.section == "balances" &&
event.event.data[0].toString() === extrinsicSigner,
);
if (eventRecord != undefined) {
const {
event: {
data: [, fee],
},
} = eventRecord;
return (fee as unknown as Balance).toBigInt();
} }
return [account, amount];
return BigInt(0);
} }
function exportFeeFromBalancesWithdrawEvent( function exportFeeFromBalancesWithdrawEvent(
@@ -271,7 +151,7 @@ function exportFeeFromBalancesWithdrawEvent(
const extrinsicSigner = from || extrinsic.extrinsic.signer.toString(); const extrinsicSigner = from || extrinsic.extrinsic.signer.toString();
const withdrawAccountId = accountid.toString(); const withdrawAccountId = accountid.toString();
return extrinsicSigner === withdrawAccountId return extrinsicSigner === withdrawAccountId
? (fee as unknown as Balance).toBigInt() ? (fee as any).toBigInt()
: BigInt(0); : BigInt(0);
} }
@@ -295,7 +175,7 @@ function exportFeeFromTransactionFeePaidEvent(
}, },
} = eventRecord; } = eventRecord;
const fullFee = (fee as Balance).toBigInt() + (tip as Balance).toBigInt(); const fullFee = (fee as any).toBigInt() + (tip as any).toBigInt();
const extrinsicSigner = from || extrinsic.extrinsic.signer.toString(); const extrinsicSigner = from || extrinsic.extrinsic.signer.toString();
const withdrawAccountId = accountid.toString(); const withdrawAccountId = accountid.toString();
@@ -319,7 +199,7 @@ function exportFeeFromBalancesDepositEvent(
}, },
} = eventRecord; } = eventRecord;
return (fee as unknown as Balance).toBigInt(); return (fee as any).toBigInt();
} }
return BigInt(0); return BigInt(0);
@@ -339,53 +219,15 @@ function exportFeeFromTreasureDepositEvent(
}, },
} = eventRecord; } = eventRecord;
return (fee as unknown as Balance).toBigInt(); return (fee as any).toBigInt();
} else { } else {
return BigInt(0); return BigInt(0);
} }
} }
export function getAssetIdFromMultilocation( export function extractTransactionPaidFee(events: any[]): string | undefined {
multilocation: any,
safe = false,
): string | undefined {
try {
let junctions = multilocation.interior;
if (junctions.isHere) {
return "native";
} else if (multilocation.parents != "0") {
return multilocation.toHex();
} else {
return junctions.asX2[1].asGeneralIndex.toString();
}
} catch (e) {
if (safe) {
return undefined;
} else {
throw e;
}
}
}
export function getRewardData(event: SubstrateEvent): [Codec, Codec] {
const {
event: { data: innerData },
} = event;
let account: Codec, amount: Codec;
if (innerData.length == 2) {
[account, amount] = innerData;
} else {
[account, , amount] = innerData;
}
return [account, amount];
}
export function extractTransactionPaidFee(
events: EventRecord[],
): string | undefined {
const eventRecord = events.find( const eventRecord = events.find(
(event) => (event: any) =>
event.event.method == "TransactionFeePaid" && event.event.method == "TransactionFeePaid" &&
event.event.section == "transactionPayment", event.event.section == "transactionPayment",
); );
@@ -398,7 +240,7 @@ export function extractTransactionPaidFee(
}, },
} = eventRecord; } = eventRecord;
const fullFee = (fee as Balance).toBigInt() + (tip as Balance).toBigInt(); const fullFee = (fee as any).toBigInt() + (tip as any).toBigInt();
return fullFee.toString(); return fullFee.toString();
} }
-119
View File
@@ -1,119 +0,0 @@
import { SubstrateEvent } from "@subql/types";
import {
BigIntFromCodec,
calculateFeeAsString,
eventId,
eventRecordToSubstrateEvent,
getAssetIdFromMultilocation,
getEventData,
isAssetTxFeePaidEvent,
isSwapExecutedEvent,
} from "../common";
import { HistoryElement } from "../../types";
import { createAssetTransmission } from "../Transfers";
export async function handleAssetConversionSwap(
event: SubstrateEvent
): Promise<void> {
const [from, to, path, amountIn, amountOut] = getEventData(event);
let element = await HistoryElement.get(`${eventId(event)}-from`);
if (element !== undefined) {
// already processed swap previously
return;
}
let assetIdFee: string;
let fee: string;
let foundAssetTxFeePaid = event.block.events.find((e) =>
isAssetTxFeePaidEvent(eventRecordToSubstrateEvent(e))
);
let swaps = event.block.events.filter((e) =>
isSwapExecutedEvent(eventRecordToSubstrateEvent(e))
);
if (foundAssetTxFeePaid === undefined) {
assetIdFee = "native";
fee = calculateFeeAsString(event.extrinsic, from.toString());
} else {
const [who, actualFee, tip, rawAssetIdFee] = getEventData(
eventRecordToSubstrateEvent(foundAssetTxFeePaid)
);
assetIdFee = getAssetIdFromMultilocation(rawAssetIdFee);
fee = actualFee.toString();
let {
event: {
data: [feeFrom, feeTo, feePath, feeAmountIn, feeAmountOut],
},
} = swaps[0];
swaps = swaps.slice(1);
if (BigIntFromCodec(actualFee) != BigIntFromCodec(feeAmountIn)) {
let {
event: {
data: [
refundFrom,
refundTo,
refundPath,
refundAmountIn,
refundAmountOut,
],
},
} = swaps[swaps.length - 1];
const feePathArray = feePath as unknown as any[];
const refundPathArray = refundPath as unknown as any[];
if (
BigIntFromCodec(feeAmountIn) ==
BigIntFromCodec(actualFee) + BigIntFromCodec(refundAmountOut) &&
getAssetIdFromMultilocation(feePathArray[0]) ==
getAssetIdFromMultilocation(
refundPathArray[refundPathArray["length"] - 1]
)
) {
swaps = swaps.slice(swaps.length - 1);
// TODO: if fee splitted, than we will process the same block two times
}
}
}
for (const e of swaps) {
await processAssetConversionSwap(
eventRecordToSubstrateEvent(e),
assetIdFee,
fee
);
}
}
async function processAssetConversionSwap(
event: SubstrateEvent,
assetIdFee: string,
fee: string
): Promise<void> {
const [from, to, path, amountIn, amountOut] = getEventData(event);
const pathArray = path as unknown as any[];
const swap = {
assetIdIn: getAssetIdFromMultilocation(pathArray[0]),
amountIn: amountIn.toString(),
assetIdOut: getAssetIdFromMultilocation(pathArray[pathArray["length"] - 1]),
amountOut: amountOut.toString(),
sender: from.toString(),
receiver: to.toString(),
assetIdFee: assetIdFee,
fee: fee,
eventIdx: event.idx,
success: true,
};
await createAssetTransmission(event, from.toString(), "-from", {
swap: swap,
});
if (from.toString() != to.toString()) {
await createAssetTransmission(event, to.toString(), "-to", { swap: swap });
}
}
-191
View File
@@ -1,191 +0,0 @@
import { SubstrateEvent, TypedEventRecord } from "@subql/types";
import {
eventId,
eventRecordToSubstrateEvent,
extractTransactionPaidFee,
isCurrencyDepositedEvent,
convertOrmlCurrencyIdToString,
} from "../common";
import { HistoryElement } from "../../types";
import { createAssetTransmission } from "../Transfers";
import { AccountId32 } from "@polkadot/types/interfaces/runtime";
import { u128, u32 } from "@polkadot/types-codec";
import { EventRecord } from "@polkadot/types/interfaces";
import { Codec } from "@polkadot/types/types";
import { INumber } from "@polkadot/types-codec/types/interfaces";
type OmnipoolSwapArgs = [
who: AccountId32,
assetIn: u32,
assetOut: u32,
amountIn: u128,
amountOut: u128,
assetFeeAmount: u128,
protocolFeeAmount: u128,
];
type RouterSwapArgs = [
assetIn: u32,
assetOut: u32,
amountIn: u128,
amountOut: u128,
];
export async function handleOmnipoolSwap(
event: SubstrateEvent<OmnipoolSwapArgs>,
): Promise<void> {
let element = await HistoryElement.get(`${eventId(event)}-from`);
if (element !== undefined) {
// already processed swap previously
return;
}
if (event.extrinsic == undefined) {
// TODO we dont yet process swap events that were initiated by the system and not by the user
// Example: https://hydradx.subscan.io/block/4361343?tab=event&event=4361343-27
return;
}
if (isPartOfRouterSwap(event.extrinsic.events)) {
// TODO: we currently don't support swaps in batch
return;
}
const fee = findHydraDxFeeTyped(event.extrinsic.events);
const [who, assetIn, assetOut, amountIn, amountOut] = event.event.data;
const swap = {
assetIdIn: convertHydraDxTokenIdToString(assetIn),
amountIn: amountIn.toString(),
assetIdOut: convertHydraDxTokenIdToString(assetOut),
amountOut: amountOut.toString(),
sender: who.toString(),
receiver: who.toString(),
assetIdFee: fee.tokenId,
fee: fee.amount,
eventIdx: event.idx,
success: true,
};
const blockNumber = event.block.block.header.number;
logger.info(
`Constructed omnipool swap ${JSON.stringify(
swap,
)} for block ${blockNumber.toString()}`,
);
await createAssetTransmission(event, who.toString(), "-from", { swap: swap });
}
export async function handleHydraRouterSwap(
event: SubstrateEvent<RouterSwapArgs>,
): Promise<void> {
let element = await HistoryElement.get(`${eventId(event)}-from`);
if (element !== undefined) {
return;
}
if (event.extrinsic == undefined) {
return;
}
const who = event.extrinsic.extrinsic.signer.toString();
const fee = findHydraDxFeeTyped(event.extrinsic.events);
const [assetIn, assetOut, amountIn, amountOut] = event.event.data;
const swap = {
assetIdIn: convertHydraDxTokenIdToString(assetIn),
amountIn: amountIn.toString(),
assetIdOut: convertHydraDxTokenIdToString(assetOut),
amountOut: amountOut.toString(),
sender: who.toString(),
receiver: who.toString(),
assetIdFee: fee.tokenId,
fee: fee.amount,
eventIdx: event.idx,
success: true,
};
const blockNumber = event.block.block.header.number;
logger.info(
`Constructed router swap ${JSON.stringify(
swap,
)} for block ${blockNumber.toString()}`,
);
await createAssetTransmission(event, who.toString(), "-from", { swap: swap });
}
export type Fee = {
tokenId: string;
amount: string;
};
export function findHydraDxFeeTyped(events: TypedEventRecord<Codec[]>[]): Fee {
return findHydraDxFee(events as EventRecord[]);
}
export function findHydraDxFee(events: EventRecord[]): Fee {
const lastCurrenciesDepositEvent = findLastEvent(events, (event) =>
isCurrencyDepositedEvent(eventRecordToSubstrateEvent(event)),
);
if (lastCurrenciesDepositEvent == undefined) return findNativeFee(events);
const {
event: {
data: [currencyId, _, amount],
},
} = lastCurrenciesDepositEvent;
return {
tokenId: convertHydraDxTokenIdToString(currencyId),
amount: (amount as INumber).toString(),
};
}
function isPartOfRouterSwap(events: TypedEventRecord<Codec[]>[]): boolean {
const eventRecords = events as EventRecord[];
for (const eventRecord of eventRecords) {
if (
eventRecord.event.section == "router" &&
(eventRecord.event.method == "Executed" ||
eventRecord.event.method == "RouteExecuted")
) {
return true;
}
}
return false;
}
function findNativeFee(events: EventRecord[]): Fee {
let foundAssetTxFeePaid = extractTransactionPaidFee(events);
if (foundAssetTxFeePaid == undefined) foundAssetTxFeePaid = "0";
return {
tokenId: "native",
amount: foundAssetTxFeePaid,
};
}
export function convertHydraDxTokenIdToString(hydraDxTokenId: Codec): string {
const asString = hydraDxTokenId.toString();
if (asString == "0") {
return "native";
} else {
return convertOrmlCurrencyIdToString(hydraDxTokenId);
}
}
function findLastEvent(
events: EventRecord[],
expression: (event: EventRecord) => boolean,
): EventRecord | undefined {
const currenciesDepositedEvents = events.filter(expression);
if (currenciesDepositedEvents.length == 0) {
return undefined;
}
return currenciesDepositedEvents[currenciesDepositedEvents.length - 1];
}
-4
View File
@@ -1,4 +0,0 @@
//Exports all handler functions
export * from "./AssetConversion";
export * from "./HydraDx";
import "@polkadot/api-augment";
+17 -1
View File
@@ -7,12 +7,28 @@
"importHelpers": true, "importHelpers": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node",
"outDir": "dist", "outDir": "dist",
"rootDir": ".", "rootDir": ".",
"target": "es2020", "target": "es2020",
"skipLibCheck": true, "skipLibCheck": true,
"strictNullChecks": false, "strictNullChecks": false,
"strict": true "strict": true,
"paths": {
"@polkadot/types": ["./node_modules/@pezkuwi/types/cjs/index.d.ts"],
"@polkadot/types/*": ["./node_modules/@pezkuwi/types/cjs/*"],
"@polkadot/types-codec": ["./node_modules/@pezkuwi/types-codec/cjs/index.d.ts"],
"@polkadot/types-codec/*": ["./node_modules/@pezkuwi/types-codec/cjs/*"],
"@polkadot/api": ["./node_modules/@pezkuwi/api/cjs/index.d.ts"],
"@polkadot/api/*": ["./node_modules/@pezkuwi/api/cjs/*"],
"@pezkuwi/types": ["./node_modules/@pezkuwi/types/cjs/index.d.ts"],
"@pezkuwi/types/*": ["./node_modules/@pezkuwi/types/cjs/*"],
"@pezkuwi/types-codec": ["./node_modules/@pezkuwi/types-codec/cjs/index.d.ts"],
"@pezkuwi/types-codec/*": ["./node_modules/@pezkuwi/types-codec/cjs/*"],
"@pezkuwi/api": ["./node_modules/@pezkuwi/api/cjs/index.d.ts"],
"@pezkuwi/api/*": ["./node_modules/@pezkuwi/api/cjs/*"]
},
"baseUrl": "."
}, },
"include": [ "include": [
"src/**/*", "src/**/*",
+9177 -12285
View File
File diff suppressed because it is too large Load Diff