Test case for EdenNetwork event handler (#31)

* Test case for calling EdenNetwork event handler

* Test cases for calling EdenNetwork event handlers

* Test cases for calling event handlers in EdenNetworkDistribution

* Test cases for calling event handlers in EdenNetworkGovernance

* Perform eth-calls according to contract name in param

* Pass function params in eth-call host API

* Return value from eth-call to wasm

* Add instructions for running tests

* Install assemblyscript from github packages

* Add instrcutions for installing private github packages

* Use vulcanize assemblyscript package
This commit is contained in:
nikugogoi 2021-10-27 14:43:48 +05:30 committed by nabarun
parent 1ce07bbb6e
commit 1c15c1eedb
22 changed files with 808 additions and 166 deletions

View File

@ -18,6 +18,9 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: yarn
# https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#use-private-packages
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Linter check
run: yarn lint
build:

View File

@ -15,6 +15,9 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: yarn
# https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#use-private-packages
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Linter check
run: yarn lint
build:

View File

@ -2,6 +2,16 @@
## Setup
There are packages used from github so we need to follow the following steps to install them:
* To install this package we need to follow steps required to install github packages.
https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#installing-a-package
* We will need to authenticate to github packages. Follow the steps in https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#authenticating-to-github-packages.
Use personal access token to authenticate to github packages ( https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#authenticating-with-a-personal-access-token).
This project uses [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/).
Install packages (Node.JS v16.13.1):

View File

@ -0,0 +1,5 @@
EXAMPLE_CONTRACT_ADDRESS=0xD5567AFC3C6c1325698F27d97b74D9ea9c44295e
EDEN_NETWORK_CONTRACT_ADDRESS=0xaf6f5863422b1df982BDC90e2eE03de83F4c8bf9
EDEN_NETWORK_DISTRIBUTION_CONTRACT_ADDRESS=0x0D74f1284Fc0D389fbe1D52C79121518Fcf956C9
EDEN_NETWORK_GOVERNANCE_CONTRACT_ADDRESS=0xF67DedC68506Ef98B8FdBec1e78812B54d79194a

View File

@ -16,6 +16,7 @@
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/no-explicit-any": "off"
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
}
}

View File

@ -4,3 +4,5 @@ node_modules/
build/
tmp/
temp/
.env

View File

@ -1,2 +1,4 @@
require: 'ts-node/register'
require:
- 'ts-node/register'
- 'dotenv/config'
timeout: 5000

View File

@ -0,0 +1 @@
@vulcanize:registry=https://npm.pkg.github.com

View File

@ -1 +1,38 @@
# graph-node
## Test
1. Run `yarn` to install all dependencies.
2. Create .env file
```bash
$ cp .env.example .env
```
3. To deploy contract for example subgraph use https://github.com/deep-stack/eth-contract-tests
```bash
# In eth-contract-test repo.
$ yarn
$ yarn example:deploy
```
Use the address the contract got deployed to and set it to `EXAMPLE_CONTRACT_ADDRESS` in .env file.
3. To deploy contracts for eden subgraph use https://github.com/vulcanize/governance
4. Follow the steps in https://github.com/vulcanize/governance/tree/watcher-ts#instructions
5. Set the contract addresses for eden contracts in .env file from `deployments/localhost` directory in the governance repository.
Following are the contracts whose address needs to be set in .env file:
* EdenNetwork - EDEN_NETWORK_CONTRACT_ADDRESS
* MerkleDistributor - EDEN_NETWORK_DISTRIBUTION_CONTRACT_ADDRESS
* DistributorGovernance - EDEN_NETWORK_GOVERNANCE_CONTRACT_ADDRESS
6. Run `yarn build:example` to build the wasm files.
7. Run `yarn test`.

View File

