diff --git a/.github/workflows/on-main.yaml b/.github/workflows/on-main.yaml index f9adb628..f64586e5 100644 --- a/.github/workflows/on-main.yaml +++ b/.github/workflows/on-main.yaml @@ -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: diff --git a/.github/workflows/on-pr.yaml b/.github/workflows/on-pr.yaml index 508decb8..c2daf938 100644 --- a/.github/workflows/on-pr.yaml +++ b/.github/workflows/on-pr.yaml @@ -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: diff --git a/README.md b/README.md index df1e7c7d..e3dc2a55 100644 --- a/README.md +++ b/README.md @@ -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): diff --git a/packages/graph-node/.env.example b/packages/graph-node/.env.example new file mode 100644 index 00000000..8bd20626 --- /dev/null +++ b/packages/graph-node/.env.example @@ -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 diff --git a/packages/graph-node/.eslintrc.json b/packages/graph-node/.eslintrc.json index aa470196..56ce563e 100644 --- a/packages/graph-node/.eslintrc.json +++ b/packages/graph-node/.eslintrc.json @@ -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" } } diff --git a/packages/graph-node/.gitignore b/packages/graph-node/.gitignore index be541260..faa63908 100644 --- a/packages/graph-node/.gitignore +++ b/packages/graph-node/.gitignore @@ -4,3 +4,5 @@ node_modules/ build/ tmp/ temp/ + +.env diff --git a/packages/graph-node/.mocharc.yml b/packages/graph-node/.mocharc.yml index a05b9563..b4b5eeaf 100644 --- a/packages/graph-node/.mocharc.yml +++ b/packages/graph-node/.mocharc.yml @@ -1,2 +1,4 @@ -require: 'ts-node/register' +require: + - 'ts-node/register' + - 'dotenv/config' timeout: 5000 diff --git a/packages/graph-node/.npmrc b/packages/graph-node/.npmrc new file mode 100644 index 00000000..1d6cdc62 --- /dev/null +++ b/packages/graph-node/.npmrc @@ -0,0 +1 @@ +@vulcanize:registry=https://npm.pkg.github.com diff --git a/packages/graph-node/README.md b/packages/graph-node/README.md index 9aeba189..e5bec640 100644 --- a/packages/graph-node/README.md +++ b/packages/graph-node/README.md @@ -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`. diff --git a/packages/graph-node/assembly/tsconfig.json b/packages/graph-node/assembly/tsconfig.json index 891cc238..062482b9 100644 --- a/packages/graph-node/assembly/tsconfig.json +++ b/packages/graph-node/assembly/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "assemblyscript/std/assembly.json", + "extends": "@vulcanize/assemblyscript/std/assembly.json", "include": [ "./**/*.ts" ] diff --git a/packages/graph-node/package.json b/packages/graph-node/package.json index b26d6973..9bc9652d 100644 --- a/packages/graph-node/package.json +++ b/packages/graph-node/package.json @@ -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" } } diff --git a/packages/graph-node/src/call-handler.test.ts b/packages/graph-node/src/call-handler.test.ts index 9ab6a542..271b6f8d 100644 --- a/packages/graph-node/src/call-handler.test.ts +++ b/packages/graph-node/src/call-handler.test.ts @@ -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); }); diff --git a/packages/graph-node/src/eden.test.ts b/packages/graph-node/src/eden.test.ts index c275f60c..1eb83405 100644 --- a/packages/graph-node/src/eden.test.ts +++ b/packages/graph-node/src/eden.test.ts @@ -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', () => { - 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); - _start(); + 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'); + ({ 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); + }); }); - 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); - _start(); + 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'); + ({ 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); + }); }); - 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); - _start(); + 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'); + ({ 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); + }); }); }); diff --git a/packages/graph-node/src/eth-call.test.ts b/packages/graph-node/src/eth-call.test.ts index c88af644..31f1a5cb 100644 --- a/packages/graph-node/src/eth-call.test.ts +++ b/packages/graph-node/src/eth-call.test.ts @@ -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; }); diff --git a/packages/graph-node/src/index.ts b/packages/graph-node/src/index.ts index 8084a3ff..c6dc98d4 100644 --- a/packages/graph-node/src/index.ts +++ b/packages/graph-node/src/index.ts @@ -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 => { +interface DataSource { + address: string +} + +interface GraphData { + abis?: {[key: string]: ContractInterface}; + dataSource?: DataSource; +} + +export const instantiate = async (filePath: string, data: GraphData = {}): Promise => { + 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 { + 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 { - // 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 { + '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 { - 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 => { + 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 => { + 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 => { + 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)); +}; diff --git a/packages/graph-node/test/subgraph/eden/EdenNetwork/EdenNetwork.wasm b/packages/graph-node/test/subgraph/eden/EdenNetwork/EdenNetwork.wasm index e1e2facc..75f5f60a 100644 Binary files a/packages/graph-node/test/subgraph/eden/EdenNetwork/EdenNetwork.wasm and b/packages/graph-node/test/subgraph/eden/EdenNetwork/EdenNetwork.wasm differ diff --git a/packages/graph-node/test/subgraph/eden/EdenNetworkDistribution/EdenNetworkDistribution.wasm b/packages/graph-node/test/subgraph/eden/EdenNetworkDistribution/EdenNetworkDistribution.wasm index 5517ffd3..422c1bf7 100644 Binary files a/packages/graph-node/test/subgraph/eden/EdenNetworkDistribution/EdenNetworkDistribution.wasm and b/packages/graph-node/test/subgraph/eden/EdenNetworkDistribution/EdenNetworkDistribution.wasm differ diff --git a/packages/graph-node/test/subgraph/eden/EdenNetworkGovernance/EdenNetworkGovernance.wasm b/packages/graph-node/test/subgraph/eden/EdenNetworkGovernance/EdenNetworkGovernance.wasm index 9860cd7d..d1fb16d6 100644 Binary files a/packages/graph-node/test/subgraph/eden/EdenNetworkGovernance/EdenNetworkGovernance.wasm and b/packages/graph-node/test/subgraph/eden/EdenNetworkGovernance/EdenNetworkGovernance.wasm differ diff --git a/packages/graph-node/test/subgraph/example1/src/mapping.ts b/packages/graph-node/test/subgraph/example1/src/mapping.ts index 5b30ce62..1ecb7aac 100644 --- a/packages/graph-node/test/subgraph/example1/src/mapping.ts +++ b/packages/graph-node/test/subgraph/example1/src/mapping.ts @@ -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. diff --git a/yarn.lock b/yarn.lock index 26e59cc5..8a6006a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"