Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added event handler for saving ibc channels #10

Merged
merged 8 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
"dependencies": {
"@subql/types-cosmos": "^3.2.3",
"@types/node": "^17.0.21",
"bech32": "^2.0.0",
"js-sha256": "^0.11.0",
"pino": "^7.8.0",
"ts-proto": "^1.112.1",
"tslib": "^2.3.1"
Expand Down
20 changes: 20 additions & 0 deletions project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ const project: CosmosProject = {
// },
// },
// },
{
handler: "handleIbcSendPacketEvent",
kind: CosmosHandlerKind.Event,
filter: {
type: "send_packet",
messageFilter: {
type: "/ibc.applications.transfer.v1.MsgTransfer",
},
},
},
{
handler: "handleIbcReceivePacketEvent",
kind: CosmosHandlerKind.Event,
filter: {
type: "recv_packet",
messageFilter: {
type: "/ibc.core.channel.v1.MsgRecvPacket",
},
},
},
{
handler: "handleStateChangeEvent",
kind: CosmosHandlerKind.Event,
Expand Down
27 changes: 27 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,30 @@ type ReserveMetrics @entity {
totalFeeMinted: BigInt!
allocations: [ReserveAllocationMetrics] @derivedFrom(field: "reserveMetrics")
}

type Account @entity {
id: ID!
}

type IBCChannel @entity {
id: ID!
channelName: String!
escrowAccount: Account!
}

enum TransferType {
SEND
RECEIVE
}

type IBCTransfer @entity {
id: ID!
blockTime: Date!
blockHeight: BigInt!
channel: IBCChannel!
srcAccount: Account!
destAccount: Account!
denom: String!
amount: String!
transferType: TransferType!
}
21 changes: 19 additions & 2 deletions src/mappings/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ export const EVENT_TYPES = {
STORAGE: "storage",
SUBMIT_PROPOSAL: "submit_proposal",
TRANSFER: "transfer",
SEND_PACKET: "send_packet",
RECEIVE_PACKET: "recv_packet",
IBC_TRANSFER: "ibc_transfer",
FUNGIBLE_TOKEN_PACKET: "fungible_token_packet",
};

export const VAULT_STATES = {
LIQUIDATING: "liquidating",
LIQUIDATED: "liquidated"
}
LIQUIDATED: "liquidated",
};

export const VALUE_KEY = b64encode("value");
export const STORE_KEY = b64encode("store");
Expand All @@ -31,3 +35,16 @@ export const KEY_KEY = b64encode("key");
export const STORE_NAME_KEY = b64encode("store_name");
export const SUBKEY_KEY = b64encode("store_subkey");
export const UNPROVED_VALUE_KEY = b64encode("unproved_value");
export const PACKET_DATA_KEY = "packet_data";
export const PACKET_SRC_CHANNEL_KEY = "packet_src_channel";
export const PACKET_DST_CHANNEL_KEY = "packet_dst_channel";
export const PACKET_SRC_PORT_KEY = "packet_src_port";
export const PACKET_DST_PORT_KEY = "packet_dst_port";
export const ACTION_KEY = b64encode("action");
export const IBC_MESSAGE_TRANSFER_VALUE = b64encode("/ibc.applications.transfer.v1.MsgTransfer");
export const IBC_MESSAGE_RECEIVE_PACKET_VALUE = b64encode("/ibc.core.channel.v1.MsgRecvPacket");
export const RECEPIENT_KEY = b64encode("recipient");
export const SENDER_KEY = b64encode("sender");
export const RECEIVER_KEY = b64encode("receiver");
export const AMOUNT_KEY = b64encode("amount");
export const TRANSFER_PORT_VALUE = "transfer";
102 changes: 102 additions & 0 deletions src/mappings/mappingHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import {
StateChangeEvent,
IBCChannel,
IBCTransfer,
TransferType,
} from "../types";
import { CosmosEvent } from "@subql/types-cosmos";
import {
b64decode,
extractStoragePath,
getStateChangeModule,
resolveBrandNamesAndValues,
getEscrowAddress,
} from "./utils";

import {
Expand All @@ -18,6 +22,12 @@ import {
STORE_NAME_KEY,
SUBKEY_KEY,
UNPROVED_VALUE_KEY,
PACKET_DATA_KEY,
PACKET_SRC_CHANNEL_KEY,
PACKET_DST_CHANNEL_KEY,
PACKET_DST_PORT_KEY,
PACKET_SRC_PORT_KEY,
TRANSFER_PORT_VALUE,
} from "./constants";
import { psmEventKit } from "./events/psm";
import { boardAuxEventKit } from "./events/boardAux";
Expand All @@ -30,6 +40,98 @@ BigInt.prototype.toJSON = function () {
return this.toString();
};

async function saveIbcChannel(channelName: string) {
const generatedEscrowAddress = getEscrowAddress(TRANSFER_PORT_VALUE, channelName);

const channelRecord = new IBCChannel(channelName, channelName, generatedEscrowAddress);
await channelRecord.save();
}

export async function handleIbcSendPacketEvent(cosmosEvent: CosmosEvent): Promise<void> {
const { event, block, tx } = cosmosEvent;
if (event.type != EVENT_TYPES.SEND_PACKET) {
logger.warn("Not valid send_packet event.");
return;
}

const packetSrcPortAttr = event.attributes.find((a) => a.key === PACKET_SRC_PORT_KEY);
if (!packetSrcPortAttr || packetSrcPortAttr.value !== TRANSFER_PORT_VALUE) {
logger.warn("packet_src_port is not transfer");
return;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that event attributes are not encoded? While working on indexing balances, I noticed that some events had attributes that were not encoded.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it is possible. in fact, packet_src_port is not encoded

const packetDataAttr = event.attributes.find((a) => a.key === PACKET_DATA_KEY);
if (!packetDataAttr) {
logger.warn("No packet data attribute found");
return;
}

const packetSrcChannelAttr = event.attributes.find((a) => a.key === PACKET_SRC_CHANNEL_KEY);
if (!packetSrcChannelAttr) {
logger.warn("No packet source channel found");
return;
}
const { amount, denom, receiver, sender } = JSON.parse(packetDataAttr.value);
const sourceChannel = packetSrcChannelAttr.value;

await saveIbcChannel(sourceChannel);

const transferRecord = new IBCTransfer(
tx.hash,
block.header.time as any,
BigInt(block.header.height),
packetSrcChannelAttr.value,
sender,
receiver,
denom,
amount,
TransferType.SEND,
);
await transferRecord.save();
}

export async function handleIbcReceivePacketEvent(cosmosEvent: CosmosEvent): Promise<void> {
const { event, block, tx } = cosmosEvent;
if (event.type != EVENT_TYPES.RECEIVE_PACKET) {
logger.warn("Not valid recv_packet event.");
return;
}

const packetDataAttr = event.attributes.find((a) => a.key === PACKET_DATA_KEY);
if (!packetDataAttr) {
logger.warn("No packet data attribute found");
return;
}

const packetDestPortAttr = event.attributes.find((a) => a.key === PACKET_DST_PORT_KEY);
if (!packetDestPortAttr || packetDestPortAttr.value !== TRANSFER_PORT_VALUE) {
logger.warn("packet_dest_port is not transfer");
return;
}

const packetDstChannelAttr = event.attributes.find((a) => a.key === PACKET_DST_CHANNEL_KEY);
if (!packetDstChannelAttr) {
logger.warn("No packet destination channel found");
return;
}
const { amount, denom, receiver, sender } = JSON.parse(packetDataAttr.value);
const destinationChannel = packetDstChannelAttr.value;

await saveIbcChannel(destinationChannel);

const transferRecord = new IBCTransfer(
tx.hash,
block.header.time as any,
BigInt(block.header.height),
destinationChannel,
sender,
receiver,
denom,
amount,
TransferType.RECEIVE,
);
await transferRecord.save();
}

export async function handleStateChangeEvent(cosmosEvent: CosmosEvent): Promise<void> {
const { event, block } = cosmosEvent;

Expand Down
13 changes: 13 additions & 0 deletions src/mappings/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { bech32 } from "bech32";
import sha256 from "js-sha256";

export function extractBrand(str: string): string {
return str.replace("Alleged: ", "").replace(" brand", "");
}
Expand Down Expand Up @@ -95,3 +98,13 @@ export function dateToDayKey(timestamp: any): number {
const day = date.getUTCDate().toString().padStart(2, "0");
return parseInt(`${year}${month}${day}`);
}

export const getEscrowAddress = (port: string, channel: string) => {
const version = "ics20-1";
const chainPrefix = "agoric";
const stringtoSha = Buffer.from([...Buffer.from(version), 0, ...Buffer.from(`${port}/${channel}`)]);
const shaHash = sha256.sha256.array(stringtoSha.toString());
const bechWords = bech32.toWords(shaHash.slice(0, 20));
const address = bech32.encode(chainPrefix, bechWords);
return address;
};