@ -1,5 +1,5 @@
{
"extends": "assemblyscript/std/assembly.json",
"extends": "@vulcanize/assemblyscript/std/assembly.json",
"include": [
"./**/*.ts"
]

View File

@ -21,14 +21,14 @@
},
"scripts": {
"lint": "eslint .",
"build": "yarn asbuild:debug && yarn build:example",
"build": "yarn asbuild && yarn build:example",
"asbuild:debug": "asc assembly/index.ts --lib ./node_modules --exportRuntime --target debug --runPasses asyncify",
"asbuild:release": "asc assembly/index.ts --lib ./node_modules --exportRuntime --target release --runPasses asyncify",
"asbuild": "yarn asbuild:debug && yarn asbuild:release",
"test": "yarn build && mocha src/**/*.test.ts",
"test": "yarn asbuild:debug && mocha src/**/*.test.ts",
"build:example": "cd test/subgraph/example1 && yarn && yarn build"
},
"dependencies": {
"assemblyscript": "https://github.com/vulcanize/assemblyscript.git#ng-integrate-asyncify"
"@vulcanize/assemblyscript": "0.0.1"
}
}

View File

@ -5,7 +5,7 @@
import path from 'path';
import { instantiate } from './index';
import { TypeId } from './types';
import { createEvent } from './utils';
describe('call handler in mapping code', () => {
let exports: any;
@ -19,51 +19,13 @@ describe('call handler in mapping code', () => {
it('should execute the handler function', async () => {
const {
_start,
__newString,
__newArray,
handleTest,
Address,
BigInt,
ethereum,
Bytes,
Test,
id_of_type: idOfType
handleTest
} = exports;
// 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
_start();
// 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()
);
// Create event params data.
const eventParamsData = [
{
@ -73,54 +35,16 @@ describe('call handler in mapping code', () => {
},
{
name: 'param2',
value: 123,
kind: 'uint'
value: BigInt(123),
kind: 'unsignedBigInt'
}
];
const eventParamArrayPromise = eventParamsData.map(async data => {
const { name, value, kind } = data;
let ethValue;
switch (kind) {
case 'uint': {
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;
}
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('0xCA6D29232D1435D8198E3E5302495417dD073d61');
const contractAddress = '0xCA6D29232D1435D8198E3E5302495417dD073d61';
// Create Test event to be passed to handler.
const test = await Test.__new(
await Address.fromString(addStrPtr),
await BigInt.fromI32(0),
await BigInt.fromI32(0),
null,
block,
transaction,
eventParams
);
const test = await createEvent(exports, contractAddress, eventParamsData);
await handleTest(test);
});

View File

@ -2,26 +2,424 @@
// Copyright 2021 Vulcanize, Inc.
//
import assert from 'assert';
import { ethers } from 'ethers';
import path from 'path';
import { instantiate } from './index';
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';
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
describe('eden wasm loader tests', () => {
describe('EdenNetwork wasm', () => {
let exports: any;
// EdenNetwork contract address string.
const contractAddress = process.env.EDEN_NETWORK_CONTRACT_ADDRESS;
assert(contractAddress);
const data = {
abis: {
EdenNetwork: edenNetworkAbi
},
dataSource: {
address: contractAddress
}
};
it('should load the subgraph network wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/eden/EdenNetwork/EdenNetwork.wasm');
const { exports: { _start } } = await instantiate(filePath);
({ exports } = await instantiate(filePath, data));
const { _start } = exports;
_start();
});
it('should call the slotClaimed handler', async () => {
const {
slotClaimed
} = exports;
// Create dummy SlotClaimedEvent params.
const eventParamsData = [
{
name: 'slot',
kind: 'i32',
value: 0
},
{
name: 'owner',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'delegate',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'newBidAmount',
kind: 'unsignedBigInt',
value: BigInt(1)
},
{
name: 'oldBidAmount',
kind: 'unsignedBigInt',
value: BigInt(1)
},
{
name: 'taxNumerator',
kind: 'i32',
value: 1
},
{
name: 'taxDenominator',
kind: 'i32',
value: 1
}
];
// Create dummy SlotClaimedEvent to be passed to handler.
const slotClaimedEvent = await createEvent(exports, contractAddress, eventParamsData);
await slotClaimed(slotClaimedEvent);
});
it('should call the slotDelegateUpdated handler', async () => {
const {
slotDelegateUpdated
} = exports;
// Create dummy SlotDelegateUpdatedEvent params.
const eventParamsData = [
{
name: 'slot',
kind: 'i32',
value: 0
},
{
name: 'owner',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'newDelegate',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'oldDelegate',
kind: 'address',
value: ZERO_ADDRESS
}
];
// Create dummy SlotDelegateUpdatedEvent to be passed to handler.
const slotClaimedEvent = await createEvent(exports, contractAddress, eventParamsData);
await slotDelegateUpdated(slotClaimedEvent);
});
xit('should call the stake handler', async () => {
const {
stake
} = exports;
// Create dummy StakeEvent params.
const eventParamsData = [
{
name: 'staker',
kind: 'address',
value: '0xDC7d7A8920C8Eecc098da5B7522a5F31509b5Bfc'
},
{
name: 'stakeAmount',
kind: 'unsignedBigInt',
value: BigInt(1)
}
];
// Create dummy StakeEvent to be passed to handler.
const stakeEvent = await createEvent(exports, contractAddress, eventParamsData);
await stake(stakeEvent);
});
xit('should call the unstake handler', async () => {
const {
unstake
} = exports;
// Create dummy UnstakeEvent params.
const eventParamsData = [
{
name: 'staker',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'unstakedAmount',
kind: 'unsignedBigInt',
value: BigInt(1)
}
];
// Create dummy UnstakeEvent to be passed to handler.
const unstakeEvent = await createEvent(exports, contractAddress, eventParamsData);
await unstake(unstakeEvent);
});
});
describe('EdenNetworkDistribution wasm', () => {
let exports: any;
// EdenNetworkDistribution contract address string.
const contractAddress = process.env.EDEN_NETWORK_DISTRIBUTION_CONTRACT_ADDRESS;
assert(contractAddress);
const data = {
abis: {
Distribution: merkleDistributorAbi
},
dataSource: {
address: contractAddress
}
};
it('should load the subgraph network distribution wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/eden/EdenNetworkDistribution/EdenNetworkDistribution.wasm');
const { exports: { _start } } = await instantiate(filePath);
({ exports } = await instantiate(filePath, data));
const { _start } = exports;
_start();
});
it('should call the claimed handler', async () => {
const {
claimed
} = exports;
// Create dummy ClaimedEvent params.
const eventParamsData = [
{
name: 'index',
kind: 'unsignedBigInt',
value: BigInt(1)
},
{
name: 'totalEarned',
kind: 'unsignedBigInt',
value: BigInt(1)
},
{
name: 'account',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'claimed',
kind: 'unsignedBigInt',
value: BigInt(1)
}
];
// Create dummy ClaimedEvent to be passed to handler.
const claimedEvent = await createEvent(exports, contractAddress, eventParamsData);
await claimed(claimedEvent);
});
it('should call the slashed handler', async () => {
const {
slashed
} = exports;
// Create dummy SlashedEvent params.
const eventParamsData = [
{
name: 'account',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'slashed',
kind: 'unsignedBigInt',
value: BigInt(1)
}
];
// Create dummy SlashedEvent to be passed to handler.
const slashedEvent = await createEvent(exports, contractAddress, eventParamsData);
await slashed(slashedEvent);
});
it('should call the merkleRootUpdated handler', async () => {
const {
merkleRootUpdated
} = exports;
// Create dummy MerkleRootUpdatedEvent params.
const eventParamsData = [
{
name: 'merkleRoot',
kind: 'bytes',
value: ethers.utils.hexlify(ethers.utils.randomBytes(32))
},
{
name: 'distributionNumber',
kind: 'unsignedBigInt',
value: BigInt(1)
},
{
name: 'metadataURI',
kind: 'string',
value: 'abc'
}
];
// Create dummy MerkleRootUpdatedEvent to be passed to handler.
const merkleRootUpdatedEvent = await createEvent(exports, contractAddress, eventParamsData);
await merkleRootUpdated(merkleRootUpdatedEvent);
});
it('should call the accountUpdated handler', async () => {
const {
accountUpdated
} = exports;
// Create dummy AccountUpdatedEvent params.
const eventParamsData = [
{
name: 'account',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'totalClaimed',
kind: 'unsignedBigInt',
value: BigInt(1)
},
{
name: 'totalSlashed',
kind: 'unsignedBigInt',
value: BigInt(1)
}
];
// Create dummy AccountUpdatedEvent to be passed to handler.
const accountUpdatedEvent = await createEvent(exports, contractAddress, eventParamsData);
await accountUpdated(accountUpdatedEvent);
});
});
describe('EdenNetworkGovernance wasm', () => {
let exports: any;
// EdenNetworkGovernance contract address string.
const contractAddress = process.env.EDEN_NETWORK_GOVERNANCE_CONTRACT_ADDRESS;
assert(contractAddress);
const data = {
abis: {
Governance: distributorGovernanceAbi
},
dataSource: {
address: contractAddress
}
};
it('should load the subgraph network governance wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/eden/EdenNetworkGovernance/EdenNetworkGovernance.wasm');
const { exports: { _start } } = await instantiate(filePath);
({ exports } = await instantiate(filePath, data));
const { _start } = exports;
_start();
});
it('should call the blockProducerAdded handler', async () => {
const {
blockProducerAdded
} = exports;
// Create dummy BlockProducerAddedEvent params.
const eventParamsData = [
{
name: 'produces',
kind: 'address',
value: ZERO_ADDRESS
}
];
// Create dummy BlockProducerAddedEvent to be passed to handler.
const blockProducerAddedEvent = await createEvent(exports, contractAddress, eventParamsData);
await blockProducerAdded(blockProducerAddedEvent);
});
it('should call the blockProducerRemoved handler', async () => {
const {
blockProducerRemoved
} = exports;
// Create dummy BlockProducerRemovedEvent params.
const eventParamsData = [
{
name: 'producer',
kind: 'address',
value: ZERO_ADDRESS
}
];
// Create dummy BlockProducerRemovedEvent to be passed to handler.
const blockProducerRemovedEvent = await createEvent(exports, contractAddress, eventParamsData);
await blockProducerRemoved(blockProducerRemovedEvent);
});
it('should call the blockProducerRewardCollectorChanged handler', async () => {
const {
blockProducerRewardCollectorChanged
} = exports;
// Create dummy BlockProducerRewardCollectorChangedEvent params.
const eventParamsData = [
{
name: 'producer',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'collector',
kind: 'address',
value: ZERO_ADDRESS
},
{
name: 'metadataURI',
kind: 'string',
value: 'abc'
}
];
// Create dummy BlockProducerRewardCollectorChangedEvent to be passed to handler.
const blockProducerRewardCollectorChangedEvent = await createEvent(exports, contractAddress, eventParamsData);
await blockProducerRewardCollectorChanged(blockProducerRewardCollectorChangedEvent);
});
it('should call the rewardScheduleChanged handler', async () => {
const {
rewardScheduleChanged
} = exports;
// Create dummy RewardScheduleChangedEvent to be passed to handler.
const rewardScheduleChangedEvent = await createEvent(exports, contractAddress, []);
await rewardScheduleChanged(rewardScheduleChangedEvent);
});
});
});

View File

@ -2,16 +2,30 @@
// Copyright 2021 Vulcanize, Inc.
//
import assert from 'assert';
import path from 'path';
import { instantiate } from './index';
import exampleAbi from '../test/subgraph/example1/build/Example1/abis/Example1.json';
describe('eth-call wasm tests', () => {
let exports: any;
const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS;
assert(contractAddress);
const data = {
abis: {
Example1: exampleAbi
},
dataSource: {
address: contractAddress
}
};
it('should load the subgraph example wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/example1/build/Example1/Example1.wasm');
const instance = await instantiate(filePath);
const instance = await instantiate(filePath, data);
exports = instance.exports;
});

View File

@ -2,33 +2,66 @@
// Copyright 2021 Vulcanize, Inc.
//
import assert from 'assert';
import fs from 'fs/promises';
import loader from 'assemblyscript/lib/loader';
import loader from '@vulcanize/assemblyscript/lib/loader';
import {
utils,
BigNumber,
getDefaultProvider,
Contract
Contract,
ContractInterface
} from 'ethers';
import { TypeId } from './types';
import exampleAbi from '../test/subgraph/example1/build/Example1/abis/Example1.json';
import { fromEthereumValue, toEthereumValue } from './utils';
const NETWORK_URL = 'http://127.0.0.1:8081';
type idOfType = (TypeId: number) => number
export const instantiate = async (filePath: string): Promise<loader.ResultObject & { exports: any }> => {
interface DataSource {
address: string
}
interface GraphData {
abis?: {[key: string]: ContractInterface};
dataSource?: DataSource;
}
export const instantiate = async (filePath: string, data: GraphData = {}): Promise<loader.ResultObject & { exports: any }> => {
const { abis = {}, dataSource } = data;
const buffer = await fs.readFile(filePath);
const provider = getDefaultProvider(NETWORK_URL);
const imports: WebAssembly.Imports = {
index: {
'store.get': () => {
'store.get': async (entity: number, id: number) => {
console.log('store.get');
const entityString = __getString(entity);
console.log('entity:', entityString);
const idString = __getString(id);
console.log('id:', idString);
// TODO: Implement store get to fetch from DB using entity and id.
// TODO: Fill entity with field values.
// return Entity.__new()
return null;
},
'store.set': () => {
'store.set': async (entity: number, id: number, data: number) => {
console.log('store.set');
const entityString = __getString(entity);
console.log('entity:', entityString);
const idString = __getString(id);
console.log('id:', idString);
const entityInstance = await Entity.wrap(data);
const entityInstanceId = __getString(await entityInstance.getString(await __newString('id')));
console.log('entity instance id:', entityInstanceId);
// TODO: Implement store set to save entity in db with values from entityInstance.
},
'typeConversion.stringToH160': () => {
@ -110,16 +143,22 @@ export const instantiate = async (filePath: string): Promise<loader.ResultObject
const contractName = __getString(await smartContractCall.contractName);
const functionName = __getString(await smartContractCall.functionName);
const functionSignature = __getString(await smartContractCall.functionSignature);
const functionParams = __getArray(await smartContractCall.functionParams);
let functionParams = __getArray(await smartContractCall.functionParams);
console.log('ethereum.call params');
console.log('contractName:', contractName);
console.log('functionSignature:', functionSignature);
// TODO: Get ABI according to contractName.
const contract = new Contract(__getString(await contractAddress.toHexString()), exampleAbi, provider);
const abi = abis[contractName];
const contract = new Contract(__getString(await contractAddress.toHexString()), abi, provider);
try {
const functionParamsPromise = functionParams.map(async param => {
const ethereumValue = await ethereum.Value.wrap(param);
return fromEthereumValue(exports, ethereumValue);
});
functionParams = await Promise.all(functionParamsPromise);
// TODO: Check for function overloading.
let result = await contract[functionName](...functionParams);
@ -127,11 +166,13 @@ export const instantiate = async (filePath: string): Promise<loader.ResultObject
result = [result];
}
const resultPtrArrayPromise = result.map(async (value: any) => {
// TODO: Create Value instance according to return type.
const ethValue = await ethereum.Value.fromString(await __newString(value));
// TODO: Check for function overloading.
// Using function signature does not work.
const outputs = contract.interface.getFunction(functionName).outputs;
return ethValue;
const resultPtrArrayPromise = result.map(async (value: any, index: number) => {
assert(outputs);
return toEthereumValue(exports, value, outputs[index].type);
});
const resultPtrArray: any[] = await Promise.all(resultPtrArrayPromise);
@ -182,15 +223,15 @@ export const instantiate = async (filePath: string): Promise<loader.ResultObject
}
},
numbers: {
'bigDecimal.dividedBy': (x: number, y: number) => {
'bigDecimal.dividedBy': async (x: number, y: number) => {
console.log('numbers bigDecimal.dividedBy');
const bigDecimaly = BigDecimal.wrap(y);
const yDigitsBigIntArray = __getArray(bigDecimaly.digits);
const yDigitsBigIntArray = __getArray(await bigDecimaly.digits);
const yDigits = BigNumber.from(yDigitsBigIntArray);
const yExpBigIntArray = __getArray(bigDecimaly.exp);
const yExpBigIntArray = __getArray(await bigDecimaly.exp);
const yExp = BigNumber.from(yExpBigIntArray);
console.log('y digits and exp', yDigits, yExp);
@ -298,8 +339,9 @@ export const instantiate = async (filePath: string): Promise<loader.ResultObject
}
},
datasource: {
'dataSource.address': () => {
console.log('dataSource.address');
'dataSource.address': async () => {
assert(dataSource);
return Address.fromString(await __newString(dataSource.address));
}
}
};
@ -309,11 +351,13 @@ export const instantiate = async (filePath: string): Promise<loader.ResultObject
const { __getString, __newString, __getArray, __newArray } = exports;
// TODO: Assign from types file generated by graph-cli
const getIdOfType: idOfType = exports.id_of_type as idOfType;
const BigDecimal: any = exports.BigDecimal as any;
const BigInt: any = exports.BigInt as any;
const Address: any = exports.Address as any;
const ethereum: any = exports.ethereum as any;
const Entity: any = exports.Entity as any;
return instance;
};

View File

@ -57,3 +57,17 @@ export enum TypeId {
ArrayF64 = 50,
ArrayBigDecimal = 51,
}
// ValueKind from https://github.com/graphprotocol/graph-ts/blob/master/chain/ethereum.ts#L13
export enum ValueKind {
ADDRESS = 0,
FIXED_BYTES = 1,
BYTES = 2,
INT = 3,
UINT = 4,
BOOL = 5,
STRING = 6,
FIXED_ARRAY = 7,
ARRAY = 8,
TUPLE = 9,
}

View File

@ -0,0 +1,217 @@
import { BigNumber } from 'ethers';
import { TypeId, ValueKind } from './types';
interface EventParam {
name: string;
value: any;
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;
// 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': {
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
);
};
/**
* Method to get value from graph-ts ethereum.Value wasm instance.
* @param exports
* @param value
* @returns
*/
export const fromEthereumValue = async (exports: any, value: any): Promise<any> => {
const {
__getString,
BigInt,
Address
} = exports;
const kind = await value.kind;
switch (kind) {
case ValueKind.ADDRESS: {
const address = Address.wrap(await value.toAddress());
const addressStringPtr = await address.toHexString();
return __getString(addressStringPtr);
}
case ValueKind.BOOL: {
const bool = await value.toBoolean();
return Boolean(bool);
}
case ValueKind.BYTES:
case ValueKind.FIXED_BYTES: {
const bytes = await value.toBytes();
const bytesStringPtr = await bytes.toHexString();
return __getString(bytesStringPtr);
}
case ValueKind.INT:
case ValueKind.UINT: {
const bigInt = BigInt.wrap(await value.toBigInt());
const bigIntStringPtr = await bigInt.toString();
const bigIntString = __getString(bigIntStringPtr);
return BigNumber.from(bigIntString);
}
default:
break;
}
};
/**
* Method to get ethereum value for passing to wasm instance.
* @param exports
* @param value
* @param type
* @returns
*/
export const toEthereumValue = async (exports: any, value: any, type: string): Promise<any> => {
const {
__newString,
ByteArray,
Bytes,
Address,
ethereum,
BigInt
} = exports;
// For boolean type.
if (type === 'bool') {
return ethereum.Value.fromBoolean(value ? 1 : 0);
}
const [isIntegerOrEnum, isInteger, isUnsigned] = type.match(/^enum|((u?)int([0-9]+))/) || [false];
// For uint/int type or enum type.
if (isIntegerOrEnum) {
const valueString = await __newString(value.toString());
const bigInt = await BigInt.fromString(valueString);
let ethereumValue = await ethereum.Value.fromUnsignedBigInt(bigInt);
if (Boolean(isInteger) && !isUnsigned) {
ethereumValue = await ethereum.Value.fromSignedBigInt(bigInt);
}
return ethereumValue;
}
if (type.startsWith('address')) {
return ethereum.Value.fromAddress(await Address.fromString(await __newString(value)));
}
// TODO: Check between fixed bytes and dynamic bytes.
if (type.startsWith('bytes')) {
const byteArray = await ByteArray.fromHexString(await __newString(value));
const bytes = await Bytes.fromByteArray(byteArray);
return ethereum.Value.fromBytes(bytes);
}
// For string type.
return ethereum.Value.fromString(await __newString(value));
};

View File

@ -1,4 +1,4 @@
import { Address, log, BigInt, BigDecimal, ByteArray } from '@graphprotocol/graph-ts';
import { Address, log, BigInt, BigDecimal, ByteArray, dataSource } from '@graphprotocol/graph-ts';
import {
Example1,
@ -57,7 +57,7 @@ export function testEthCall (): void {
// Bind the contract to the address that emitted the event.
// TODO: Address.fromString throws error in WASM module instantiation.
const contractAddress = Address.fromString('0x3ebd8bb51fF52aDAc490117B31F5F137BB125A9D');
const contractAddress = dataSource.address();
const contract = Example1.bind(contractAddress);
// Access functions by calling them.

View File

@ -2849,6 +2849,15 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.7.tgz#c792bf92d19025e7940358536d468218a11eeedb"
integrity sha512-YwGOcNZjOY/MmadpzFBXWyHEwZSf0lVU4XF5zpD7tXC9dmqjdo38Jkk06wATu4LYHDPW4emXKMB5YLFPWPkwFA==
"@vulcanize/assemblyscript@0.0.1":
version "0.0.1"
resolved "https://npm.pkg.github.com/download/@vulcanize/assemblyscript/0.0.1/f1dc67712e8582a0748a9eeac772920b4a33baf03aba382b5086a8d0054e43b5#c98f2a706f797ba205f49b86a9de7093e7033726"
integrity sha512-CCTyGjUH9NPASd3YRx2YeJQquEeiPRcQ5DaZjQn2c/PHOdSAoKStU/9JkO1+oKTDhuF/eEX7LZh09NeojWjibg==
dependencies:
asyncify-wasm "^1.2.1"
binaryen "101.0.0-nightly.20210723"
long "^4.0.0"
"@wry/context@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.0.tgz#f903eceb89d238ef7e8168ed30f4511f92d83e06"
@ -3435,7 +3444,7 @@ array.prototype.flat@^1.2.4:
define-properties "^1.1.3"
es-abstract "^1.18.0-next.1"
arrify@^1.0.0, arrify@^1.0.1:
arrify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
@ -3475,16 +3484,6 @@ assemblyscript@0.19.10:
binaryen "101.0.0-nightly.20210723"
long "^4.0.0"
"assemblyscript@https://github.com/vulcanize/assemblyscript.git#ng-integrate-asyncify":
version "0.0.0"
resolved "https://github.com/vulcanize/assemblyscript.git#ccbd560972503ecef05b43e0c7f694f97d42959f"
dependencies:
asyncify-wasm "^1.2.1"
binaryen "101.0.0-nightly.20210723"
long "^4.0.0"
source-map-support "^0.5.19"
ts-node "^6.2.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
@ -4373,11 +4372,6 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
buffer-from@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer-to-arraybuffer@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a"
@ -5691,7 +5685,7 @@ dicer@0.3.0:
dependencies:
streamsearch "0.1.2"
diff@3.5.0, diff@^3.1.0:
diff@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
@ -12747,14 +12741,6 @@ source-map-support@^0.5.13, source-map-support@^0.5.17:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-support@^0.5.19, source-map-support@^0.5.6:
version "0.5.20"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9"
integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-url@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
@ -13512,20 +13498,6 @@ ts-node@^10.2.1:
make-error "^1.1.1"
yn "3.1.1"
ts-node@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-6.2.0.tgz#65a0ae2acce319ea4fd7ac8d7c9f1f90c5da6baf"
integrity sha512-ZNT+OEGfUNVMGkpIaDJJ44Zq3Yr0bkU/ugN1PHbU+/01Z7UV1fsELRiTx1KuQNvQ1A3pGh3y25iYF6jXgxV21A==
dependencies:
arrify "^1.0.0"
buffer-from "^1.1.0"
diff "^3.1.0"
make-error "^1.1.1"
minimist "^1.2.0"
mkdirp "^0.5.1"
source-map-support "^0.5.6"
yn "^2.0.0"
tsconfig-paths@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
@ -14786,11 +14758,6 @@ yn@3.1.1:
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
yn@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a"
integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"