mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-08-10 07:04:07 +00:00
* Instantiate a new wasm string to convert JSON value to BigInt * Implement remaining json host APIs * Upgrade graph-ts and graph-cli in test example subgraph * Handle null context for dataSource context host API * Use JSONBig for parsing JSON strings
803 lines
29 KiB
TypeScript
803 lines
29 KiB
TypeScript
//
|
|
// Copyright 2021 Vulcanize, Inc.
|
|
//
|
|
|
|
import assert from 'assert';
|
|
import fs from 'fs/promises';
|
|
import {
|
|
utils,
|
|
BigNumber,
|
|
Contract,
|
|
ContractInterface
|
|
} from 'ethers';
|
|
import BN from 'bn.js';
|
|
import debug from 'debug';
|
|
|
|
import { BaseProvider } from '@ethersproject/providers';
|
|
import loader from '@cerc-io/assemblyscript/lib/loader';
|
|
import {
|
|
IndexerInterface,
|
|
GraphDecimal,
|
|
getGraphDigitsAndExp,
|
|
prepareEntityState,
|
|
TypeId,
|
|
Level,
|
|
GraphDatabase,
|
|
Block,
|
|
fromEthereumValue,
|
|
toEthereumValue,
|
|
getEthereumTypes,
|
|
jsonFromBytes,
|
|
getStorageValueType
|
|
} from '@cerc-io/util';
|
|
|
|
// Endianness of BN used in bigInt store host API.
|
|
// Negative bigInt is being stored in wasm in 2's compliment, 'le' representation.
|
|
// (for eg. bigInt.fromString(negativeI32Value))
|
|
const BN_ENDIANNESS = 'le';
|
|
|
|
type idOfType = (TypeId: number) => number
|
|
|
|
export interface GraphData {
|
|
abis?: {[key: string]: ContractInterface};
|
|
dataSource: {
|
|
network: string;
|
|
name: string;
|
|
};
|
|
}
|
|
|
|
export interface Context {
|
|
rpcSupportsBlockHashParam: boolean;
|
|
block?: Block;
|
|
contractAddress?: string;
|
|
}
|
|
|
|
const log = debug('vulcanize:graph-node');
|
|
|
|
export const instantiate = async (
|
|
database: GraphDatabase,
|
|
indexer: IndexerInterface,
|
|
provider: BaseProvider,
|
|
context: Context,
|
|
filePathOrModule: string | WebAssembly.Module,
|
|
data: GraphData
|
|
): Promise<loader.ResultObject & { exports: any }> => {
|
|
const { abis = {}, dataSource } = data;
|
|
|
|
let source = filePathOrModule;
|
|
|
|
if (!(filePathOrModule instanceof WebAssembly.Module)) {
|
|
source = await fs.readFile(filePathOrModule);
|
|
}
|
|
|
|
const imports: WebAssembly.Imports = {
|
|
index: {
|
|
'store.get': async (entity: number, id: number) => {
|
|
const entityName = __getString(entity);
|
|
const entityId = __getString(id);
|
|
|
|
assert(context.block);
|
|
const entityData = await database.getEntity(entityName, entityId, context.block.blockHash);
|
|
|
|
if (!entityData || entityData.isRemoved) {
|
|
return null;
|
|
}
|
|
|
|
assert(indexer.getEntityTypesMap);
|
|
const entityTypesMap = indexer.getEntityTypesMap();
|
|
|
|
const entityTypes = entityTypesMap.get(entityName);
|
|
assert(entityTypes);
|
|
|
|
return database.toGraphEntity(instanceExports, entityName, entityData, entityTypes);
|
|
},
|
|
'store.set': async (entity: number, id: number, data: number) => {
|
|
const entityName = __getString(entity);
|
|
|
|
const entityInstance = await Entity.wrap(data);
|
|
|
|
assert(context.block);
|
|
const dbData = await database.fromGraphEntity(instanceExports, context.block, entityName, entityInstance);
|
|
const dbEntity = await database.saveEntity(entityName, dbData);
|
|
database.cacheUpdatedEntityByName(entityName, dbEntity);
|
|
|
|
// Update the in-memory subgraph state if enabled
|
|
if (indexer.serverConfig.enableState) {
|
|
// Prepare diff data for the entity update
|
|
assert(indexer.getRelationsMap);
|
|
const diffData = prepareEntityState(dbData, entityName, indexer.getRelationsMap());
|
|
|
|
assert(indexer.updateSubgraphState);
|
|
assert(context.contractAddress);
|
|
indexer.updateSubgraphState(context.contractAddress, diffData);
|
|
}
|
|
},
|
|
'store.remove': async (entity: number, id: number) => {
|
|
const entityName = __getString(entity);
|
|
const entityId = __getString(id);
|
|
|
|
assert(context.block);
|
|
const entityData = await database.getEntity(entityName, entityId, context.block.blockHash);
|
|
|
|
if (!entityData || entityData.isRemoved) {
|
|
return;
|
|
}
|
|
|
|
// Add an additional entry at block with isRemoved set to true
|
|
entityData.blockHash = context.block.blockHash;
|
|
entityData.blockNumber = context.block.blockNumber;
|
|
entityData.isRemoved = true;
|
|
|
|
const dbEntity = await database.saveEntity(entityName, entityData);
|
|
database.cacheUpdatedEntityByName(entityName, dbEntity);
|
|
},
|
|
|
|
'log.log': (level: number, msg: number) => {
|
|
log('log %s | %s', Level[level], __getString(msg));
|
|
},
|
|
|
|
'crypto.keccak256': async (input: number) => {
|
|
const byteArray = await ByteArray.wrap(input);
|
|
const hexStringPtr = await byteArray.toHexString();
|
|
const hexString = __getString(hexStringPtr);
|
|
const keccak256 = utils.keccak256(hexString);
|
|
const keccak256Ptr = await __newString(keccak256);
|
|
|
|
return ByteArray.fromHexString(keccak256Ptr);
|
|
},
|
|
|
|
'test.asyncMethod': async () => {
|
|
console.log('before timer start');
|
|
await new Promise(resolve => {
|
|
setTimeout(() => {
|
|
resolve(1);
|
|
}, 3000);
|
|
});
|
|
console.log('after timer complete');
|
|
|
|
return 123;
|
|
}
|
|
},
|
|
ethereum: {
|
|
'ethereum.call': async (call: number) => {
|
|
const smartContractCall = await ethereum.SmartContractCall.wrap(call);
|
|
|
|
const contractAddressPtr = await smartContractCall.contractAddress;
|
|
const contractAddress = await Address.wrap(contractAddressPtr);
|
|
|
|
const contractNamePtr = await smartContractCall.contractName;
|
|
const contractName = __getString(contractNamePtr);
|
|
|
|
const functionNamePtr = await smartContractCall.functionName;
|
|
const functionName = __getString(functionNamePtr);
|
|
|
|
const functionSignaturePtr = await smartContractCall.functionSignature;
|
|
const functionSignature = __getString(functionSignaturePtr);
|
|
|
|
const functionParamsPtr = await smartContractCall.functionParams;
|
|
let functionParams = __getArray(functionParamsPtr);
|
|
|
|
console.log('ethereum.call functionSignature:', functionSignature);
|
|
|
|
const abi = abis[contractName];
|
|
const contractAddressStringPtr = await contractAddress.toHexString();
|
|
const contract = new Contract(__getString(contractAddressStringPtr), abi, provider);
|
|
|
|
try {
|
|
const functionParamsPromise = functionParams.map(async param => {
|
|
const ethereumValue = await ethereum.Value.wrap(param);
|
|
return fromEthereumValue(instanceExports, ethereumValue);
|
|
});
|
|
|
|
functionParams = await Promise.all(functionParamsPromise);
|
|
|
|
assert(context.block);
|
|
let result: any;
|
|
|
|
// TODO: Check for function overloading.
|
|
console.time(`time:loader#ethereum.call-${functionName}`);
|
|
if (context.rpcSupportsBlockHashParam) {
|
|
result = await contract[functionName](
|
|
...functionParams,
|
|
{ blockTag: context.block.blockHash }
|
|
);
|
|
} else {
|
|
result = await contract[functionName](
|
|
...functionParams,
|
|
{ blockTag: BigNumber.from(context.block.blockNumber).toHexString() }
|
|
);
|
|
}
|
|
console.timeEnd(`time:loader#ethereum.call-${functionName}`);
|
|
|
|
// Using function signature does not work.
|
|
const { outputs } = contract.interface.getFunction(functionName);
|
|
assert(outputs);
|
|
|
|
// If method returns a single value, ethers returns it directly compared to returning multiple values in an array.
|
|
if (outputs.length === 1) {
|
|
// Put result in an array to map with the outputs array from abi.
|
|
result = [result];
|
|
}
|
|
|
|
const resultPtrArrayPromise = outputs.map(
|
|
async (
|
|
output: any,
|
|
index: number
|
|
) => toEthereumValue(
|
|
instanceExports,
|
|
output,
|
|
result[index]
|
|
)
|
|
);
|
|
|
|
const resultPtrArray: any[] = await Promise.all(resultPtrArrayPromise);
|
|
const arrayEthereumValueId = await getIdOfType(TypeId.ArrayEthereumValue);
|
|
const res = await __newArray(arrayEthereumValueId, resultPtrArray);
|
|
|
|
return res;
|
|
} catch (err: any) {
|
|
log('eth_call error', err.message);
|
|
|
|
return null;
|
|
}
|
|
},
|
|
'ethereum.encode': async (token: number) => {
|
|
const ethValue = await ethereum.Value.wrap(token);
|
|
|
|
const data = await fromEthereumValue(instanceExports, ethValue);
|
|
const type = await getEthereumTypes(instanceExports, ethValue);
|
|
|
|
const encoded = utils.defaultAbiCoder.encode([type], [data]);
|
|
const encodedString = await __newString(encoded);
|
|
|
|
return ByteArray.fromHexString(encodedString);
|
|
},
|
|
'ethereum.decode': async (types: number, data: number) => {
|
|
const typesString = __getString(types);
|
|
const byteArray = await ByteArray.wrap(data);
|
|
const bytesHex = await byteArray.toHex();
|
|
const dataString = __getString(bytesHex);
|
|
|
|
const [decoded] = utils.defaultAbiCoder.decode([typesString], dataString);
|
|
|
|
return toEthereumValue(instanceExports, utils.ParamType.from(typesString), decoded);
|
|
},
|
|
'ethereum.storageValue': async (variable: number, mappingKeys: number) => {
|
|
assert(context.contractAddress);
|
|
const addressStringPtr = await __newString(context.contractAddress);
|
|
const addressString = __getString(addressStringPtr);
|
|
|
|
const variableString = __getString(variable);
|
|
const mappingKeyPtrs = __getArray(mappingKeys);
|
|
|
|
const mappingKeyPromises = mappingKeyPtrs.map(async mappingKeyPtr => {
|
|
const ethereumValue = await ethereum.Value.wrap(mappingKeyPtr);
|
|
return fromEthereumValue(instanceExports, ethereumValue);
|
|
});
|
|
|
|
const mappingKeyValues = await Promise.all(mappingKeyPromises);
|
|
const storageLayout = indexer.storageLayoutMap.get(dataSource.name);
|
|
assert(storageLayout);
|
|
assert(context.block);
|
|
|
|
console.time(`time:loader#ethereum.storageValue-${variableString}`);
|
|
const result = await indexer.getStorageValue(
|
|
storageLayout,
|
|
context.block.blockHash,
|
|
addressString,
|
|
variableString,
|
|
...mappingKeyValues
|
|
);
|
|
console.timeEnd(`time:loader#ethereum.storageValue-${variableString}`);
|
|
|
|
const storageValueType = getStorageValueType(storageLayout, variableString, mappingKeyValues);
|
|
|
|
return toEthereumValue(
|
|
instanceExports,
|
|
storageValueType,
|
|
result.value
|
|
);
|
|
}
|
|
},
|
|
conversion: {
|
|
'typeConversion.stringToH160': async (s: number) => {
|
|
const string = __getString(s);
|
|
const address = utils.getAddress(string.trim());
|
|
const byteArray = utils.arrayify(address);
|
|
|
|
const uint8ArrayId = await getIdOfType(TypeId.Uint8Array);
|
|
const ptr = __newArray(uint8ArrayId, byteArray);
|
|
|
|
return ptr;
|
|
},
|
|
|
|
'typeConversion.bigIntToString': (bigInt: number) => {
|
|
const bigIntByteArray = __getArray(bigInt);
|
|
|
|
// Create a BN with 'le' endianness.
|
|
const bigNumber = new BN(bigIntByteArray, BN_ENDIANNESS);
|
|
|
|
// Convert BN from two's compliment and to string.
|
|
const bigNumberString = bigNumber.fromTwos(bigIntByteArray.length * 8).toString();
|
|
|
|
const ptr = __newString(bigNumberString);
|
|
|
|
return ptr;
|
|
},
|
|
'typeConversion.bigIntToHex': async (bigInt: number) => {
|
|
const bigIntInstance = await ASBigInt.wrap(bigInt);
|
|
const bigIntString = await bigIntInstance.toString();
|
|
|
|
const bigNumber = BigNumber.from(__getString(bigIntString));
|
|
const bigNumberHex = bigNumber.toHexString();
|
|
|
|
return __newString(bigNumberHex);
|
|
},
|
|
|
|
'typeConversion.bytesToHex': async (bytes: number) => {
|
|
const byteArray = __getArray(bytes);
|
|
const hexString = utils.hexlify(byteArray);
|
|
const ptr = await __newString(hexString);
|
|
|
|
return ptr;
|
|
},
|
|
'typeConversion.bytesToString': async (bytes: number) => {
|
|
const byteArray = __getArray(bytes);
|
|
const string = utils.toUtf8String(byteArray);
|
|
const ptr = await __newString(string);
|
|
|
|
return ptr;
|
|
},
|
|
'typeConversion.bytesToBase58': async (n: number) => {
|
|
const uint8Array = __getArray(n);
|
|
const string = utils.base58.encode(uint8Array);
|
|
const ptr = await __newString(string);
|
|
|
|
return ptr;
|
|
}
|
|
},
|
|
numbers: {
|
|
'bigDecimal.dividedBy': async (x: number, y: number) => {
|
|
// Creating decimal x.
|
|
const xBigDecimal = await BigDecimal.wrap(x);
|
|
const xStringPtr = await xBigDecimal.toString();
|
|
const xDecimalString = __getString(xStringPtr);
|
|
const xDecimal = new GraphDecimal(xDecimalString);
|
|
|
|
// Create decimal y.
|
|
const yBigDecimal = await BigDecimal.wrap(y);
|
|
const yStringPtr = await yBigDecimal.toString();
|
|
const yDecimalString = __getString(yStringPtr);
|
|
|
|
// Performing the decimal division operation.
|
|
const divResult = xDecimal.dividedBy(yDecimalString);
|
|
const ptr = await __newString(divResult.toString());
|
|
const divResultBigDecimal = await BigDecimal.fromString(ptr);
|
|
|
|
return divResultBigDecimal;
|
|
},
|
|
'bigDecimal.toString': async (bigDecimal: number) => {
|
|
const bigDecimalInstance = BigDecimal.wrap(bigDecimal);
|
|
|
|
const digitsPtr = await bigDecimalInstance.digits;
|
|
const digitsBigInt = ASBigInt.wrap(digitsPtr);
|
|
|
|
const expPtr = await bigDecimalInstance.exp;
|
|
const expBigInt = ASBigInt.wrap(expPtr);
|
|
|
|
const digitsStringPtr = await digitsBigInt.toString();
|
|
const digits = __getString(digitsStringPtr);
|
|
|
|
const expStringPtr = await expBigInt.toString();
|
|
const exp = __getString(expStringPtr);
|
|
|
|
const decimal = new GraphDecimal(`${digits}e${exp}`);
|
|
const ptr = __newString(decimal.toFixed());
|
|
|
|
return ptr;
|
|
},
|
|
'bigDecimal.fromString': async (s: number) => {
|
|
const string = __getString(s);
|
|
|
|
// Creating a decimal using custom decimal implementation.
|
|
const decimal = new GraphDecimal(string);
|
|
|
|
// Get digits string and exp using decimal 'd' and 'e' properties.
|
|
const { digits, exp } = getGraphDigitsAndExp(decimal.value.d, decimal.value.e);
|
|
|
|
// Create a digits BigInt using digits string and decimal sign 's' property.
|
|
const digitsBigNumber = BigNumber.from(digits);
|
|
const signBigNumber = BigNumber.from(decimal.value.s);
|
|
const digitsStringPtr = await __newString(digitsBigNumber.mul(signBigNumber).toString());
|
|
const digitsBigInt = await ASBigInt.fromString(digitsStringPtr);
|
|
|
|
// Create an exp BigInt.
|
|
const expStringPtr = await __newString(exp.toString());
|
|
const expBigInt = await ASBigInt.fromString(expStringPtr);
|
|
|
|
// Create a BigDecimal using digits and exp BigInts.
|
|
const bigDecimal = await BigDecimal.__new(digitsBigInt);
|
|
bigDecimal.exp = expBigInt;
|
|
|
|
return bigDecimal;
|
|
},
|
|
'bigDecimal.plus': async (x: number, y: number) => {
|
|
// Create decimal x string.
|
|
const xBigDecimal = await BigDecimal.wrap(x);
|
|
const xStringPtr = await xBigDecimal.toString();
|
|
const xDecimalString = __getString(xStringPtr);
|
|
const xDecimal = new GraphDecimal(xDecimalString);
|
|
|
|
// Create decimal y string.
|
|
const yBigDecimal = await BigDecimal.wrap(y);
|
|
const yStringPtr = await yBigDecimal.toString();
|
|
const yDecimalString = __getString(yStringPtr);
|
|
|
|
// Perform the decimal plus operation.
|
|
const sumResult = xDecimal.plus(yDecimalString);
|
|
const ptr = await __newString(sumResult.toString());
|
|
const sumResultBigDecimal = await BigDecimal.fromString(ptr);
|
|
|
|
return sumResultBigDecimal;
|
|
},
|
|
'bigDecimal.minus': async (x: number, y: number) => {
|
|
// Create decimal x string.
|
|
const xBigDecimal = await BigDecimal.wrap(x);
|
|
const xStringPtr = await xBigDecimal.toString();
|
|
const xDecimalString = __getString(xStringPtr);
|
|
const xDecimal = new GraphDecimal(xDecimalString);
|
|
|
|
// Create decimal y string.
|
|
const yBigDecimal = await BigDecimal.wrap(y);
|
|
const yStringPtr = await yBigDecimal.toString();
|
|
const yDecimalString = __getString(yStringPtr);
|
|
|
|
// Perform the decimal minus operation.
|
|
const subResult = xDecimal.minus(yDecimalString);
|
|
const ptr = await __newString(subResult.toString());
|
|
const subResultBigDecimal = await BigDecimal.fromString(ptr);
|
|
|
|
return subResultBigDecimal;
|
|
},
|
|
'bigDecimal.times': async (x: number, y: number) => {
|
|
// Create decimal x string.
|
|
const xBigDecimal = await BigDecimal.wrap(x);
|
|
const xStringPtr = await xBigDecimal.toString();
|
|
const xDecimalString = __getString(xStringPtr);
|
|
const xDecimal = new GraphDecimal(xDecimalString);
|
|
|
|
// Create decimal y string.
|
|
const yBigDecimal = await BigDecimal.wrap(y);
|
|
const yStringPtr = await yBigDecimal.toString();
|
|
const yDecimalString = __getString(yStringPtr);
|
|
|
|
// Perform the decimal times operation.
|
|
const mulResult = xDecimal.times(yDecimalString);
|
|
const ptr = await __newString(mulResult.toString());
|
|
const mulResultBigDecimal = await BigDecimal.fromString(ptr);
|
|
|
|
return mulResultBigDecimal;
|
|
},
|
|
'bigDecimal.pow': async (x: number, y: number) => {
|
|
// Create decimal x string.
|
|
const xBigDecimal = await BigDecimal.wrap(x);
|
|
const xStringPtr = await xBigDecimal.toString();
|
|
const xDecimalString = __getString(xStringPtr);
|
|
const xDecimal = new GraphDecimal(xDecimalString);
|
|
|
|
// Create decimal y string.
|
|
const yBigDecimal = await BigDecimal.wrap(y);
|
|
const yStringPtr = await yBigDecimal.toString();
|
|
const yDecimalString = __getString(yStringPtr);
|
|
|
|
// Perform the decimal pow operation.
|
|
const powResult = xDecimal.pow(yDecimalString);
|
|
const ptr = await __newString(powResult.toString());
|
|
const powResultBigDecimal = await BigDecimal.fromString(ptr);
|
|
|
|
return powResultBigDecimal;
|
|
},
|
|
'bigDecimal.equals': async (x: number, y: number) => {
|
|
// Create decimal x string.
|
|
const xBigDecimal = await BigDecimal.wrap(x);
|
|
const xStringPtr = await xBigDecimal.toString();
|
|
const xDecimalString = __getString(xStringPtr);
|
|
const xDecimal = new GraphDecimal(xDecimalString);
|
|
|
|
// Create decimal y string.
|
|
const yBigDecimal = await BigDecimal.wrap(y);
|
|
const yStringPtr = await yBigDecimal.toString();
|
|
const yDecimalString = __getString(yStringPtr);
|
|
|
|
// Perform the decimal equal operation.
|
|
const isEqual = xDecimal.equals(yDecimalString);
|
|
|
|
return isEqual;
|
|
},
|
|
'bigInt.fromString': async (s: number) => {
|
|
const string = __getString(s);
|
|
|
|
// The BN is being stored as a byte array in wasm memory in 2's compliment representation and interpreted as such in other APIs.
|
|
// Create a BN in 2's compliment representation.
|
|
// Need to use BN as ethers.BigNumber:
|
|
// Doesn't store -ve numbers in 2's compilment form
|
|
// Stores in big endian form.
|
|
let bigNumber = new BN(string);
|
|
|
|
// Size (in bytes) of the BN stored.
|
|
// Add an extra byte to the BNs byte length to allow for 2's compiment.
|
|
const bnSize = bigNumber.byteLength() + 1;
|
|
bigNumber = bigNumber.toTwos(bnSize * 8);
|
|
|
|
// Create a byte array out of BN in 'le' endianness.
|
|
const bytes = bigNumber.toArray(BN_ENDIANNESS, bnSize);
|
|
|
|
const uint8ArrayId = await getIdOfType(TypeId.Uint8Array);
|
|
const ptr = await __newArray(uint8ArrayId, bytes);
|
|
const bigInt = await ASBigInt.fromSignedBytes(ptr);
|
|
|
|
return bigInt;
|
|
},
|
|
'bigInt.plus': async (x: number, y: number) => {
|
|
const xBigInt = await ASBigInt.wrap(x);
|
|
const xStringPtr = await xBigInt.toString();
|
|
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
|
|
|
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 ASBigInt.fromString(ptr);
|
|
|
|
return sumBigInt;
|
|
},
|
|
'bigInt.minus': async (x: number, y: number) => {
|
|
const xBigInt = await ASBigInt.wrap(x);
|
|
const xStringPtr = await xBigInt.toString();
|
|
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
|
|
|
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 = ASBigInt.fromString(ptr);
|
|
|
|
return diffBigInt;
|
|
},
|
|
'bigInt.times': async (x: number, y: number) => {
|
|
const xBigInt = await ASBigInt.wrap(x);
|
|
const xStringPtr = await xBigInt.toString();
|
|
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
|
|
|
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 = ASBigInt.fromString(ptr);
|
|
|
|
return productBigInt;
|
|
},
|
|
'bigInt.dividedBy': async (x: number, y: number) => {
|
|
const xBigInt = await ASBigInt.wrap(x);
|
|
const xStringPtr = await xBigInt.toString();
|
|
const xBigNumber = BigNumber.from(__getString(xStringPtr));
|
|
|
|
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 = ASBigInt.fromString(ptr);
|
|
|
|
return quotientBigInt;
|
|
},
|
|
'bigInt.dividedByDecimal': async (x: number, y: number) => {
|
|
// Create a decimal out of bigInt x.
|
|
const xBigInt = await ASBigInt.wrap(x);
|
|
const xStringPtr = await xBigInt.toString();
|
|
const xDecimal = new GraphDecimal(__getString(xStringPtr));
|
|
|
|
// Create decimal y.
|
|
const yBigDecimal = await BigDecimal.wrap(y);
|
|
const yStringPtr = await yBigDecimal.toString();
|
|
const yDecimal = new GraphDecimal(__getString(yStringPtr));
|
|
|
|
// Perform the decimal division operation.
|
|
const divResult = xDecimal.dividedBy(yDecimal);
|
|
const ptr = await __newString(divResult.toString());
|
|
const divResultBigDecimal = await BigDecimal.fromString(ptr);
|
|
|
|
return divResultBigDecimal;
|
|
},
|
|
'bigInt.mod': async (x: number, y: number) => {
|
|
// Create a bigNumber 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 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 = ASBigInt.fromString(ptr);
|
|
|
|
return remainderBigInt;
|
|
},
|
|
'bigInt.bitOr': async (x: number, y: number) => {
|
|
// Create a bigNumber 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 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 = ASBigInt.fromString(ptr);
|
|
|
|
return resBigInt;
|
|
},
|
|
'bigInt.bitAnd': async (x: number, y: number) => {
|
|
// Create a bigNumber 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 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 = ASBigInt.fromString(ptr);
|
|
|
|
return resBigInt;
|
|
},
|
|
'bigInt.leftShift': async (x: number, y: number) => {
|
|
// Create a bigNumber 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 = ASBigInt.fromString(ptr);
|
|
|
|
return resBigInt;
|
|
},
|
|
'bigInt.rightShift': async (x: number, y: number) => {
|
|
// Create a bigNumber 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 = ASBigInt.fromString(ptr);
|
|
|
|
return resBigInt;
|
|
},
|
|
'bigInt.pow': async (x: number, y: number) => {
|
|
// Create a bigNumber 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 = ASBigInt.fromString(ptr);
|
|
|
|
return resBigInt;
|
|
}
|
|
},
|
|
datasource: {
|
|
'dataSource.address': async () => {
|
|
assert(context.contractAddress);
|
|
const addressStringPtr = await __newString(context.contractAddress);
|
|
return Address.fromString(addressStringPtr);
|
|
},
|
|
'dataSource.context': async () => {
|
|
assert(context.contractAddress);
|
|
const contract = indexer.isWatchedContract(context.contractAddress);
|
|
|
|
if (!contract) {
|
|
return null;
|
|
}
|
|
|
|
return database.toGraphContext(instanceExports, contract.context);
|
|
},
|
|
'dataSource.network': async () => {
|
|
assert(dataSource);
|
|
return __newString(dataSource.network);
|
|
},
|
|
'dataSource.create': async (name: number, params: number) => {
|
|
const [addressStringPtr] = __getArray(params);
|
|
const addressString = __getString(addressStringPtr);
|
|
const contractKind = __getString(name);
|
|
|
|
assert(indexer.watchContract);
|
|
assert(context.block);
|
|
await indexer.watchContract(utils.getAddress(addressString), contractKind, true, Number(context.block.blockNumber));
|
|
},
|
|
'dataSource.createWithContext': async (name: number, params: number, dataSourceContext: number) => {
|
|
const [addressStringPtr] = __getArray(params);
|
|
const addressString = __getString(addressStringPtr);
|
|
const contractKind = __getString(name);
|
|
|
|
const contextInstance = await Entity.wrap(dataSourceContext);
|
|
const dbData = await database.fromGraphContext(instanceExports, contextInstance);
|
|
|
|
assert(indexer.watchContract);
|
|
assert(context.block);
|
|
await indexer.watchContract(utils.getAddress(addressString), contractKind, true, Number(context.block.blockNumber), dbData);
|
|
}
|
|
},
|
|
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);
|
|
}
|
|
},
|
|
'json.toI64': (decimal: number) => {
|
|
return BigInt(__getString(decimal));
|
|
},
|
|
'json.toU64': (decimal: number) => {
|
|
return BigInt(__getString(decimal));
|
|
},
|
|
'json.toF64': (decimal: number) => {
|
|
return Number(__getString(decimal));
|
|
},
|
|
'json.toBigInt': async (decimal: number) => {
|
|
const ptr = await __newString(__getString(decimal));
|
|
return ASBigInt.fromString(ptr);
|
|
}
|
|
}
|
|
};
|
|
|
|
const instance = await loader.instantiate(source, imports);
|
|
const { exports: instanceExports } = instance;
|
|
|
|
const { __getString, __newString, __getArray, __newArray } = instanceExports;
|
|
|
|
// 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 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;
|
|
};
|