mirror of
https://github.com/cerc-io/watcher-ts
synced 2026-09-09 17:24:07 +00:00
Invoke subgraph handler in watcher event processing (#34)
* Invoke subgraph handler in watcher event processing * Fix error when invoking subgraph handler * Parse events using event signature specified in subgraph yaml * Use contract abi to parse event params * Invoke event handler based on event signature * Fill event with block and transaction data * Comment missing fields in block and transaction data
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import { getDummyEventData } from '../test/utils';
|
||||
import { instantiate } from './loader';
|
||||
import { createEvent } from './utils';
|
||||
|
||||
@@ -26,8 +27,10 @@ describe('call handler in mapping code', () => {
|
||||
// TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533
|
||||
_start();
|
||||
|
||||
const eventData = getDummyEventData();
|
||||
|
||||
// Create event params data.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'param1',
|
||||
value: 'abc',
|
||||
@@ -36,7 +39,7 @@ describe('call handler in mapping code', () => {
|
||||
{
|
||||
name: 'param2',
|
||||
value: BigInt(123),
|
||||
kind: 'unsignedBigInt'
|
||||
kind: 'uint256'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -44,7 +47,7 @@ describe('call handler in mapping code', () => {
|
||||
const contractAddress = '0xCA6D29232D1435D8198E3E5302495417dD073d61';
|
||||
|
||||
// Create Test event to be passed to handler.
|
||||
const test = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const test = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await handleTest(test);
|
||||
});
|
||||
|
||||
@@ -11,10 +11,13 @@ import { createEvent } from './utils';
|
||||
import edenNetworkAbi from '../test/subgraph/eden/EdenNetwork/abis/EdenNetwork.json';
|
||||
import merkleDistributorAbi from '../test/subgraph/eden/EdenNetworkDistribution/abis/MerkleDistributor.json';
|
||||
import distributorGovernanceAbi from '../test/subgraph/eden/EdenNetworkGovernance/abis/DistributorGovernance.json';
|
||||
import { getDummyEventData } from '../test/utils';
|
||||
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
describe('eden wasm loader tests', () => {
|
||||
const eventData = getDummyEventData();
|
||||
|
||||
describe('EdenNetwork wasm', () => {
|
||||
let exports: any;
|
||||
|
||||
@@ -44,10 +47,10 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy SlotClaimedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'slot',
|
||||
kind: 'i32',
|
||||
kind: 'uint8',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
@@ -62,28 +65,28 @@ describe('eden wasm loader tests', () => {
|
||||
},
|
||||
{
|
||||
name: 'newBidAmount',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint128',
|
||||
value: BigInt(1)
|
||||
},
|
||||
{
|
||||
name: 'oldBidAmount',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint128',
|
||||
value: BigInt(1)
|
||||
},
|
||||
{
|
||||
name: 'taxNumerator',
|
||||
kind: 'i32',
|
||||
kind: 'uint16',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
name: 'taxDenominator',
|
||||
kind: 'i32',
|
||||
kind: 'uint16',
|
||||
value: 1
|
||||
}
|
||||
];
|
||||
|
||||
// Create dummy SlotClaimedEvent to be passed to handler.
|
||||
const slotClaimedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const slotClaimedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await slotClaimed(slotClaimedEvent);
|
||||
});
|
||||
@@ -94,10 +97,10 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy SlotDelegateUpdatedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'slot',
|
||||
kind: 'i32',
|
||||
kind: 'uint8',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
@@ -118,7 +121,7 @@ describe('eden wasm loader tests', () => {
|
||||
];
|
||||
|
||||
// Create dummy SlotDelegateUpdatedEvent to be passed to handler.
|
||||
const slotClaimedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const slotClaimedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await slotDelegateUpdated(slotClaimedEvent);
|
||||
});
|
||||
@@ -129,7 +132,7 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy StakeEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'staker',
|
||||
kind: 'address',
|
||||
@@ -137,13 +140,13 @@ describe('eden wasm loader tests', () => {
|
||||
},
|
||||
{
|
||||
name: 'stakeAmount',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint256',
|
||||
value: BigInt(1)
|
||||
}
|
||||
];
|
||||
|
||||
// Create dummy StakeEvent to be passed to handler.
|
||||
const stakeEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const stakeEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await stake(stakeEvent);
|
||||
});
|
||||
@@ -154,7 +157,7 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy UnstakeEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'staker',
|
||||
kind: 'address',
|
||||
@@ -162,13 +165,13 @@ describe('eden wasm loader tests', () => {
|
||||
},
|
||||
{
|
||||
name: 'unstakedAmount',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uin256',
|
||||
value: BigInt(1)
|
||||
}
|
||||
];
|
||||
|
||||
// Create dummy UnstakeEvent to be passed to handler.
|
||||
const unstakeEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const unstakeEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await unstake(unstakeEvent);
|
||||
});
|
||||
@@ -203,15 +206,15 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy ClaimedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'index',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint256',
|
||||
value: BigInt(1)
|
||||
},
|
||||
{
|
||||
name: 'totalEarned',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint256',
|
||||
value: BigInt(1)
|
||||
},
|
||||
{
|
||||
@@ -221,13 +224,13 @@ describe('eden wasm loader tests', () => {
|
||||
},
|
||||
{
|
||||
name: 'claimed',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint256',
|
||||
value: BigInt(1)
|
||||
}
|
||||
];
|
||||
|
||||
// Create dummy ClaimedEvent to be passed to handler.
|
||||
const claimedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const claimedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await claimed(claimedEvent);
|
||||
});
|
||||
@@ -238,7 +241,7 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy SlashedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'account',
|
||||
kind: 'address',
|
||||
@@ -246,13 +249,13 @@ describe('eden wasm loader tests', () => {
|
||||
},
|
||||
{
|
||||
name: 'slashed',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint256',
|
||||
value: BigInt(1)
|
||||
}
|
||||
];
|
||||
|
||||
// Create dummy SlashedEvent to be passed to handler.
|
||||
const slashedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const slashedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await slashed(slashedEvent);
|
||||
});
|
||||
@@ -263,15 +266,15 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy MerkleRootUpdatedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'merkleRoot',
|
||||
kind: 'bytes',
|
||||
kind: 'bytes32',
|
||||
value: ethers.utils.hexlify(ethers.utils.randomBytes(32))
|
||||
},
|
||||
{
|
||||
name: 'distributionNumber',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint256',
|
||||
value: BigInt(1)
|
||||
},
|
||||
{
|
||||
@@ -282,7 +285,7 @@ describe('eden wasm loader tests', () => {
|
||||
];
|
||||
|
||||
// Create dummy MerkleRootUpdatedEvent to be passed to handler.
|
||||
const merkleRootUpdatedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const merkleRootUpdatedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await merkleRootUpdated(merkleRootUpdatedEvent);
|
||||
});
|
||||
@@ -293,7 +296,7 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy AccountUpdatedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'account',
|
||||
kind: 'address',
|
||||
@@ -301,18 +304,18 @@ describe('eden wasm loader tests', () => {
|
||||
},
|
||||
{
|
||||
name: 'totalClaimed',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint256',
|
||||
value: BigInt(1)
|
||||
},
|
||||
{
|
||||
name: 'totalSlashed',
|
||||
kind: 'unsignedBigInt',
|
||||
kind: 'uint256',
|
||||
value: BigInt(1)
|
||||
}
|
||||
];
|
||||
|
||||
// Create dummy AccountUpdatedEvent to be passed to handler.
|
||||
const accountUpdatedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const accountUpdatedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await accountUpdated(accountUpdatedEvent);
|
||||
});
|
||||
@@ -347,7 +350,7 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy BlockProducerAddedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'produces',
|
||||
kind: 'address',
|
||||
@@ -356,7 +359,7 @@ describe('eden wasm loader tests', () => {
|
||||
];
|
||||
|
||||
// Create dummy BlockProducerAddedEvent to be passed to handler.
|
||||
const blockProducerAddedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const blockProducerAddedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await blockProducerAdded(blockProducerAddedEvent);
|
||||
});
|
||||
@@ -367,7 +370,7 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy BlockProducerRemovedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'producer',
|
||||
kind: 'address',
|
||||
@@ -376,7 +379,7 @@ describe('eden wasm loader tests', () => {
|
||||
];
|
||||
|
||||
// Create dummy BlockProducerRemovedEvent to be passed to handler.
|
||||
const blockProducerRemovedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const blockProducerRemovedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await blockProducerRemoved(blockProducerRemovedEvent);
|
||||
});
|
||||
@@ -387,7 +390,7 @@ describe('eden wasm loader tests', () => {
|
||||
} = exports;
|
||||
|
||||
// Create dummy BlockProducerRewardCollectorChangedEvent params.
|
||||
const eventParamsData = [
|
||||
eventData.eventParams = [
|
||||
{
|
||||
name: 'producer',
|
||||
kind: 'address',
|
||||
@@ -406,7 +409,7 @@ describe('eden wasm loader tests', () => {
|
||||
];
|
||||
|
||||
// Create dummy BlockProducerRewardCollectorChangedEvent to be passed to handler.
|
||||
const blockProducerRewardCollectorChangedEvent = await createEvent(exports, contractAddress, eventParamsData);
|
||||
const blockProducerRewardCollectorChangedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await blockProducerRewardCollectorChanged(blockProducerRewardCollectorChangedEvent);
|
||||
});
|
||||
@@ -416,8 +419,10 @@ describe('eden wasm loader tests', () => {
|
||||
rewardScheduleChanged
|
||||
} = exports;
|
||||
|
||||
eventData.eventParams = [];
|
||||
|
||||
// Create dummy RewardScheduleChangedEvent to be passed to handler.
|
||||
const rewardScheduleChangedEvent = await createEvent(exports, contractAddress, []);
|
||||
const rewardScheduleChangedEvent = await createEvent(exports, contractAddress, eventData);
|
||||
|
||||
await rewardScheduleChanged(rewardScheduleChangedEvent);
|
||||
});
|
||||
|
||||
+143
-114
@@ -14,128 +14,39 @@ interface EventParam {
|
||||
kind: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create ethereum event.
|
||||
* @param exports
|
||||
* @param contractAddress
|
||||
* @param eventParamsData
|
||||
* @returns
|
||||
*/
|
||||
export const createEvent = async (exports: any, contractAddress: string, eventParamsData: EventParam[]): Promise<any> => {
|
||||
const {
|
||||
__newString,
|
||||
__newArray,
|
||||
Address,
|
||||
BigInt,
|
||||
ethereum,
|
||||
Bytes,
|
||||
ByteArray,
|
||||
id_of_type: idOfType
|
||||
} = exports;
|
||||
interface Block {
|
||||
hash: string;
|
||||
number: number;
|
||||
timestamp: number;
|
||||
parentHash: string;
|
||||
}
|
||||
|
||||
// Create dummy block data.
|
||||
const block = await ethereum.Block.__new(
|
||||
await Bytes.empty(),
|
||||
await Bytes.empty(),
|
||||
await Bytes.empty(),
|
||||
await Address.zero(),
|
||||
await Bytes.empty(),
|
||||
await Bytes.empty(),
|
||||
await Bytes.empty(),
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
null
|
||||
);
|
||||
interface Transaction {
|
||||
hash: string;
|
||||
index: number;
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
// Create dummy transaction data.
|
||||
const transaction = await ethereum.Transaction.__new(
|
||||
await Bytes.empty(),
|
||||
await BigInt.fromI32(0),
|
||||
await Address.zero(),
|
||||
null,
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
await Bytes.empty()
|
||||
);
|
||||
|
||||
const eventParamArrayPromise = eventParamsData.map(async data => {
|
||||
const { name, value, kind } = data;
|
||||
let ethValue;
|
||||
|
||||
switch (kind) {
|
||||
case 'unsignedBigInt': {
|
||||
const bigIntString = await (await __newString(value.toString()));
|
||||
const bigInt = await BigInt.fromString(bigIntString);
|
||||
ethValue = await ethereum.Value.fromUnsignedBigInt(bigInt);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'string': {
|
||||
ethValue = await ethereum.Value.fromString(await __newString(value));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'i32': {
|
||||
ethValue = await ethereum.Value.fromI32(value);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'address': {
|
||||
ethValue = await ethereum.Value.fromAddress(await Address.fromString(await __newString(value)));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'bytes': {
|
||||
const byteArray = await ByteArray.fromHexString(await __newString(value));
|
||||
ethValue = await ethereum.Value.fromBytes(await Bytes.fromByteArray(byteArray));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ethereum.EventParam.__new(
|
||||
await __newString(name),
|
||||
ethValue
|
||||
);
|
||||
});
|
||||
|
||||
const eventParamArray = await Promise.all(eventParamArrayPromise);
|
||||
const eventParams = await __newArray(await idOfType(TypeId.ArrayEventParam), eventParamArray);
|
||||
|
||||
// Dummy contract address string.
|
||||
const addStrPtr = await __newString(contractAddress);
|
||||
|
||||
// Create Test event to be passed to handler.
|
||||
return ethereum.Event.__new(
|
||||
await Address.fromString(addStrPtr),
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
null,
|
||||
block,
|
||||
transaction,
|
||||
eventParams
|
||||
);
|
||||
};
|
||||
export interface EventData {
|
||||
block: Block;
|
||||
tx: Transaction;
|
||||
eventParams: EventParam[];
|
||||
eventIndex: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get value from graph-ts ethereum.Value wasm instance.
|
||||
* @param exports
|
||||
* @param instanceExports
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
export const fromEthereumValue = async (exports: any, value: any): Promise<any> => {
|
||||
export const fromEthereumValue = async (instanceExports: any, value: any): Promise<any> => {
|
||||
const {
|
||||
__getString,
|
||||
BigInt,
|
||||
Address
|
||||
} = exports;
|
||||
} = instanceExports;
|
||||
|
||||
const kind = await value.kind;
|
||||
|
||||
@@ -173,12 +84,12 @@ export const fromEthereumValue = async (exports: any, value: any): Promise<any>
|
||||
|
||||
/**
|
||||
* Method to get ethereum value for passing to wasm instance.
|
||||
* @param exports
|
||||
* @param instanceExports
|
||||
* @param value
|
||||
* @param type
|
||||
* @returns
|
||||
*/
|
||||
export const toEthereumValue = async (exports: any, value: any, type: string): Promise<any> => {
|
||||
export const toEthereumValue = async (instanceExports: any, value: any, type: string): Promise<any> => {
|
||||
const {
|
||||
__newString,
|
||||
ByteArray,
|
||||
@@ -186,7 +97,7 @@ export const toEthereumValue = async (exports: any, value: any, type: string): P
|
||||
Address,
|
||||
ethereum,
|
||||
BigInt
|
||||
} = exports;
|
||||
} = instanceExports;
|
||||
|
||||
// For boolean type.
|
||||
if (type === 'bool') {
|
||||
@@ -223,6 +134,125 @@ export const toEthereumValue = async (exports: any, value: any, type: string): P
|
||||
return ethereum.Value.fromString(await __newString(value));
|
||||
};
|
||||
|
||||
/**
|
||||
* Method to create ethereum event.
|
||||
* @param instanceExports
|
||||
* @param contractAddress
|
||||
* @param eventParamsData
|
||||
* @returns
|
||||
*/
|
||||
export const createEvent = async (instanceExports: any, contractAddress: string, eventData: EventData): Promise<any> => {
|
||||
const {
|
||||
tx,
|
||||
eventIndex,
|
||||
eventParams: eventParamsData,
|
||||
block: blockData
|
||||
} = eventData;
|
||||
|
||||
const {
|
||||
__newString,
|
||||
__newArray,
|
||||
Address,
|
||||
BigInt,
|
||||
ethereum,
|
||||
Bytes,
|
||||
ByteArray,
|
||||
id_of_type: idOfType
|
||||
} = instanceExports;
|
||||
|
||||
// Fill block data.
|
||||
const blockHashByteArray = await ByteArray.fromHexString(await __newString(blockData.hash));
|
||||
const blockHash = await Bytes.fromByteArray(blockHashByteArray);
|
||||
|
||||
const parentHashByteArray = await ByteArray.fromHexString(await __newString(blockData.parentHash));
|
||||
const parentHash = await Bytes.fromByteArray(parentHashByteArray);
|
||||
|
||||
const blockNumber = await BigInt.fromI32(blockData.number);
|
||||
|
||||
const blockTimestamp = await BigInt.fromI32(blockData.timestamp);
|
||||
|
||||
// Missing fields from watcher in block data:
|
||||
// unclesHash
|
||||
// author
|
||||
// stateRoot
|
||||
// transactionsRoot
|
||||
// receiptsRoot
|
||||
// gasUsed
|
||||
// gasLimit
|
||||
// difficulty
|
||||
// totalDifficulty
|
||||
// size
|
||||
const block = await ethereum.Block.__new(
|
||||
blockHash,
|
||||
parentHash,
|
||||
await Bytes.empty(),
|
||||
await Address.zero(),
|
||||
await Bytes.empty(),
|
||||
await Bytes.empty(),
|
||||
await Bytes.empty(),
|
||||
blockNumber,
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
blockTimestamp,
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
null
|
||||
);
|
||||
|
||||
// Fill transaction data.
|
||||
const txHashByteArray = await ByteArray.fromHexString(await __newString(tx.hash));
|
||||
const txHash = await Bytes.fromByteArray(txHashByteArray);
|
||||
|
||||
const txIndex = await BigInt.fromI32(tx.index);
|
||||
|
||||
const txFrom = await Address.fromString(await __newString(tx.from));
|
||||
|
||||
const txTo = tx.to && await Address.fromString(await __newString(tx.to));
|
||||
|
||||
// Missing fields from watcher in transaction data:
|
||||
// value
|
||||
// gasLimit
|
||||
// gasPrice
|
||||
// input
|
||||
const transaction = await ethereum.Transaction.__new(
|
||||
txHash,
|
||||
txIndex,
|
||||
txFrom,
|
||||
txTo,
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
await BigInt.fromI32(0),
|
||||
await Bytes.empty()
|
||||
);
|
||||
|
||||
const eventParamArrayPromise = eventParamsData.map(async data => {
|
||||
const { name, value, kind } = data;
|
||||
|
||||
const ethValue = await toEthereumValue(instanceExports, value, kind);
|
||||
|
||||
return ethereum.EventParam.__new(
|
||||
await __newString(name),
|
||||
ethValue
|
||||
);
|
||||
});
|
||||
|
||||
const eventParamArray = await Promise.all(eventParamArrayPromise);
|
||||
const eventParams = await __newArray(await idOfType(TypeId.ArrayEventParam), eventParamArray);
|
||||
|
||||
const addStrPtr = await __newString(contractAddress);
|
||||
|
||||
// Create event to be passed to handler.
|
||||
return ethereum.Event.__new(
|
||||
await Address.fromString(addStrPtr),
|
||||
await BigInt.fromI32(eventIndex),
|
||||
await BigInt.fromI32(0),
|
||||
null,
|
||||
block,
|
||||
transaction,
|
||||
eventParams
|
||||
);
|
||||
};
|
||||
|
||||
export const getSubgraphConfig = async (subgraphPath: string): Promise<any> => {
|
||||
const configFilePath = path.resolve(path.join(subgraphPath, 'subgraph.yaml'));
|
||||
const fileExists = await fs.pathExists(configFilePath);
|
||||
@@ -231,7 +261,6 @@ export const getSubgraphConfig = async (subgraphPath: string): Promise<any> => {
|
||||
throw new Error(`Config file not found: ${configFilePath}`);
|
||||
}
|
||||
|
||||
console.log(configFilePath);
|
||||
const config = yaml.load(await fs.readFile(configFilePath, 'utf8'));
|
||||
log('config', JSON.stringify(config, null, 2));
|
||||
|
||||
|
||||
@@ -6,18 +6,24 @@ import 'reflect-metadata';
|
||||
import debug from 'debug';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { ContractInterface } from 'ethers';
|
||||
import { ContractInterface, utils } from 'ethers';
|
||||
|
||||
import { getSubgraphConfig } from './utils';
|
||||
import { instantiate } from './loader';
|
||||
import { ResultObject } from '@vulcanize/assemblyscript/lib/loader';
|
||||
|
||||
import { createEvent, getSubgraphConfig } from './utils';
|
||||
import { instantiate } from './loader';
|
||||
|
||||
const log = debug('vulcanize:graph-watcher');
|
||||
|
||||
interface DataSource {
|
||||
instance: ResultObject & { exports: any },
|
||||
contractInterface: utils.Interface
|
||||
}
|
||||
|
||||
export class GraphWatcher {
|
||||
_subgraphPath: string;
|
||||
_dataSources: any[] = []
|
||||
_instanceMap: { [key: string]: ResultObject & { exports: any } } = {};
|
||||
_dataSources: any[] = [];
|
||||
_dataSourceMap: { [key: string]: DataSource } = {};
|
||||
|
||||
constructor (subgraphPath: string) {
|
||||
this._subgraphPath = subgraphPath;
|
||||
@@ -27,33 +33,57 @@ export class GraphWatcher {
|
||||
const { dataSources } = await getSubgraphConfig(this._subgraphPath);
|
||||
this._dataSources = dataSources;
|
||||
|
||||
this._instanceMap = this._dataSources.reduce(async (acc: { [key: string]: ResultObject & { exports: any } }, dataSource: any) => {
|
||||
const { source: { address }, mapping } = dataSource;
|
||||
// Create wasm instance and contract interface for each dataSource in subgraph yaml.
|
||||
const dataPromises = this._dataSources.map(async (dataSource: any) => {
|
||||
const { source: { address, abi }, mapping } = dataSource;
|
||||
const { abis, file } = mapping;
|
||||
|
||||
const abisMap = abis.reduce((acc: {[key: string]: ContractInterface}, abi: any) => {
|
||||
const { name, file } = abi;
|
||||
const abiFilePath = path.join(this._subgraphPath, file);
|
||||
acc[name] = JSON.parse(fs.readFileSync(abiFilePath).toString());
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const contractInterface = new utils.Interface(abisMap[abi]);
|
||||
|
||||
const data = {
|
||||
abis: abis.reduce((acc: {[key: string]: ContractInterface}, abi: any) => {
|
||||
const { name, file } = abi;
|
||||
const abiFilePath = path.join(this._subgraphPath, file);
|
||||
acc[name] = JSON.parse(fs.readFileSync(abiFilePath).toString());
|
||||
return acc;
|
||||
}, {}),
|
||||
abis: abisMap,
|
||||
dataSource: {
|
||||
address
|
||||
}
|
||||
};
|
||||
|
||||
const filePath = path.join(this._subgraphPath, file);
|
||||
const instance = await instantiate(filePath, data);
|
||||
|
||||
acc[address] = instance;
|
||||
return {
|
||||
instance: await instantiate(filePath, data),
|
||||
contractInterface
|
||||
};
|
||||
}, {});
|
||||
|
||||
const data = await Promise.all(dataPromises);
|
||||
|
||||
// Create a map from dataSource contract address to instance and contract interface.
|
||||
this._dataSourceMap = this._dataSources.reduce((acc: { [key: string]: DataSource }, dataSource: any, index: number) => {
|
||||
const { instance } = data[index];
|
||||
|
||||
// Important to call _start for built subgraphs on instantiation!
|
||||
// TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533
|
||||
instance.exports._start();
|
||||
|
||||
const { source: { address } } = dataSource;
|
||||
acc[address] = data[index];
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
async handleEvent (eventData: any) {
|
||||
const { contract } = eventData;
|
||||
const { contract, event, eventSignature, block, tx, eventIndex } = eventData;
|
||||
|
||||
// Get dataSource in subgraph yaml based on contract address.
|
||||
const dataSource = this._dataSources.find(dataSource => dataSource.source.address === contract);
|
||||
|
||||
if (!dataSource) {
|
||||
@@ -61,16 +91,36 @@ export class GraphWatcher {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Call instance methods based on event signature.
|
||||
// value should contain event signature.
|
||||
// Get event handler based on event signature.
|
||||
const eventHandler = dataSource.mapping.eventHandlers.find((eventHandler: any) => eventHandler.event === eventSignature);
|
||||
|
||||
const [{ handler }] = dataSource.mapping.eventHandlers;
|
||||
const { exports } = this._instanceMap[contract];
|
||||
if (!eventHandler) {
|
||||
log(`No handler configured in subgraph for event ${eventSignature}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create ethereum event to be passed to handler.
|
||||
// TODO: Create ethereum event to be passed to handler.
|
||||
// const ethereumEvent = await createEvent(exports, address, event);
|
||||
const { instance: { exports }, contractInterface } = this._dataSourceMap[contract];
|
||||
|
||||
await exports[handler]();
|
||||
const eventFragment = contractInterface.getEvent(eventSignature);
|
||||
|
||||
const eventParams = eventFragment.inputs.map((input) => {
|
||||
return {
|
||||
name: input.name,
|
||||
value: event[input.name],
|
||||
kind: input.type
|
||||
};
|
||||
});
|
||||
|
||||
const data = {
|
||||
eventParams: eventParams,
|
||||
block,
|
||||
tx,
|
||||
eventIndex
|
||||
};
|
||||
|
||||
// Create ethereum event to be passed to the wasm event handler.
|
||||
const ethereumEvent = await createEvent(exports, contract, data);
|
||||
|
||||
await exports[eventHandler.handler](ethereumEvent);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user