mirror of
https://github.com/cerc-io/watcher-ts
synced 2026-09-09 17:24:07 +00:00
Add support for subgraph schema data types (#46)
* Fix packages build * Add support for boolean type in subgraph schema * Add BigDecimal fromString and toString implementation * Add implementation for subgraph Bytes type * Implement enum type for subgraph schema * Add implementation for subgraph schema BigDecimal type * JSON stringify and parse entity data for auto-diff
This commit is contained in:
@@ -12,6 +12,8 @@ import {
|
||||
Contract,
|
||||
ContractInterface
|
||||
} from 'ethers';
|
||||
import Decimal from 'decimal.js';
|
||||
import JSONbig from 'json-bigint';
|
||||
|
||||
import { IndexerInterface } from '@vulcanize/util';
|
||||
|
||||
@@ -73,7 +75,9 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
|
||||
|
||||
// Prepare the diff data.
|
||||
const diffData: any = { state: {} };
|
||||
diffData.state[entityName] = dbData;
|
||||
// JSON stringify and parse data for handling unknown types when encoding.
|
||||
// For example, decimal.js values are converted to string in the diff data.
|
||||
diffData.state[entityName] = JSONbig.parse(JSONbig.stringify(dbData));
|
||||
|
||||
// Create an auto-diff.
|
||||
assert(indexer.createDiffStaged);
|
||||
@@ -81,65 +85,10 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
|
||||
await indexer.createDiffStaged(dataSource.address, context.event.block.blockHash, diffData);
|
||||
},
|
||||
|
||||
'typeConversion.stringToH160': () => {
|
||||
console.log('index typeConversion.stringToH160');
|
||||
},
|
||||
'typeConversion.bytesToHex': () => {
|
||||
console.log('index typeConversion.bytesToHex');
|
||||
},
|
||||
// 'typeConversion.bytesToString': () => {
|
||||
// console.log('typeConversion.bytesToString');
|
||||
// },
|
||||
'typeConversion.bigIntToString': () => {
|
||||
console.log('index typeConversion.bigIntToString');
|
||||
},
|
||||
|
||||
// 'bigDecimal.fromString': () => {
|
||||
// console.log('bigDecimal.fromString');
|
||||
// },
|
||||
// 'bigDecimal.times': () => {
|
||||
// console.log('bigDecimal.times');
|
||||
// },
|
||||
'bigDecimal.dividedBy': () => {
|
||||
console.log('bigDecimal.dividedBy');
|
||||
},
|
||||
// 'bigDecimal.plus': () => {
|
||||
// console.log('bigDecimal.plus');
|
||||
// },
|
||||
// 'bigDecimal.minus': () => {
|
||||
// console.log('bigDecimal.minus');
|
||||
// },
|
||||
|
||||
'bigInt.plus': () => {
|
||||
console.log('bigInt.plus');
|
||||
},
|
||||
'bigInt.minus': () => {
|
||||
console.log('bigInt.minus');
|
||||
},
|
||||
'bigInt.times': () => {
|
||||
console.log('bigInt.times');
|
||||
},
|
||||
'bigInt.dividedBy': () => {
|
||||
console.log('bigInt.dividedBy');
|
||||
},
|
||||
// 'bigInt.mod': () => {
|
||||
// console.log('bigInt.mod');
|
||||
// },
|
||||
'bigInt.fromString': () => {
|
||||
console.log('bigInt.fromString');
|
||||
},
|
||||
|
||||
'log.log': (_: number, msg: number) => {
|
||||
console.log('log.log', __getString(msg));
|
||||
},
|
||||
|
||||
// 'dataSource.create': () => {
|
||||
// console.log('dataSource.create');
|
||||
// },
|
||||
'dataSource.address': () => {
|
||||
console.log('dataSource.address');
|
||||
},
|
||||
|
||||
'test.asyncMethod': async () => {
|
||||
console.log('before timer start');
|
||||
await new Promise(resolve => {
|
||||
@@ -253,11 +202,38 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
|
||||
|
||||
console.log('y digits and exp', yDigits, yExp);
|
||||
},
|
||||
'bigDecimal.toString': () => {
|
||||
console.log('numbers bigDecimal.toString');
|
||||
'bigDecimal.toString': async (bigDecimal: number) => {
|
||||
const bigDecimalInstance = BigDecimal.wrap(bigDecimal);
|
||||
|
||||
const digitsBigInt = BigInt.wrap(await bigDecimalInstance.digits);
|
||||
const expBigInt = BigInt.wrap(await bigDecimalInstance.exp);
|
||||
|
||||
const digits = __getString(await digitsBigInt.toString());
|
||||
const exp = __getString(await expBigInt.toString());
|
||||
|
||||
const decimal = new Decimal(`${digits}e${exp}`);
|
||||
const ptr = __newString(decimal.toFixed());
|
||||
|
||||
return ptr;
|
||||
},
|
||||
'bigDecimal.fromString': () => {
|
||||
console.log('numbers bigDecimal.toString');
|
||||
'bigDecimal.fromString': async (s: number) => {
|
||||
const string = __getString(s);
|
||||
const decimal = new Decimal(string);
|
||||
|
||||
// Convert from digits array to BigInt.
|
||||
const digits = decimal.d.join('');
|
||||
const digitsBigNumber = BigNumber.from(digits);
|
||||
const signBigNumber = BigNumber.from(decimal.s);
|
||||
const digitsBigInt = await BigInt.fromString(await __newString(digitsBigNumber.mul(signBigNumber).toString()));
|
||||
|
||||
// Calculate exp after converting digits to BigInt above.
|
||||
const exp = decimal.e - digits.length + 1;
|
||||
const expBigInt = await BigInt.fromString(await __newString(exp.toString()));
|
||||
|
||||
const bigDecimal = await BigDecimal.__new(digitsBigInt);
|
||||
bigDecimal.exp = expBigInt;
|
||||
|
||||
return bigDecimal;
|
||||
},
|
||||
'bigDecimal.plus': () => {
|
||||
console.log('bigDecimal.plus');
|
||||
|
||||
@@ -28,11 +28,22 @@ describe('numbers wasm tests', () => {
|
||||
_start();
|
||||
});
|
||||
|
||||
it('should execute bigInt fromString API', async () => {
|
||||
const { testBigIntFromString, __getString } = exports;
|
||||
describe('should execute bigInt fromString API', () => {
|
||||
let testBigIntFromString: any, __getString: any, __newString: any;
|
||||
|
||||
const ptr = await testBigIntFromString();
|
||||
expect(__getString(ptr)).to.equal('123');
|
||||
before(() => {
|
||||
({ testBigIntFromString, __getString, __newString } = exports);
|
||||
});
|
||||
|
||||
it('should get bigInt for positive numbers', async () => {
|
||||
const ptr = await testBigIntFromString(await __newString('123'));
|
||||
expect(__getString(ptr)).to.equal('123');
|
||||
});
|
||||
|
||||
xit('should get bigInt for negative numbers', async () => {
|
||||
const ptr = await testBigIntFromString(await __newString('-123'));
|
||||
expect(__getString(ptr)).to.equal('-123');
|
||||
});
|
||||
});
|
||||
|
||||
it('should execute bigInt plus API', async () => {
|
||||
@@ -63,6 +74,31 @@ describe('numbers wasm tests', () => {
|
||||
expect(__getString(ptr)).to.equal('100');
|
||||
});
|
||||
|
||||
it('should execute bigDecimal toString API', async () => {
|
||||
const { testBigDecimalToString, __getString } = exports;
|
||||
|
||||
const ptr = await testBigDecimalToString();
|
||||
expect(__getString(ptr)).to.equal('1000000000000000000');
|
||||
});
|
||||
|
||||
describe('should execute bigDecimal fromString API', () => {
|
||||
let testBigDecimalFromString: any, __getString: any, __newString: any;
|
||||
|
||||
before(() => {
|
||||
({ testBigDecimalFromString, __getString, __newString } = exports);
|
||||
});
|
||||
|
||||
it('should get bigDecimal for numbers without decimals', async () => {
|
||||
const ptr = await testBigDecimalFromString(await __newString('4.321e+4'));
|
||||
expect(__getString(ptr)).to.equal('43210');
|
||||
});
|
||||
|
||||
xit('should get bigDecimal for numbers with decimals', async () => {
|
||||
const ptr = await testBigDecimalFromString(await __newString('5032485723458348569331745.33434346346912144534543'));
|
||||
expect(__getString(ptr)).to.equal('5032485723458348569331745.33434346346912144534543');
|
||||
});
|
||||
});
|
||||
|
||||
xit('should execute bigDecimal dividedBy API', () => {
|
||||
const { testBigDecimalDividedBy, __getString } = exports;
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ export enum TypeId {
|
||||
ArrayBigDecimal = 51,
|
||||
}
|
||||
|
||||
// ValueKind from https://github.com/graphprotocol/graph-ts/blob/master/chain/ethereum.ts#L13
|
||||
export enum ValueKind {
|
||||
// ethereum ValueKind from https://github.com/graphprotocol/graph-ts/blob/master/chain/ethereum.ts#L13
|
||||
export enum EthereumValueKind {
|
||||
ADDRESS = 0,
|
||||
FIXED_BYTES = 1,
|
||||
BYTES = 2,
|
||||
@@ -71,3 +71,15 @@ export enum ValueKind {
|
||||
ARRAY = 8,
|
||||
TUPLE = 9,
|
||||
}
|
||||
|
||||
// ValueKind from https://github.com/graphprotocol/graph-ts/blob/master/common/value.ts#L8
|
||||
export enum ValueKind {
|
||||
STRING = 0,
|
||||
INT = 1,
|
||||
BIGDECIMAL = 2,
|
||||
BOOL = 3,
|
||||
ARRAY = 4,
|
||||
NULL = 5,
|
||||
BYTES = 6,
|
||||
BIGINT = 7,
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import fs from 'fs-extra';
|
||||
import debug from 'debug';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
import { TypeId, ValueKind } from './types';
|
||||
import { TypeId, EthereumValueKind, ValueKind } from './types';
|
||||
import Decimal from 'decimal.js';
|
||||
|
||||
const log = debug('vulcanize:utils');
|
||||
|
||||
@@ -55,26 +56,26 @@ export const fromEthereumValue = async (instanceExports: any, value: any): Promi
|
||||
const kind = await value.kind;
|
||||
|
||||
switch (kind) {
|
||||
case ValueKind.ADDRESS: {
|
||||
case EthereumValueKind.ADDRESS: {
|
||||
const address = Address.wrap(await value.toAddress());
|
||||
const addressStringPtr = await address.toHexString();
|
||||
return __getString(addressStringPtr);
|
||||
}
|
||||
|
||||
case ValueKind.BOOL: {
|
||||
case EthereumValueKind.BOOL: {
|
||||
const bool = await value.toBoolean();
|
||||
return Boolean(bool);
|
||||
}
|
||||
|
||||
case ValueKind.BYTES:
|
||||
case ValueKind.FIXED_BYTES: {
|
||||
case EthereumValueKind.BYTES:
|
||||
case EthereumValueKind.FIXED_BYTES: {
|
||||
const bytes = await value.toBytes();
|
||||
const bytesStringPtr = await bytes.toHexString();
|
||||
return __getString(bytesStringPtr);
|
||||
}
|
||||
|
||||
case ValueKind.INT:
|
||||
case ValueKind.UINT: {
|
||||
case EthereumValueKind.INT:
|
||||
case EthereumValueKind.UINT: {
|
||||
const bigInt = BigInt.wrap(await value.toBigInt());
|
||||
const bigIntStringPtr = await bigInt.toString();
|
||||
const bigIntString = __getString(bigIntStringPtr);
|
||||
@@ -292,7 +293,7 @@ export const getSubgraphConfig = async (subgraphPath: string): Promise<any> => {
|
||||
};
|
||||
|
||||
export const toEntityValue = async (instanceExports: any, entityInstance: any, data: any, type: string, key: string) => {
|
||||
const { __newString, BigInt: ExportBigInt } = instanceExports;
|
||||
const { __newString, BigInt: ExportBigInt, Value, ByteArray, Bytes, BigDecimal } = instanceExports;
|
||||
const entityKey = await __newString(key);
|
||||
const value = data[key];
|
||||
|
||||
@@ -300,7 +301,20 @@ export const toEntityValue = async (instanceExports: any, entityInstance: any, d
|
||||
case 'varchar': {
|
||||
const entityValue = await __newString(value);
|
||||
|
||||
return entityInstance.setString(entityKey, entityValue);
|
||||
const graphValue = Value.wrap(await entityInstance.get(entityKey));
|
||||
|
||||
const kind = await graphValue.kind;
|
||||
|
||||
switch (kind) {
|
||||
case ValueKind.BYTES: {
|
||||
const byteArray = await ByteArray.fromHexString(entityValue);
|
||||
const bytes = await Bytes.fromByteArray(byteArray);
|
||||
return entityInstance.setBytes(entityKey, bytes);
|
||||
}
|
||||
|
||||
default:
|
||||
return entityInstance.setString(entityKey, entityValue);
|
||||
}
|
||||
}
|
||||
|
||||
case 'integer': {
|
||||
@@ -313,6 +327,20 @@ export const toEntityValue = async (instanceExports: any, entityInstance: any, d
|
||||
return entityInstance.setBigInt(entityKey, bigInt);
|
||||
}
|
||||
|
||||
case 'boolean': {
|
||||
return entityInstance.setBoolean(entityKey, value ? 1 : 0);
|
||||
}
|
||||
|
||||
case 'enum': {
|
||||
const entityValue = await __newString(value);
|
||||
return entityInstance.setString(entityKey, entityValue);
|
||||
}
|
||||
|
||||
case 'numeric': {
|
||||
const bigDecimal = await BigDecimal.fromString(await __newString(value.toString()));
|
||||
return entityInstance.setBigDecimal(entityKey, bigDecimal);
|
||||
}
|
||||
|
||||
// TODO: Support more types.
|
||||
default:
|
||||
throw new Error(`Unsupported type: ${type}`);
|
||||
@@ -320,12 +348,25 @@ export const toEntityValue = async (instanceExports: any, entityInstance: any, d
|
||||
};
|
||||
|
||||
export const fromEntityValue = async (instanceExports: any, entityInstance: any, type: string, key: string): Promise<any> => {
|
||||
const { __newString, __getString, BigInt: ExportBigInt } = instanceExports;
|
||||
const { __newString, __getString, BigInt: ExportBigInt, Value, BigDecimal, Bytes } = instanceExports;
|
||||
const entityKey = await __newString(key);
|
||||
|
||||
switch (type) {
|
||||
case 'varchar': {
|
||||
return __getString(await entityInstance.getString(entityKey));
|
||||
const value = Value.wrap(await entityInstance.get(entityKey));
|
||||
|
||||
const kind = await value.kind;
|
||||
|
||||
switch (kind) {
|
||||
case ValueKind.BYTES: {
|
||||
const bytes = await Bytes.wrap(await value.toBytes());
|
||||
const bytesStringPtr = await bytes.toHexString();
|
||||
return __getString(bytesStringPtr);
|
||||
}
|
||||
|
||||
default:
|
||||
return __getString(await entityInstance.getString(entityKey));
|
||||
}
|
||||
}
|
||||
|
||||
case 'integer': {
|
||||
@@ -337,6 +378,19 @@ export const fromEntityValue = async (instanceExports: any, entityInstance: any,
|
||||
return BigInt(__getString(await bigInt.toString()));
|
||||
}
|
||||
|
||||
case 'boolean': {
|
||||
return Boolean(await entityInstance.getBoolean(entityKey));
|
||||
}
|
||||
|
||||
case 'enum': {
|
||||
return __getString(await entityInstance.getString(entityKey));
|
||||
}
|
||||
|
||||
case 'numeric': {
|
||||
const bigDecimal = BigDecimal.wrap(await entityInstance.getBigDecimal(entityKey));
|
||||
return new Decimal(__getString(await bigDecimal.toString()));
|
||||
}
|
||||
|
||||
// TODO: Support more types.
|
||||
default:
|
||||
throw new Error(`Unsupported type: ${type}`);
|
||||
|
||||
Reference in New Issue
Block a user