mirror of
https://github.com/cerc-io/watcher-ts
synced 2026-09-11 10:14:07 +00:00
Implement subgraph JSON host APIs fromBytes and try_fromBytes (#105)
* Implement JSON fromBytes host API * Implement json.try_fromBytes host API * Fill host API code for json number methods
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Copyright 2021 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import { BaseProvider } from '@ethersproject/providers';
|
||||
|
||||
import { instantiate } from './loader';
|
||||
import { getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils';
|
||||
import { Database } from './database';
|
||||
import { Indexer } from '../test/utils/indexer';
|
||||
|
||||
describe('json host api', () => {
|
||||
let exports: any;
|
||||
let db: Database;
|
||||
let indexer: Indexer;
|
||||
let provider: BaseProvider;
|
||||
|
||||
before(async () => {
|
||||
db = getTestDatabase();
|
||||
indexer = getTestIndexer();
|
||||
provider = getTestProvider();
|
||||
});
|
||||
|
||||
it('should load the subgraph example wasm', async () => {
|
||||
const filePath = path.resolve(__dirname, '../test/subgraph/example1/build/Example1/Example1.wasm');
|
||||
const instance = await instantiate(
|
||||
db,
|
||||
indexer,
|
||||
provider,
|
||||
{},
|
||||
filePath
|
||||
);
|
||||
exports = instance.exports;
|
||||
const { _start } = 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();
|
||||
});
|
||||
|
||||
it('should get JSONValue from bytes', async () => {
|
||||
const { testJsonFromBytes } = exports;
|
||||
|
||||
await testJsonFromBytes();
|
||||
});
|
||||
|
||||
it('should parse JSON safely', async () => {
|
||||
const { testJsonTryFromBytes } = exports;
|
||||
|
||||
await testJsonTryFromBytes();
|
||||
});
|
||||
});
|
||||
@@ -24,7 +24,8 @@ import {
|
||||
fromEthereumValue,
|
||||
toEthereumValue,
|
||||
resolveEntityFieldConflicts,
|
||||
getEthereumTypes
|
||||
getEthereumTypes,
|
||||
jsonFromBytes
|
||||
} from './utils';
|
||||
import { Database } from './database';
|
||||
|
||||
@@ -255,7 +256,7 @@ export const instantiate = async (
|
||||
return ptr;
|
||||
},
|
||||
'typeConversion.bigIntToHex': async (bigInt: number) => {
|
||||
const bigIntInstance = await BigInt.wrap(bigInt);
|
||||
const bigIntInstance = await ASBigInt.wrap(bigInt);
|
||||
const bigIntString = await bigIntInstance.toString();
|
||||
|
||||
const bigNumber = BigNumber.from(__getString(bigIntString));
|
||||
@@ -310,10 +311,10 @@ export const instantiate = async (
|
||||
const bigDecimalInstance = BigDecimal.wrap(bigDecimal);
|
||||
|
||||
const digitsPtr = await bigDecimalInstance.digits;
|
||||
const digitsBigInt = BigInt.wrap(digitsPtr);
|
||||
const digitsBigInt = ASBigInt.wrap(digitsPtr);
|
||||
|
||||
const expPtr = await bigDecimalInstance.exp;
|
||||
const expBigInt = BigInt.wrap(expPtr);
|
||||
const expBigInt = ASBigInt.wrap(expPtr);
|
||||
|
||||
const digitsStringPtr = await digitsBigInt.toString();
|
||||
const digits = __getString(digitsStringPtr);
|
||||
@@ -339,11 +340,11 @@ export const instantiate = async (
|
||||
const digitsBigNumber = BigNumber.from(digits);
|
||||
const signBigNumber = BigNumber.from(decimal.value.s);
|
||||
const digitsStringPtr = await __newString(digitsBigNumber.mul(signBigNumber).toString());
|
||||
const digitsBigInt = await BigInt.fromString(digitsStringPtr);
|
||||
const digitsBigInt = await ASBigInt.fromString(digitsStringPtr);
|
||||
|
||||
// Create an exp BigInt.
|
||||
const expStringPtr = await __newString(exp.toString());
|
||||
const expBigInt = await BigInt.fromString(expStringPtr);
|
||||
const expBigInt = await ASBigInt.fromString(expStringPtr);
|
||||
|
||||
// Create a BigDecimal using digits and exp BigInts.
|
||||
const bigDecimal = await BigDecimal.__new(digitsBigInt);
|
||||
@@ -429,73 +430,73 @@ export const instantiate = async (
|
||||
|
||||
const uint8ArrayId = await getIdOfType(TypeId.Uint8Array);
|
||||
const ptr = await __newArray(uint8ArrayId, bytes);
|
||||
const bigInt = await BigInt.fromSignedBytes(ptr);
|
||||
const bigInt = await ASBigInt.fromSignedBytes(ptr);
|
||||
|
||||
return bigInt;
|
||||
},
|
||||
'bigInt.plus': async (x: number, y: number) => {
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
const yBigInt = await BigInt.wrap(y);
|
||||
const yBigInt = await ASBigInt.wrap(y);
|
||||
const yStringPtr = await yBigInt.toString();
|
||||
const yBigNumber = BigNumber.from(__getString(yStringPtr));
|
||||
|
||||
const sum = xBigNumber.add(yBigNumber);
|
||||
const ptr = await __newString(sum.toString());
|
||||
const sumBigInt = await BigInt.fromString(ptr);
|
||||
const sumBigInt = await ASBigInt.fromString(ptr);
|
||||
|
||||
return sumBigInt;
|
||||
},
|
||||
'bigInt.minus': async (x: number, y: number) => {
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
const yBigInt = await BigInt.wrap(y);
|
||||
const yBigInt = await ASBigInt.wrap(y);
|
||||
const yStringPtr = await yBigInt.toString();
|
||||
const yBigNumber = BigNumber.from(__getString(yStringPtr));
|
||||
|
||||
const diff = xBigNumber.sub(yBigNumber);
|
||||
const ptr = await __newString(diff.toString());
|
||||
const diffBigInt = BigInt.fromString(ptr);
|
||||
const diffBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return diffBigInt;
|
||||
},
|
||||
'bigInt.times': async (x: number, y: number) => {
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
const yBigInt = await BigInt.wrap(y);
|
||||
const yBigInt = await ASBigInt.wrap(y);
|
||||
const yStringPtr = await yBigInt.toString();
|
||||
const yBigNumber = BigNumber.from(__getString(yStringPtr));
|
||||
|
||||
const product = xBigNumber.mul(yBigNumber);
|
||||
const ptr = await __newString(product.toString());
|
||||
const productBigInt = BigInt.fromString(ptr);
|
||||
const productBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return productBigInt;
|
||||
},
|
||||
'bigInt.dividedBy': async (x: number, y: number) => {
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
const yBigInt = await BigInt.wrap(y);
|
||||
const yBigInt = await ASBigInt.wrap(y);
|
||||
const yStringPtr = await yBigInt.toString();
|
||||
const yBigNumber = BigNumber.from(__getString(yStringPtr));
|
||||
|
||||
const quotient = xBigNumber.div(yBigNumber);
|
||||
const ptr = await __newString(quotient.toString());
|
||||
const quotientBigInt = BigInt.fromString(ptr);
|
||||
const quotientBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return quotientBigInt;
|
||||
},
|
||||
'bigInt.dividedByDecimal': async (x: number, y: number) => {
|
||||
// Create a decimal out of bigInt x.
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xDecimal = new GraphDecimal(__getString(xStringPtr));
|
||||
|
||||
@@ -513,94 +514,94 @@ export const instantiate = async (
|
||||
},
|
||||
'bigInt.mod': async (x: number, y: number) => {
|
||||
// Create a bigNumber x.
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
// Create a bigNumber y.
|
||||
const yBigInt = await BigInt.wrap(y);
|
||||
const yBigInt = await ASBigInt.wrap(y);
|
||||
const yStringPtr = await yBigInt.toString();
|
||||
const yBigNumber = BigNumber.from(__getString(yStringPtr));
|
||||
|
||||
// Perform the bigNumber mod operation.
|
||||
const remainder = xBigNumber.mod(yBigNumber);
|
||||
const ptr = await __newString(remainder.toString());
|
||||
const remainderBigInt = BigInt.fromString(ptr);
|
||||
const remainderBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return remainderBigInt;
|
||||
},
|
||||
'bigInt.bitOr': async (x: number, y: number) => {
|
||||
// Create a bigNumber x.
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
// Create a bigNumber y.
|
||||
const yBigInt = await BigInt.wrap(y);
|
||||
const yBigInt = await ASBigInt.wrap(y);
|
||||
const yStringPtr = await yBigInt.toString();
|
||||
const yBigNumber = BigNumber.from(__getString(yStringPtr));
|
||||
|
||||
// Perform the bigNumber bit or operation.
|
||||
const res = xBigNumber.or(yBigNumber);
|
||||
const ptr = await __newString(res.toString());
|
||||
const resBigInt = BigInt.fromString(ptr);
|
||||
const resBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return resBigInt;
|
||||
},
|
||||
'bigInt.bitAnd': async (x: number, y: number) => {
|
||||
// Create a bigNumber x.
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
// Create a bigNumber y.
|
||||
const yBigInt = await BigInt.wrap(y);
|
||||
const yBigInt = await ASBigInt.wrap(y);
|
||||
const yStringPtr = await yBigInt.toString();
|
||||
const yBigNumber = BigNumber.from(__getString(yStringPtr));
|
||||
|
||||
// Perform the bigNumber bit and operation.
|
||||
const res = xBigNumber.and(yBigNumber);
|
||||
const ptr = await __newString(res.toString());
|
||||
const resBigInt = BigInt.fromString(ptr);
|
||||
const resBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return resBigInt;
|
||||
},
|
||||
'bigInt.leftShift': async (x: number, y: number) => {
|
||||
// Create a bigNumber x.
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
// Perform the bigNumber left shift operation.
|
||||
const res = xBigNumber.shl(y);
|
||||
const ptr = await __newString(res.toString());
|
||||
const resBigInt = BigInt.fromString(ptr);
|
||||
const resBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return resBigInt;
|
||||
},
|
||||
'bigInt.rightShift': async (x: number, y: number) => {
|
||||
// Create a bigNumber x.
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
// Perform the bigNumber right shift operation.
|
||||
const res = xBigNumber.shr(y);
|
||||
const ptr = await __newString(res.toString());
|
||||
const resBigInt = BigInt.fromString(ptr);
|
||||
const resBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return resBigInt;
|
||||
},
|
||||
'bigInt.pow': async (x: number, y: number) => {
|
||||
// Create a bigNumber x.
|
||||
const xBigInt = await BigInt.wrap(x);
|
||||
const xBigInt = await ASBigInt.wrap(x);
|
||||
const xStringPtr = await xBigInt.toString();
|
||||
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
||||
|
||||
// Perform the bigNumber pow operation.
|
||||
const res = xBigNumber.pow(y);
|
||||
const ptr = await __newString(res.toString());
|
||||
const resBigInt = BigInt.fromString(ptr);
|
||||
const resBigInt = ASBigInt.fromString(ptr);
|
||||
|
||||
return resBigInt;
|
||||
}
|
||||
@@ -621,6 +622,37 @@ export const instantiate = async (
|
||||
assert(dataSource);
|
||||
return __newString(dataSource.network);
|
||||
}
|
||||
},
|
||||
json: {
|
||||
'json.fromBytes': async (bytes: number) => {
|
||||
return jsonFromBytes(instanceExports, bytes);
|
||||
},
|
||||
'json.try_fromBytes': async (bytes: number) => {
|
||||
try {
|
||||
const jsonValue = await jsonFromBytes(instanceExports, bytes);
|
||||
|
||||
return JSONResult.__new(jsonValue);
|
||||
} catch (error) {
|
||||
log('json.try_fromBytes error', error);
|
||||
|
||||
return JSONResult.__new(null);
|
||||
}
|
||||
},
|
||||
// TODO: Number methods do not work as 64bit values are not supported in js.
|
||||
// Tried solution in https://github.com/AssemblyScript/assemblyscript/issues/117#issuecomment-531556954
|
||||
'json.toI64': async (decimal: number) => {
|
||||
return BigInt(__getString(decimal));
|
||||
},
|
||||
'json.toU64': async (decimal: number) => {
|
||||
return BigInt(__getString(decimal));
|
||||
},
|
||||
'json.toF64': async (decimal: number) => {
|
||||
return BigInt(__getString(decimal));
|
||||
},
|
||||
// TODO: Debug toBigInt not working.
|
||||
'json.toBigInt': async (decimal: number) => {
|
||||
return ASBigInt.fromString(decimal);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -632,11 +664,12 @@ export const instantiate = async (
|
||||
// TODO: Assign from types file generated by graph-cli
|
||||
const getIdOfType: idOfType = instanceExports.id_of_type as idOfType;
|
||||
const BigDecimal: any = instanceExports.BigDecimal as any;
|
||||
const BigInt: any = instanceExports.BigInt as any;
|
||||
const ASBigInt: any = instanceExports.BigInt as any;
|
||||
const Address: any = instanceExports.Address as any;
|
||||
const ethereum: any = instanceExports.ethereum as any;
|
||||
const Entity: any = instanceExports.Entity as any;
|
||||
const ByteArray: any = instanceExports.ByteArray as any;
|
||||
const JSONResult: any = instanceExports.JSONResult as any;
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
// Enum types from @graphprotocol/graph-ts.
|
||||
|
||||
export enum TypeId {
|
||||
String = 0,
|
||||
ArrayBuffer = 1,
|
||||
@@ -89,3 +90,12 @@ export enum Level {
|
||||
INFO = 3,
|
||||
DEBUG = 4,
|
||||
}
|
||||
|
||||
export enum JSONValueKind {
|
||||
NULL = 0,
|
||||
BOOL = 1,
|
||||
NUMBER = 2,
|
||||
STRING = 3,
|
||||
ARRAY = 4,
|
||||
OBJECT = 5,
|
||||
}
|
||||
|
||||
@@ -548,7 +548,7 @@ const parseEntityValue = async (instanceExports: any, valuePtr: number) => {
|
||||
const {
|
||||
__getString,
|
||||
__getArray,
|
||||
BigInt: ExportBigInt,
|
||||
BigInt: ASBigInt,
|
||||
Bytes,
|
||||
BigDecimal,
|
||||
Value
|
||||
@@ -583,7 +583,7 @@ const parseEntityValue = async (instanceExports: any, valuePtr: number) => {
|
||||
|
||||
case ValueKind.BIGINT: {
|
||||
const bigIntPtr = await value.toBigInt();
|
||||
const bigInt = ExportBigInt.wrap(bigIntPtr);
|
||||
const bigInt = ASBigInt.wrap(bigIntPtr);
|
||||
const bigIntStringPtr = await bigInt.toString();
|
||||
const bigIntString = __getString(bigIntStringPtr);
|
||||
|
||||
@@ -616,7 +616,7 @@ const parseEntityValue = async (instanceExports: any, valuePtr: number) => {
|
||||
};
|
||||
|
||||
const formatEntityValue = async (instanceExports: any, subgraphValue: any, type: string, value: any, isArray: boolean): Promise<any> => {
|
||||
const { __newString, __newArray, BigInt: ExportBigInt, Value, ByteArray, Bytes, BigDecimal, id_of_type: getIdOfType } = instanceExports;
|
||||
const { __newString, __newArray, BigInt: ASBigInt, Value, ByteArray, Bytes, BigDecimal, id_of_type: getIdOfType } = instanceExports;
|
||||
|
||||
if (isArray) {
|
||||
const dataArrayPromises = value.map((el: any) => formatEntityValue(instanceExports, subgraphValue, type, el, false));
|
||||
@@ -645,7 +645,7 @@ const formatEntityValue = async (instanceExports: any, subgraphValue: any, type:
|
||||
|
||||
case 'BigInt': {
|
||||
const valueStringPtr = await __newString(value.toString());
|
||||
const bigInt = await ExportBigInt.fromString(valueStringPtr);
|
||||
const bigInt = await ASBigInt.fromString(valueStringPtr);
|
||||
|
||||
return Value.fromBigInt(bigInt);
|
||||
}
|
||||
@@ -695,3 +695,61 @@ export const resolveEntityFieldConflicts = (entity: any): any => {
|
||||
|
||||
return entity;
|
||||
};
|
||||
|
||||
export const toJSONValue = async (instanceExports: any, value: any): Promise<any> => {
|
||||
const { CustomJSONValue, JSONValueTypedMap, __newString, __newArray, id_of_type: getIdOfType } = instanceExports;
|
||||
|
||||
if (!value) {
|
||||
return CustomJSONValue.fromNull();
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const arrayPromise = value.map(async (el: any) => toJSONValue(instanceExports, el));
|
||||
const array = await Promise.all(arrayPromise);
|
||||
const arrayJsonValueId = await getIdOfType(TypeId.ArrayJsonValue);
|
||||
const arrayPtr = __newArray(arrayJsonValueId, array);
|
||||
|
||||
return CustomJSONValue.fromArray(arrayPtr);
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
const map = await JSONValueTypedMap.__new();
|
||||
|
||||
const valuePromises = Object.entries(value).map(async ([key, value]) => {
|
||||
const valuePtr = await toJSONValue(instanceExports, value);
|
||||
const keyPtr = await __newString(key);
|
||||
await map.set(keyPtr, valuePtr);
|
||||
});
|
||||
|
||||
await Promise.all(valuePromises);
|
||||
|
||||
return CustomJSONValue.fromObject(map);
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
const stringPtr = await __newString(value);
|
||||
|
||||
return CustomJSONValue.fromString(stringPtr);
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
const stringPtr = await __newString(value.toString());
|
||||
|
||||
return CustomJSONValue.fromNumber(stringPtr);
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
return CustomJSONValue.fromBoolean(value);
|
||||
}
|
||||
};
|
||||
|
||||
export const jsonFromBytes = async (instanceExports: any, bytesPtr: number): Promise<any> => {
|
||||
const { ByteArray, __getString } = instanceExports;
|
||||
|
||||
const byteArray = await ByteArray.wrap(bytesPtr);
|
||||
const jsonStringPtr = await byteArray.toString();
|
||||
const json = JSON.parse(__getString(jsonStringPtr));
|
||||
const jsonValue = await toJSONValue(instanceExports, json);
|
||||
|
||||
return jsonValue;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user