mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-07-27 10:42:06 +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:
parent
6cca55a1ab
commit
43d64f9e4b
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
import { getDummyEventData } from '../test/utils';
|
||||||
import { instantiate } from './loader';
|
import { instantiate } from './loader';
|
||||||
import { createEvent } from './utils';
|
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
|
// TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533
|
||||||
_start();
|
_start();
|
||||||
|
|
||||||
|
const eventData = getDummyEventData();
|
||||||
|
|
||||||
// Create event params data.
|
// Create event params data.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'param1',
|
name: 'param1',
|
||||||
value: 'abc',
|
value: 'abc',
|
||||||
@ -36,7 +39,7 @@ describe('call handler in mapping code', () => {
|
|||||||
{
|
{
|
||||||
name: 'param2',
|
name: 'param2',
|
||||||
value: BigInt(123),
|
value: BigInt(123),
|
||||||
kind: 'unsignedBigInt'
|
kind: 'uint256'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -44,7 +47,7 @@ describe('call handler in mapping code', () => {
|
|||||||
const contractAddress = '0xCA6D29232D1435D8198E3E5302495417dD073d61';
|
const contractAddress = '0xCA6D29232D1435D8198E3E5302495417dD073d61';
|
||||||
|
|
||||||
// Create Test event to be passed to handler.
|
// 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);
|
await handleTest(test);
|
||||||
});
|
});
|
||||||
|
@ -11,10 +11,13 @@ import { createEvent } from './utils';
|
|||||||
import edenNetworkAbi from '../test/subgraph/eden/EdenNetwork/abis/EdenNetwork.json';
|
import edenNetworkAbi from '../test/subgraph/eden/EdenNetwork/abis/EdenNetwork.json';
|
||||||
import merkleDistributorAbi from '../test/subgraph/eden/EdenNetworkDistribution/abis/MerkleDistributor.json';
|
import merkleDistributorAbi from '../test/subgraph/eden/EdenNetworkDistribution/abis/MerkleDistributor.json';
|
||||||
import distributorGovernanceAbi from '../test/subgraph/eden/EdenNetworkGovernance/abis/DistributorGovernance.json';
|
import distributorGovernanceAbi from '../test/subgraph/eden/EdenNetworkGovernance/abis/DistributorGovernance.json';
|
||||||
|
import { getDummyEventData } from '../test/utils';
|
||||||
|
|
||||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||||
|
|
||||||
describe('eden wasm loader tests', () => {
|
describe('eden wasm loader tests', () => {
|
||||||
|
const eventData = getDummyEventData();
|
||||||
|
|
||||||
describe('EdenNetwork wasm', () => {
|
describe('EdenNetwork wasm', () => {
|
||||||
let exports: any;
|
let exports: any;
|
||||||
|
|
||||||
@ -44,10 +47,10 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy SlotClaimedEvent params.
|
// Create dummy SlotClaimedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'slot',
|
name: 'slot',
|
||||||
kind: 'i32',
|
kind: 'uint8',
|
||||||
value: 0
|
value: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -62,28 +65,28 @@ describe('eden wasm loader tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'newBidAmount',
|
name: 'newBidAmount',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint128',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'oldBidAmount',
|
name: 'oldBidAmount',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint128',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'taxNumerator',
|
name: 'taxNumerator',
|
||||||
kind: 'i32',
|
kind: 'uint16',
|
||||||
value: 1
|
value: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'taxDenominator',
|
name: 'taxDenominator',
|
||||||
kind: 'i32',
|
kind: 'uint16',
|
||||||
value: 1
|
value: 1
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy SlotClaimedEvent to be passed to handler.
|
// 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);
|
await slotClaimed(slotClaimedEvent);
|
||||||
});
|
});
|
||||||
@ -94,10 +97,10 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy SlotDelegateUpdatedEvent params.
|
// Create dummy SlotDelegateUpdatedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'slot',
|
name: 'slot',
|
||||||
kind: 'i32',
|
kind: 'uint8',
|
||||||
value: 0
|
value: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -118,7 +121,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy SlotDelegateUpdatedEvent to be passed to handler.
|
// 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);
|
await slotDelegateUpdated(slotClaimedEvent);
|
||||||
});
|
});
|
||||||
@ -129,7 +132,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy StakeEvent params.
|
// Create dummy StakeEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'staker',
|
name: 'staker',
|
||||||
kind: 'address',
|
kind: 'address',
|
||||||
@ -137,13 +140,13 @@ describe('eden wasm loader tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'stakeAmount',
|
name: 'stakeAmount',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy StakeEvent to be passed to handler.
|
// 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);
|
await stake(stakeEvent);
|
||||||
});
|
});
|
||||||
@ -154,7 +157,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy UnstakeEvent params.
|
// Create dummy UnstakeEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'staker',
|
name: 'staker',
|
||||||
kind: 'address',
|
kind: 'address',
|
||||||
@ -162,13 +165,13 @@ describe('eden wasm loader tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'unstakedAmount',
|
name: 'unstakedAmount',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uin256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy UnstakeEvent to be passed to handler.
|
// 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);
|
await unstake(unstakeEvent);
|
||||||
});
|
});
|
||||||
@ -203,15 +206,15 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy ClaimedEvent params.
|
// Create dummy ClaimedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'index',
|
name: 'index',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'totalEarned',
|
name: 'totalEarned',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -221,13 +224,13 @@ describe('eden wasm loader tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'claimed',
|
name: 'claimed',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy ClaimedEvent to be passed to handler.
|
// 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);
|
await claimed(claimedEvent);
|
||||||
});
|
});
|
||||||
@ -238,7 +241,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy SlashedEvent params.
|
// Create dummy SlashedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'account',
|
name: 'account',
|
||||||
kind: 'address',
|
kind: 'address',
|
||||||
@ -246,13 +249,13 @@ describe('eden wasm loader tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'slashed',
|
name: 'slashed',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy SlashedEvent to be passed to handler.
|
// 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);
|
await slashed(slashedEvent);
|
||||||
});
|
});
|
||||||
@ -263,15 +266,15 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy MerkleRootUpdatedEvent params.
|
// Create dummy MerkleRootUpdatedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'merkleRoot',
|
name: 'merkleRoot',
|
||||||
kind: 'bytes',
|
kind: 'bytes32',
|
||||||
value: ethers.utils.hexlify(ethers.utils.randomBytes(32))
|
value: ethers.utils.hexlify(ethers.utils.randomBytes(32))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'distributionNumber',
|
name: 'distributionNumber',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -282,7 +285,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy MerkleRootUpdatedEvent to be passed to handler.
|
// 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);
|
await merkleRootUpdated(merkleRootUpdatedEvent);
|
||||||
});
|
});
|
||||||
@ -293,7 +296,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy AccountUpdatedEvent params.
|
// Create dummy AccountUpdatedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'account',
|
name: 'account',
|
||||||
kind: 'address',
|
kind: 'address',
|
||||||
@ -301,18 +304,18 @@ describe('eden wasm loader tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'totalClaimed',
|
name: 'totalClaimed',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'totalSlashed',
|
name: 'totalSlashed',
|
||||||
kind: 'unsignedBigInt',
|
kind: 'uint256',
|
||||||
value: BigInt(1)
|
value: BigInt(1)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy AccountUpdatedEvent to be passed to handler.
|
// 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);
|
await accountUpdated(accountUpdatedEvent);
|
||||||
});
|
});
|
||||||
@ -347,7 +350,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy BlockProducerAddedEvent params.
|
// Create dummy BlockProducerAddedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'produces',
|
name: 'produces',
|
||||||
kind: 'address',
|
kind: 'address',
|
||||||
@ -356,7 +359,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy BlockProducerAddedEvent to be passed to handler.
|
// 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);
|
await blockProducerAdded(blockProducerAddedEvent);
|
||||||
});
|
});
|
||||||
@ -367,7 +370,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy BlockProducerRemovedEvent params.
|
// Create dummy BlockProducerRemovedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'producer',
|
name: 'producer',
|
||||||
kind: 'address',
|
kind: 'address',
|
||||||
@ -376,7 +379,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy BlockProducerRemovedEvent to be passed to handler.
|
// 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);
|
await blockProducerRemoved(blockProducerRemovedEvent);
|
||||||
});
|
});
|
||||||
@ -387,7 +390,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
// Create dummy BlockProducerRewardCollectorChangedEvent params.
|
// Create dummy BlockProducerRewardCollectorChangedEvent params.
|
||||||
const eventParamsData = [
|
eventData.eventParams = [
|
||||||
{
|
{
|
||||||
name: 'producer',
|
name: 'producer',
|
||||||
kind: 'address',
|
kind: 'address',
|
||||||
@ -406,7 +409,7 @@ describe('eden wasm loader tests', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Create dummy BlockProducerRewardCollectorChangedEvent to be passed to handler.
|
// 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);
|
await blockProducerRewardCollectorChanged(blockProducerRewardCollectorChangedEvent);
|
||||||
});
|
});
|
||||||
@ -416,8 +419,10 @@ describe('eden wasm loader tests', () => {
|
|||||||
rewardScheduleChanged
|
rewardScheduleChanged
|
||||||
} = exports;
|
} = exports;
|
||||||
|
|
||||||
|
eventData.eventParams = [];
|
||||||
|
|
||||||
// Create dummy RewardScheduleChangedEvent to be passed to handler.
|
// Create dummy RewardScheduleChangedEvent to be passed to handler.
|
||||||
const rewardScheduleChangedEvent = await createEvent(exports, contractAddress, []);
|
const rewardScheduleChangedEvent = await createEvent(exports, contractAddress, eventData);
|
||||||
|
|
||||||
await rewardScheduleChanged(rewardScheduleChangedEvent);
|
await rewardScheduleChanged(rewardScheduleChangedEvent);
|
||||||
});
|
});
|
||||||
|
@ -14,128 +14,39 @@ interface EventParam {
|
|||||||
kind: string;
|
kind: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
interface Block {
|
||||||
* Method to create ethereum event.
|
hash: string;
|
||||||
* @param exports
|
number: number;
|
||||||
* @param contractAddress
|
timestamp: number;
|
||||||
* @param eventParamsData
|
parentHash: string;
|
||||||
* @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;
|
|
||||||
|
|
||||||
// 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
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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': {
|
interface Transaction {
|
||||||
ethValue = await ethereum.Value.fromString(await __newString(value));
|
hash: string;
|
||||||
break;
|
index: number;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'i32': {
|
export interface EventData {
|
||||||
ethValue = await ethereum.Value.fromI32(value);
|
block: Block;
|
||||||
break;
|
tx: Transaction;
|
||||||
|
eventParams: EventParam[];
|
||||||
|
eventIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to get value from graph-ts ethereum.Value wasm instance.
|
* Method to get value from graph-ts ethereum.Value wasm instance.
|
||||||
* @param exports
|
* @param instanceExports
|
||||||
* @param value
|
* @param value
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const fromEthereumValue = async (exports: any, value: any): Promise<any> => {
|
export const fromEthereumValue = async (instanceExports: any, value: any): Promise<any> => {
|
||||||
const {
|
const {
|
||||||
__getString,
|
__getString,
|
||||||
BigInt,
|
BigInt,
|
||||||
Address
|
Address
|
||||||
} = exports;
|
} = instanceExports;
|
||||||
|
|
||||||
const kind = await value.kind;
|
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.
|
* Method to get ethereum value for passing to wasm instance.
|
||||||
* @param exports
|
* @param instanceExports
|
||||||
* @param value
|
* @param value
|
||||||
* @param type
|
* @param type
|
||||||
* @returns
|
* @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 {
|
const {
|
||||||
__newString,
|
__newString,
|
||||||
ByteArray,
|
ByteArray,
|
||||||
@ -186,7 +97,7 @@ export const toEthereumValue = async (exports: any, value: any, type: string): P
|
|||||||
Address,
|
Address,
|
||||||
ethereum,
|
ethereum,
|
||||||
BigInt
|
BigInt
|
||||||
} = exports;
|
} = instanceExports;
|
||||||
|
|
||||||
// For boolean type.
|
// For boolean type.
|
||||||
if (type === 'bool') {
|
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));
|
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> => {
|
export const getSubgraphConfig = async (subgraphPath: string): Promise<any> => {
|
||||||
const configFilePath = path.resolve(path.join(subgraphPath, 'subgraph.yaml'));
|
const configFilePath = path.resolve(path.join(subgraphPath, 'subgraph.yaml'));
|
||||||
const fileExists = await fs.pathExists(configFilePath);
|
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}`);
|
throw new Error(`Config file not found: ${configFilePath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(configFilePath);
|
|
||||||
const config = yaml.load(await fs.readFile(configFilePath, 'utf8'));
|
const config = yaml.load(await fs.readFile(configFilePath, 'utf8'));
|
||||||
log('config', JSON.stringify(config, null, 2));
|
log('config', JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
|
@ -6,18 +6,24 @@ import 'reflect-metadata';
|
|||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
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 { ResultObject } from '@vulcanize/assemblyscript/lib/loader';
|
||||||
|
|
||||||
|
import { createEvent, getSubgraphConfig } from './utils';
|
||||||
|
import { instantiate } from './loader';
|
||||||
|
|
||||||
const log = debug('vulcanize:graph-watcher');
|
const log = debug('vulcanize:graph-watcher');
|
||||||
|
|
||||||
|
interface DataSource {
|
||||||
|
instance: ResultObject & { exports: any },
|
||||||
|
contractInterface: utils.Interface
|
||||||
|
}
|
||||||
|
|
||||||
export class GraphWatcher {
|
export class GraphWatcher {
|
||||||
_subgraphPath: string;
|
_subgraphPath: string;
|
||||||
_dataSources: any[] = []
|
_dataSources: any[] = [];
|
||||||
_instanceMap: { [key: string]: ResultObject & { exports: any } } = {};
|
_dataSourceMap: { [key: string]: DataSource } = {};
|
||||||
|
|
||||||
constructor (subgraphPath: string) {
|
constructor (subgraphPath: string) {
|
||||||
this._subgraphPath = subgraphPath;
|
this._subgraphPath = subgraphPath;
|
||||||
@ -27,33 +33,57 @@ export class GraphWatcher {
|
|||||||
const { dataSources } = await getSubgraphConfig(this._subgraphPath);
|
const { dataSources } = await getSubgraphConfig(this._subgraphPath);
|
||||||
this._dataSources = dataSources;
|
this._dataSources = dataSources;
|
||||||
|
|
||||||
this._instanceMap = this._dataSources.reduce(async (acc: { [key: string]: ResultObject & { exports: any } }, dataSource: any) => {
|
// Create wasm instance and contract interface for each dataSource in subgraph yaml.
|
||||||
const { source: { address }, mapping } = dataSource;
|
const dataPromises = this._dataSources.map(async (dataSource: any) => {
|
||||||
|
const { source: { address, abi }, mapping } = dataSource;
|
||||||
const { abis, file } = mapping;
|
const { abis, file } = mapping;
|
||||||
|
|
||||||
const data = {
|
const abisMap = abis.reduce((acc: {[key: string]: ContractInterface}, abi: any) => {
|
||||||
abis: abis.reduce((acc: {[key: string]: ContractInterface}, abi: any) => {
|
|
||||||
const { name, file } = abi;
|
const { name, file } = abi;
|
||||||
const abiFilePath = path.join(this._subgraphPath, file);
|
const abiFilePath = path.join(this._subgraphPath, file);
|
||||||
acc[name] = JSON.parse(fs.readFileSync(abiFilePath).toString());
|
acc[name] = JSON.parse(fs.readFileSync(abiFilePath).toString());
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
}, {});
|
||||||
|
|
||||||
|
const contractInterface = new utils.Interface(abisMap[abi]);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
abis: abisMap,
|
||||||
dataSource: {
|
dataSource: {
|
||||||
address
|
address
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const filePath = path.join(this._subgraphPath, file);
|
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;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEvent (eventData: any) {
|
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);
|
const dataSource = this._dataSources.find(dataSource => dataSource.source.address === contract);
|
||||||
|
|
||||||
if (!dataSource) {
|
if (!dataSource) {
|
||||||
@ -61,16 +91,36 @@ export class GraphWatcher {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Call instance methods based on event signature.
|
// Get event handler based on event signature.
|
||||||
// value should contain event signature.
|
const eventHandler = dataSource.mapping.eventHandlers.find((eventHandler: any) => eventHandler.event === eventSignature);
|
||||||
|
|
||||||
const [{ handler }] = dataSource.mapping.eventHandlers;
|
if (!eventHandler) {
|
||||||
const { exports } = this._instanceMap[contract];
|
log(`No handler configured in subgraph for event ${eventSignature}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create ethereum event to be passed to handler.
|
const { instance: { exports }, contractInterface } = this._dataSourceMap[contract];
|
||||||
// TODO: Create ethereum event to be passed to handler.
|
|
||||||
// const ethereumEvent = await createEvent(exports, address, event);
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,27 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": false,
|
"indexed": false,
|
||||||
"internalType": "uint256",
|
"internalType": "uint8",
|
||||||
"name": "param2",
|
"name": "param2",
|
||||||
"type": "uint256"
|
"type": "uint8"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"name": "Test",
|
"name": "Test",
|
||||||
"type": "event"
|
"type": "event"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "emitEvent",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"internalType": "bool",
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"inputs": [],
|
"inputs": [],
|
||||||
"name": "getMethod",
|
"name": "getMethod",
|
||||||
|
@ -27,8 +27,8 @@ export class Test__Params {
|
|||||||
return this._event.parameters[0].value.toString();
|
return this._event.parameters[0].value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
get param2(): BigInt {
|
get param2(): i32 {
|
||||||
return this._event.parameters[1].value.toBigInt();
|
return this._event.parameters[1].value.toI32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +37,21 @@ export class Example1 extends ethereum.SmartContract {
|
|||||||
return new Example1("Example1", address);
|
return new Example1("Example1", address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitEvent(): boolean {
|
||||||
|
let result = super.call("emitEvent", "emitEvent():(bool)", []);
|
||||||
|
|
||||||
|
return result[0].toBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
try_emitEvent(): ethereum.CallResult<boolean> {
|
||||||
|
let result = super.tryCall("emitEvent", "emitEvent():(bool)", []);
|
||||||
|
if (result.reverted) {
|
||||||
|
return new ethereum.CallResult();
|
||||||
|
}
|
||||||
|
let value = result.value;
|
||||||
|
return ethereum.CallResult.fromValue(value[0].toBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
getMethod(): string {
|
getMethod(): string {
|
||||||
let result = super.call("getMethod", "getMethod():(string)", []);
|
let result = super.call("getMethod", "getMethod():(string)", []);
|
||||||
|
|
||||||
@ -52,3 +67,33 @@ export class Example1 extends ethereum.SmartContract {
|
|||||||
return ethereum.CallResult.fromValue(value[0].toString());
|
return ethereum.CallResult.fromValue(value[0].toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class EmitEventCall extends ethereum.Call {
|
||||||
|
get inputs(): EmitEventCall__Inputs {
|
||||||
|
return new EmitEventCall__Inputs(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get outputs(): EmitEventCall__Outputs {
|
||||||
|
return new EmitEventCall__Outputs(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EmitEventCall__Inputs {
|
||||||
|
_call: EmitEventCall;
|
||||||
|
|
||||||
|
constructor(call: EmitEventCall) {
|
||||||
|
this._call = call;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EmitEventCall__Outputs {
|
||||||
|
_call: EmitEventCall;
|
||||||
|
|
||||||
|
constructor(call: EmitEventCall) {
|
||||||
|
this._call = call;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value0(): boolean {
|
||||||
|
return this._call.outputValues[0].value.toBoolean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,21 +6,19 @@ import {
|
|||||||
BigInt,
|
BigInt,
|
||||||
ethereum,
|
ethereum,
|
||||||
Address,
|
Address,
|
||||||
Bytes
|
ByteArray,
|
||||||
|
Bytes,
|
||||||
|
Entity
|
||||||
} from '@graphprotocol/graph-ts';
|
} from '@graphprotocol/graph-ts';
|
||||||
|
|
||||||
import {
|
|
||||||
Test
|
|
||||||
} from './Example1/Example1';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
BigDecimal,
|
BigDecimal,
|
||||||
BigInt,
|
BigInt,
|
||||||
|
|
||||||
ethereum,
|
ethereum,
|
||||||
|
Entity,
|
||||||
|
|
||||||
Address,
|
Address,
|
||||||
Bytes,
|
ByteArray,
|
||||||
|
Bytes
|
||||||
Test
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export class ExampleEntity extends Entity {
|
|||||||
|
|
||||||
this.set("count", Value.fromBigInt(BigInt.zero()));
|
this.set("count", Value.fromBigInt(BigInt.zero()));
|
||||||
this.set("param1", Value.fromString(""));
|
this.set("param1", Value.fromString(""));
|
||||||
this.set("param2", Value.fromBigInt(BigInt.zero()));
|
this.set("param2", Value.fromI32(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
save(): void {
|
save(): void {
|
||||||
@ -66,12 +66,12 @@ export class ExampleEntity extends Entity {
|
|||||||
this.set("param1", Value.fromString(value));
|
this.set("param1", Value.fromString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get param2(): BigInt {
|
get param2(): i32 {
|
||||||
let value = this.get("param2");
|
let value = this.get("param2");
|
||||||
return value!.toBigInt();
|
return value!.toI32();
|
||||||
}
|
}
|
||||||
|
|
||||||
set param2(value: BigInt) {
|
set param2(value: i32) {
|
||||||
this.set("param2", Value.fromBigInt(value));
|
this.set("param2", Value.fromI32(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 example1"
|
"deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 example1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@graphprotocol/graph-cli": "ssh://git@github.com:vulcanize/graph-cli.git#graph-watcher-v0.22.1",
|
"@graphprotocol/graph-cli": "ssh://git@github.com:vulcanize/graph-cli.git#ng-add-exports",
|
||||||
"@graphprotocol/graph-ts": "0.22.0"
|
"@graphprotocol/graph-ts": "^0.22.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,5 @@ type ExampleEntity @entity {
|
|||||||
id: ID!
|
id: ID!
|
||||||
count: BigInt!
|
count: BigInt!
|
||||||
param1: String! # string
|
param1: String! # string
|
||||||
param2: BigInt! # uint256
|
param2: Int! # uint8
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ export function handleTest (event: Test): void {
|
|||||||
log.debug('event.address: {}', [event.address.toHexString()]);
|
log.debug('event.address: {}', [event.address.toHexString()]);
|
||||||
log.debug('event.params.param1: {}', [event.params.param1]);
|
log.debug('event.params.param1: {}', [event.params.param1]);
|
||||||
log.debug('event.params.param2: {}', [event.params.param2.toString()]);
|
log.debug('event.params.param2: {}', [event.params.param2.toString()]);
|
||||||
|
log.debug('event.block.hash: {}', [event.block.hash.toHexString()]);
|
||||||
|
|
||||||
// Entities can be loaded from the store using a string ID; this ID
|
// Entities can be loaded from the store using a string ID; this ID
|
||||||
// needs to be unique across all entities of the same type
|
// needs to be unique across all entities of the same type
|
||||||
|
@ -6,7 +6,7 @@ dataSources:
|
|||||||
name: Example1
|
name: Example1
|
||||||
network: mainnet
|
network: mainnet
|
||||||
source:
|
source:
|
||||||
address: "0x3ebd8bb51fF52aDAc490117B31F5F137BB125A9D"
|
address: "0x4Ab7aE18973491Df21d6103dfA55170fdB2CCC98"
|
||||||
abi: Example1
|
abi: Example1
|
||||||
mapping:
|
mapping:
|
||||||
kind: ethereum/events
|
kind: ethereum/events
|
||||||
@ -18,6 +18,6 @@ dataSources:
|
|||||||
- name: Example1
|
- name: Example1
|
||||||
file: ./abis/Example1.json
|
file: ./abis/Example1.json
|
||||||
eventHandlers:
|
eventHandlers:
|
||||||
- event: Test(string,uint256)
|
- event: Test(string,uint8)
|
||||||
handler: handleTest
|
handler: handleTest
|
||||||
file: ./src/mapping.ts
|
file: ./src/mapping.ts
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
chalk "^2.0.0"
|
chalk "^2.0.0"
|
||||||
js-tokens "^4.0.0"
|
js-tokens "^4.0.0"
|
||||||
|
|
||||||
"@graphprotocol/graph-cli@ssh://git@github.com:vulcanize/graph-cli.git#graph-watcher-v0.22.1":
|
"@graphprotocol/graph-cli@ssh://git@github.com:vulcanize/graph-cli.git#ng-add-exports":
|
||||||
version "0.22.1"
|
version "0.22.1"
|
||||||
resolved "ssh://git@github.com:vulcanize/graph-cli.git#4241570e91578a0128deccc518d52eca00bd587c"
|
resolved "ssh://git@github.com:vulcanize/graph-cli.git#d70ae40a7517a666f550b25a0fa4ae0d3758e7d0"
|
||||||
dependencies:
|
dependencies:
|
||||||
assemblyscript "0.19.10"
|
assemblyscript "0.19.10"
|
||||||
binary-install-raw "0.0.13"
|
binary-install-raw "0.0.13"
|
||||||
@ -51,10 +51,10 @@
|
|||||||
which "2.0.2"
|
which "2.0.2"
|
||||||
yaml "^1.5.1"
|
yaml "^1.5.1"
|
||||||
|
|
||||||
"@graphprotocol/graph-ts@0.22.0":
|
"@graphprotocol/graph-ts@^0.22.1":
|
||||||
version "0.22.0"
|
version "0.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/@graphprotocol/graph-ts/-/graph-ts-0.22.0.tgz#5280513d1c8a162077f82ce7f9a492bb5783d6f4"
|
resolved "https://registry.yarnpkg.com/@graphprotocol/graph-ts/-/graph-ts-0.22.1.tgz#3189b2495b33497280f617316cce68074d48e236"
|
||||||
integrity sha512-kJIBL73xBxj0+NJdRABukAtcrc3Mb8jt31s0tCLPe6c57rQXEf7KR9oYrFdzE1ZsJCP6j+MX+A2+sj1Pj3aJtQ==
|
integrity sha512-T5xrHN0tHJwd7ZnSTLhk5hAL3rCIp6rJ40kBCrETnv1mfK9hVyoojJK6VtBQXTbLsYtKe4SYjjD0cdOsAR9QiA==
|
||||||
dependencies:
|
dependencies:
|
||||||
assemblyscript "0.19.10"
|
assemblyscript "0.19.10"
|
||||||
|
|
||||||
|
31
packages/graph-node/test/utils/index.ts
Normal file
31
packages/graph-node/test/utils/index.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 Vulcanize, Inc.
|
||||||
|
//
|
||||||
|
|
||||||
|
import { EventData } from '../../src/utils';
|
||||||
|
|
||||||
|
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||||
|
export const ZERO_HASH = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
||||||
|
|
||||||
|
export const getDummyEventData = (): EventData => {
|
||||||
|
const block = {
|
||||||
|
hash: ZERO_HASH,
|
||||||
|
number: 0,
|
||||||
|
timestamp: 0,
|
||||||
|
parentHash: ZERO_HASH
|
||||||
|
};
|
||||||
|
|
||||||
|
const tx = {
|
||||||
|
hash: ZERO_HASH,
|
||||||
|
index: 0,
|
||||||
|
from: ZERO_ADDRESS,
|
||||||
|
to: ZERO_ADDRESS
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
block,
|
||||||
|
tx,
|
||||||
|
eventParams: [],
|
||||||
|
eventIndex: 0
|
||||||
|
};
|
||||||
|
};
|
@ -19,6 +19,7 @@
|
|||||||
gqlApiEndpoint = "http://127.0.0.1:8082/graphql"
|
gqlApiEndpoint = "http://127.0.0.1:8082/graphql"
|
||||||
gqlPostgraphileEndpoint = "http://127.0.0.1:5000/graphql"
|
gqlPostgraphileEndpoint = "http://127.0.0.1:5000/graphql"
|
||||||
rpcProviderEndpoint = "http://127.0.0.1:8081"
|
rpcProviderEndpoint = "http://127.0.0.1:8081"
|
||||||
|
blockDelayInMilliSecs = 2000
|
||||||
|
|
||||||
[upstream.cache]
|
[upstream.cache]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
|
@ -12,7 +12,8 @@ import {
|
|||||||
EventWatcher as BaseEventWatcher,
|
EventWatcher as BaseEventWatcher,
|
||||||
QUEUE_BLOCK_PROCESSING,
|
QUEUE_BLOCK_PROCESSING,
|
||||||
QUEUE_EVENT_PROCESSING,
|
QUEUE_EVENT_PROCESSING,
|
||||||
UNKNOWN_EVENT_NAME
|
UNKNOWN_EVENT_NAME,
|
||||||
|
UpstreamConfig
|
||||||
} from '@vulcanize/util';
|
} from '@vulcanize/util';
|
||||||
|
|
||||||
import { Indexer } from './indexer';
|
import { Indexer } from './indexer';
|
||||||
@ -30,7 +31,7 @@ export class EventWatcher {
|
|||||||
_pubsub: PubSub
|
_pubsub: PubSub
|
||||||
_jobQueue: JobQueue
|
_jobQueue: JobQueue
|
||||||
|
|
||||||
constructor (ethClient: EthClient, indexer: Indexer, pubsub: PubSub, jobQueue: JobQueue) {
|
constructor (upstreamConfig: UpstreamConfig, ethClient: EthClient, postgraphileClient: EthClient, indexer: Indexer, pubsub: PubSub, jobQueue: JobQueue) {
|
||||||
assert(ethClient);
|
assert(ethClient);
|
||||||
assert(indexer);
|
assert(indexer);
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ export class EventWatcher {
|
|||||||
this._indexer = indexer;
|
this._indexer = indexer;
|
||||||
this._pubsub = pubsub;
|
this._pubsub = pubsub;
|
||||||
this._jobQueue = jobQueue;
|
this._jobQueue = jobQueue;
|
||||||
this._baseEventWatcher = new BaseEventWatcher(this._ethClient, this._indexer, this._pubsub, this._jobQueue);
|
this._baseEventWatcher = new BaseEventWatcher(upstreamConfig, this._ethClient, postgraphileClient, this._indexer, this._pubsub, this._jobQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEventIterator (): AsyncIterator<any> {
|
getEventIterator (): AsyncIterator<any> {
|
||||||
@ -52,22 +53,15 @@ export class EventWatcher {
|
|||||||
async start (): Promise<void> {
|
async start (): Promise<void> {
|
||||||
assert(!this._subscription, 'subscription already started');
|
assert(!this._subscription, 'subscription already started');
|
||||||
|
|
||||||
await this.watchBlocksAtChainHead();
|
|
||||||
await this.initBlockProcessingOnCompleteHandler();
|
await this.initBlockProcessingOnCompleteHandler();
|
||||||
await this.initEventProcessingOnCompleteHandler();
|
await this.initEventProcessingOnCompleteHandler();
|
||||||
|
this._baseEventWatcher.startBlockProcessing();
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop (): Promise<void> {
|
async stop (): Promise<void> {
|
||||||
this._baseEventWatcher.stop();
|
this._baseEventWatcher.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchBlocksAtChainHead (): Promise<void> {
|
|
||||||
log('Started watching upstream blocks...');
|
|
||||||
this._subscription = await this._ethClient.watchBlocks(async (value) => {
|
|
||||||
await this._baseEventWatcher.blocksHandler(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async initBlockProcessingOnCompleteHandler (): Promise<void> {
|
async initBlockProcessingOnCompleteHandler (): Promise<void> {
|
||||||
this._jobQueue.onComplete(QUEUE_BLOCK_PROCESSING, async (job) => {
|
this._jobQueue.onComplete(QUEUE_BLOCK_PROCESSING, async (job) => {
|
||||||
const { id, data: { failed } } = job;
|
const { id, data: { failed } } = job;
|
||||||
|
@ -54,13 +54,13 @@ export const main = async (): Promise<any> => {
|
|||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
assert(upstream, 'Missing upstream config');
|
assert(upstream, 'Missing upstream config');
|
||||||
const { ethServer: { gqlPostgraphileEndpoint, rpcProviderEndpoint }, cache: cacheConfig } = upstream;
|
const { ethServer: { gqlApiEndpoint, gqlPostgraphileEndpoint, rpcProviderEndpoint, blockDelayInMilliSecs }, cache: cacheConfig } = upstream;
|
||||||
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
assert(gqlPostgraphileEndpoint, 'Missing upstream ethServer.gqlPostgraphileEndpoint');
|
||||||
|
|
||||||
const cache = await getCache(cacheConfig);
|
const cache = await getCache(cacheConfig);
|
||||||
|
|
||||||
const ethClient = new EthClient({
|
const ethClient = new EthClient({
|
||||||
gqlEndpoint: gqlPostgraphileEndpoint,
|
gqlEndpoint: gqlApiEndpoint,
|
||||||
gqlSubscriptionEndpoint: gqlPostgraphileEndpoint,
|
gqlSubscriptionEndpoint: gqlPostgraphileEndpoint,
|
||||||
cache
|
cache
|
||||||
});
|
});
|
||||||
@ -83,11 +83,11 @@ export const main = async (): Promise<any> => {
|
|||||||
const jobQueue = new JobQueue({ dbConnectionString, maxCompletionLag: maxCompletionLagInSecs });
|
const jobQueue = new JobQueue({ dbConnectionString, maxCompletionLag: maxCompletionLagInSecs });
|
||||||
await jobQueue.start();
|
await jobQueue.start();
|
||||||
|
|
||||||
const eventWatcher = new EventWatcher(ethClient, indexer, pubsub, jobQueue);
|
const eventWatcher = new EventWatcher(upstream, ethClient, postgraphileClient, indexer, pubsub, jobQueue);
|
||||||
|
|
||||||
assert(jobQueueConfig, 'Missing job queue config');
|
assert(jobQueueConfig, 'Missing job queue config');
|
||||||
|
|
||||||
await fillBlocks(jobQueue, indexer, ethClient, eventWatcher, argv);
|
await fillBlocks(jobQueue, indexer, postgraphileClient, eventWatcher, blockDelayInMilliSecs, argv);
|
||||||
};
|
};
|
||||||
|
|
||||||
main().catch(err => {
|
main().catch(err => {
|
||||||
|
@ -44,6 +44,7 @@ export type ResultEvent = {
|
|||||||
contract: string;
|
contract: string;
|
||||||
|
|
||||||
eventIndex: number;
|
eventIndex: number;
|
||||||
|
eventSignature: string;
|
||||||
event: any;
|
event: any;
|
||||||
|
|
||||||
proof: string;
|
proof: string;
|
||||||
@ -67,8 +68,8 @@ export class Indexer {
|
|||||||
|
|
||||||
this._db = db;
|
this._db = db;
|
||||||
this._ethClient = ethClient;
|
this._ethClient = ethClient;
|
||||||
this._ethProvider = ethProvider;
|
|
||||||
this._postgraphileClient = postgraphileClient;
|
this._postgraphileClient = postgraphileClient;
|
||||||
|
this._ethProvider = ethProvider;
|
||||||
this._graphWatcher = graphWatcher;
|
this._graphWatcher = graphWatcher;
|
||||||
this._baseIndexer = new BaseIndexer(this._db, this._ethClient, this._ethProvider);
|
this._baseIndexer = new BaseIndexer(this._db, this._ethClient, this._ethProvider);
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ export class Indexer {
|
|||||||
getResultEvent (event: Event): ResultEvent {
|
getResultEvent (event: Event): ResultEvent {
|
||||||
const block = event.block;
|
const block = event.block;
|
||||||
const eventFields = JSONbig.parse(event.eventInfo);
|
const eventFields = JSONbig.parse(event.eventInfo);
|
||||||
const { tx } = JSON.parse(event.extraInfo);
|
const { tx, eventSignature } = JSON.parse(event.extraInfo);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
block: {
|
block: {
|
||||||
@ -106,6 +107,7 @@ export class Indexer {
|
|||||||
contract: event.contract,
|
contract: event.contract,
|
||||||
|
|
||||||
eventIndex: event.index,
|
eventIndex: event.index,
|
||||||
|
eventSignature,
|
||||||
event: {
|
event: {
|
||||||
__typename: `${event.eventName}Event`,
|
__typename: `${event.eventName}Event`,
|
||||||
...eventFields
|
...eventFields
|
||||||
@ -168,7 +170,8 @@ export class Indexer {
|
|||||||
async triggerIndexingOnEvent (event: Event): Promise<void> {
|
async triggerIndexingOnEvent (event: Event): Promise<void> {
|
||||||
const resultEvent = this.getResultEvent(event);
|
const resultEvent = this.getResultEvent(event);
|
||||||
|
|
||||||
this._graphWatcher.handleEvent(resultEvent);
|
// Call subgraph handler for event.
|
||||||
|
await this._graphWatcher.handleEvent(resultEvent);
|
||||||
|
|
||||||
// Call custom hook function for indexing on event.
|
// Call custom hook function for indexing on event.
|
||||||
await handleEvent(this, resultEvent);
|
await handleEvent(this, resultEvent);
|
||||||
@ -199,7 +202,11 @@ export class Indexer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { eventName, eventInfo };
|
return {
|
||||||
|
eventName,
|
||||||
|
eventInfo,
|
||||||
|
eventSignature: logDescription.signature
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchContract (address: string, startingBlock: number): Promise<boolean> {
|
async watchContract (address: string, startingBlock: number): Promise<boolean> {
|
||||||
@ -326,7 +333,7 @@ export class Indexer {
|
|||||||
let eventName = UNKNOWN_EVENT_NAME;
|
let eventName = UNKNOWN_EVENT_NAME;
|
||||||
let eventInfo = {};
|
let eventInfo = {};
|
||||||
const tx = transactionMap[txHash];
|
const tx = transactionMap[txHash];
|
||||||
const extraInfo = { topics, data, tx };
|
const extraInfo: { [key: string]: any } = { topics, data, tx };
|
||||||
|
|
||||||
const contract = ethers.utils.getAddress(address);
|
const contract = ethers.utils.getAddress(address);
|
||||||
const watchedContract = await this.isWatchedContract(contract);
|
const watchedContract = await this.isWatchedContract(contract);
|
||||||
@ -335,6 +342,7 @@ export class Indexer {
|
|||||||
const eventDetails = this.parseEventNameAndArgs(watchedContract.kind, logObj);
|
const eventDetails = this.parseEventNameAndArgs(watchedContract.kind, logObj);
|
||||||
eventName = eventDetails.eventName;
|
eventName = eventDetails.eventName;
|
||||||
eventInfo = eventDetails.eventInfo;
|
eventInfo = eventDetails.eventInfo;
|
||||||
|
extraInfo.eventSignature = eventDetails.eventSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbEvents.push({
|
dbEvents.push({
|
||||||
|
@ -34,9 +34,9 @@ export class JobRunner {
|
|||||||
_jobQueueConfig: JobQueueConfig
|
_jobQueueConfig: JobQueueConfig
|
||||||
|
|
||||||
constructor (jobQueueConfig: JobQueueConfig, indexer: Indexer, jobQueue: JobQueue) {
|
constructor (jobQueueConfig: JobQueueConfig, indexer: Indexer, jobQueue: JobQueue) {
|
||||||
|
this._jobQueueConfig = jobQueueConfig;
|
||||||
this._indexer = indexer;
|
this._indexer = indexer;
|
||||||
this._jobQueue = jobQueue;
|
this._jobQueue = jobQueue;
|
||||||
this._jobQueueConfig = jobQueueConfig;
|
|
||||||
this._baseJobRunner = new BaseJobRunner(this._jobQueueConfig, this._indexer, this._jobQueue);
|
this._baseJobRunner = new BaseJobRunner(this._jobQueueConfig, this._indexer, this._jobQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ type ResultEvent {
|
|||||||
tx: Transaction!
|
tx: Transaction!
|
||||||
contract: String!
|
contract: String!
|
||||||
eventIndex: Int!
|
eventIndex: Int!
|
||||||
|
eventSignature: String!
|
||||||
event: Event!
|
event: Event!
|
||||||
proof: Proof
|
proof: Proof
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ export const main = async (): Promise<any> => {
|
|||||||
|
|
||||||
const jobQueue = new JobQueue({ dbConnectionString, maxCompletionLag: maxCompletionLagInSecs });
|
const jobQueue = new JobQueue({ dbConnectionString, maxCompletionLag: maxCompletionLagInSecs });
|
||||||
|
|
||||||
const eventWatcher = new EventWatcher(ethClient, indexer, pubsub, jobQueue);
|
const eventWatcher = new EventWatcher(upstream, ethClient, postgraphileClient, indexer, pubsub, jobQueue);
|
||||||
|
|
||||||
if (watcherKind === KIND_ACTIVE) {
|
if (watcherKind === KIND_ACTIVE) {
|
||||||
await jobQueue.start();
|
await jobQueue.start();
|
||||||
|
Loading…
Reference in New Issue
Block a user