Skip to content

Commit cbf7aa7

Browse files
feat: track almanac registrations
1 parent 6f5235b commit cbf7aa7

File tree

6 files changed

+148
-10
lines changed

6 files changed

+148
-10
lines changed

project.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,10 @@ dataSources:
130130
kind: cosmos/MessageHandler
131131
filter:
132132
type: "/cosmos.authz.v1beta1.MsgExec"
133+
- handler: handleAlmanacRegistration
134+
kind: cosmos/EventHandler
135+
filter:
136+
type: "wasm"
137+
messageFilter:
138+
type: "/cosmwasm.wasm.v1.MsgExecuteContract"
139+
contractCall: "register"

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
//Exports all handler functions
22
export * from "./mappings/mappingHandlers";
3+
export {parseAttributes} from "./mappings/utils";

src/mappings/almanac/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./registrations";

src/mappings/almanac/registrations.ts

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import {
2+
attemptHandling,
3+
messageId,
4+
parseAttributes,
5+
unprocessedEventHandler,
6+
WasmdEventAttributesI
7+
} from "../utils";
8+
import {CosmosEvent} from "@subql/types-cosmos";
9+
import {Agent, AlmanacRecord, AlmanacRegistration, Contract} from "../../types";
10+
11+
export async function handleAlmanacRegistration(event: CosmosEvent): Promise<void> {
12+
await attemptHandling(event, _handleAlmanacRegistration, unprocessedEventHandler);
13+
}
14+
15+
export interface RegisterAttributes extends WasmdEventAttributesI {
16+
expiry_height?: string,
17+
sequence?: number;
18+
signature?: string;
19+
agent_address?: string;
20+
record?: string,
21+
}
22+
23+
async function _handleAlmanacRegistration(event: CosmosEvent): Promise<void> {
24+
const id = messageId(event);
25+
logger.info(`[handleAlmanacRegistration] (tx ${event.tx.hash}): indexing AlmanacRegistration ${id}`);
26+
logger.debug(`[handleAlmanacRegistration] (event.log.log): ${JSON.stringify(event.log.log, null, 2)}`);
27+
28+
/* NB: signature verification is handled by the almanac contract!
29+
* This handler assumes that the contract will only emit events
30+
* with valid signatures corresponding to the agent address.
31+
*/
32+
33+
const {
34+
_contract_address,
35+
agent_address,
36+
expiry_height,
37+
sequence,
38+
signature,
39+
record: recordStr,
40+
} = parseAttributes<RegisterAttributes>(event.event.attributes);
41+
42+
if (!agent_address) {
43+
logger.warn("[handleAlmanacRegistration]: missing address");
44+
return;
45+
}
46+
47+
if (!signature) {
48+
logger.warn("[handleAlmanacRegistration]: missing signature");
49+
return;
50+
}
51+
52+
if (!sequence) {
53+
logger.warn("[handleAlmanacRegistration]: missing sequence");
54+
return;
55+
}
56+
57+
if (!expiry_height) {
58+
logger.warn("[handleAlmanacRegistration]: missing expiry_height");
59+
return;
60+
}
61+
62+
if (!recordStr) {
63+
logger.warn("[handleAlmanacRegistration]: missing record");
64+
return;
65+
}
66+
67+
const record = JSON.parse(recordStr);
68+
if (!record || !record.service) {
69+
logger.warn("[handleAlmanacRegistration]: missing record service");
70+
return;
71+
}
72+
73+
// NB: ensure agent exists
74+
const agent = await Agent.get(agent_address);
75+
if (!agent) {
76+
await Agent.create({
77+
id: agent_address,
78+
}).save();
79+
}
80+
81+
// NB: ensure contract exists
82+
const contract = await Contract.get(_contract_address);
83+
if (!contract) {
84+
logger.warn(`[handleAlmanacRegistration]: unable to find contract with address: ${_contract_address}`);
85+
return;
86+
}
87+
88+
const recordEntity = AlmanacRecord.create({
89+
id,
90+
service: record.service,
91+
// eventId: id,
92+
transactionId: event.tx.hash,
93+
blockId: event.block.block.id,
94+
});
95+
await recordEntity.save();
96+
97+
const registrationEntity = AlmanacRegistration.create({
98+
id,
99+
expiryHeight: BigInt(expiry_height),
100+
signature,
101+
sequence,
102+
agentId: agent_address,
103+
recordId: id,
104+
contractId: _contract_address,
105+
// eventId: id,
106+
transactionId: event.tx.hash,
107+
blockId: event.block.block.id,
108+
});
109+
await registrationEntity.save();
110+
}

src/mappings/mappingHandlers.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from "./authz";
2+
export * from "./almanac";
23
export * from "./bank";
34
export * from "./dist";
45
export * from "./gov";

src/mappings/utils.ts

+28-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
import {CosmosBlock, CosmosEvent, CosmosMessage, CosmosTransaction} from "@subql/types-cosmos";
22
import {Account, Interface, UnprocessedEntity} from "../types";
3-
import { createHash } from "crypto";
3+
import {createHash} from "crypto";
4+
import {Attribute} from "@cosmjs/stargate/build/logs";
5+
6+
export type Primitive = CosmosEvent | CosmosMessage | CosmosTransaction | CosmosBlock;
7+
8+
export interface Primitives {
9+
event?: CosmosEvent;
10+
msg?: CosmosMessage;
11+
tx?: CosmosTransaction;
12+
block?: CosmosBlock;
13+
}
414

515
// messageId returns the id of the message passed or
616
// that of the message which generated the event passed.
@@ -43,15 +53,6 @@ export function getJaccardResult(payload: object): Interface {
4353
return prediction.getInterface(); // return best matched Interface to contract
4454
}
4555

46-
export type Primitive = CosmosEvent | CosmosMessage | CosmosTransaction | CosmosBlock;
47-
48-
export interface Primitives {
49-
event?: CosmosEvent;
50-
msg?: CosmosMessage;
51-
tx?: CosmosTransaction;
52-
block?: CosmosBlock;
53-
}
54-
5556
export async function attemptHandling(input: Primitive,
5657
handlerFn: (primitive) => Promise<void>,
5758
errorFn: (Error, Primitive) => void): Promise<void> {
@@ -168,3 +169,20 @@ class LegacyBridgeSwapStructure extends Structure {
168169
return Interface.LegacyBridgeSwap;
169170
}
170171
}
172+
173+
export interface BaseEventAttributesI {
174+
action: string;
175+
}
176+
177+
// (see: https://github.com/CosmWasm/wasmd/blob/main/x/wasm/keeper/events.go)
178+
export interface WasmdEventAttributesI extends BaseEventAttributesI {
179+
_contract_address: string;
180+
}
181+
182+
export function parseAttributes<T extends BaseEventAttributesI>(attributes: readonly Attribute[]): T {
183+
// @ts-ignore
184+
return attributes.reduce((acc, curr) => {
185+
acc[curr.key] = curr.value;
186+
return acc as T;
187+
}, {});
188+
}

0 commit comments

Comments
 (0)