AssemblyScript API
Reading time: 21 min
Note: if you created a subgraph prior to graph-cli
/graph-ts
version 0.22.0
, you're using an older version of AssemblyScript, we recommend taking a look at the
Esta página documenta qué API integradas se pueden usar al escribir mappings de subgrafos. Hay dos tipos de API disponibles listas para usar:
It is also possible to add other libraries as dependencies, as long as they are compatible with . Since this is the language mappings are written in, the is a good source for language and standard library features.
The @graphprotocol/graph-ts
library provides the following APIs:
- An
ethereum
API for working with Ethereum smart contracts, events, blocks, transactions, and Ethereum values. - A
store
API to load and save entities from and to the Graph Node store. - A
log
API to log messages to the Graph Node output and Graph Explorer. - An
ipfs
API to load files from IPFS. - A
json
API to parse JSON data. - A
crypto
API to use cryptographic functions. - Primitivas de bajo nivel para traducir entre los distintos tipos de sistemas, tales como, Ethereum, JSON, GraphQL y AssemblyScript.
The apiVersion
in the subgraph manifest specifies the mapping API version which is run by Graph Node for a given subgraph.
Documentation on the base types built into AssemblyScript can be found in the .
The following additional types are provided by @graphprotocol/graph-ts
.
import { ByteArray } from '@graphprotocol/graph-ts'
ByteArray
represents an array of u8
.
Construction
fromI32(x: i32): ByteArray
- Decomposesx
into bytes.fromHexString(hex: string): ByteArray
- Input length must be even. Prefixing with0x
is optional.
Type conversions
toHexString(): string
- Converts to a hex string prefixed with0x
.toString(): string
- Interprets the bytes as a UTF-8 string.toBase58(): string
- Encodes the bytes into a base58 string.toU32(): u32
- Interprets the bytes as a little-endianu32
. Throws in case of overflow.toI32(): i32
- Interprets the byte array as a little-endiani32
. Throws in case of overflow.
Operators
equals(y: ByteArray): bool
– can be written asx == y
.concat(other: ByteArray) : ByteArray
- return a newByteArray
consisting ofthis
directly followed byother
concatI32(other: i32) : ByteArray
- return a newByteArray
consisting ofthis
directly followed by the byte representation ofother
import { BigDecimal } from '@graphprotocol/graph-ts'
BigDecimal
is used to represent arbitrary precision decimals.
Note: BigDecimal
is stored in , which supports 34 decimal digits of significand. This makes BigDecimal
unsuitable for representing fixed-point types that can span wider than 34 digits, such as a Solidity or equivalent.
Construction
constructor(bigInt: BigInt)
– creates aBigDecimal
from anBigInt
.static fromString(s: string): BigDecimal
– parses from a decimal string.
Type conversions
toString(): string
– prints to a decimal string.
Math
plus(y: BigDecimal): BigDecimal
– can be written asx + y
.minus(y: BigDecimal): BigDecimal
– can be written asx - y
.times(y: BigDecimal): BigDecimal
– can be written asx * y
.div(y: BigDecimal): BigDecimal
– can be written asx / y
.equals(y: BigDecimal): bool
– can be written asx == y
.notEqual(y: BigDecimal): bool
– can be written asx != y
.lt(y: BigDecimal): bool
– can be written asx < y
.le(y: BigDecimal): bool
– can be written asx <= y
.gt(y: BigDecimal): bool
– can be written asx > y
.ge(y: BigDecimal): bool
– can be written asx >= y
.neg(): BigDecimal
- can be written as-x
.
import { BigInt } from '@graphprotocol/graph-ts'
BigInt
is used to represent big integers. This includes Ethereum values of type uint32
to uint256
and int64
to int256
. Everything below uint32
, such as int32
, uint24
or int8
is represented as i32
.
The BigInt
class has the following API:
Construction
-
BigInt.fromI32(x: i32): BigInt
– creates aBigInt
from ani32
. -
BigInt.fromString(s: string): BigInt
– Parses aBigInt
from a string. -
BigInt.fromUnsignedBytes(x: Bytes): BigInt
– Interpretsbytes
as an unsigned, little-endian integer. If your input is big-endian, call.reverse()
first. -
BigInt.fromSignedBytes(x: Bytes): BigInt
– Interpretsbytes
as a signed, little-endian integer. If your input is big-endian, call.reverse()
first.Type conversions
-
x.toHex(): string
– turnsBigInt
into a string of hexadecimal characters. -
x.toString(): string
– turnsBigInt
into a decimal number string. -
x.toI32(): i32
– returns theBigInt
as ani32
; fails if the value does not fit intoi32
. It's a good idea to first checkx.isI32()
. -
x.toBigDecimal(): BigDecimal
- converts into a decimal with no fractional part.
Math
x.plus(y: BigInt): BigInt
– can be written asx + y
.x.minus(y: BigInt): BigInt
– can be written asx - y
.x.times(y: BigInt): BigInt
– can be written asx * y
.x.div(y: BigInt): BigInt
– can be written asx / y
.x.mod(y: BigInt): BigInt
– can be written asx % y
.x.equals(y: BigInt): bool
– can be written asx == y
.x.notEqual(y: BigInt): bool
– can be written asx != y
.x.lt(y: BigInt): bool
– can be written asx < y
.x.le(y: BigInt): bool
– can be written asx <= y
.x.gt(y: BigInt): bool
– can be written asx > y
.x.ge(y: BigInt): bool
– can be written asx >= y
.x.neg(): BigInt
– can be written as-x
.x.divDecimal(y: BigDecimal): BigDecimal
– divides by a decimal, giving a decimal result.x.isZero(): bool
– Convenience for checking if the number is zero.x.isI32(): bool
– Check if the number fits in ani32
.x.abs(): BigInt
– Absolute value.x.pow(exp: u8): BigInt
– Exponentiation.bitOr(x: BigInt, y: BigInt): BigInt
– can be written asx | y
.bitAnd(x: BigInt, y: BigInt): BigInt
– can be written asx & y
.leftShift(x: BigInt, bits: u8): BigInt
– can be written asx << y
.rightShift(x: BigInt, bits: u8): BigInt
– can be written asx >> y
.
import { TypedMap } from '@graphprotocol/graph-ts'
TypedMap
can be used to store key-value pairs. See .
The TypedMap
class has the following API:
new TypedMap<K, V>()
– creates an empty map with keys of typeK
and values of typeV
map.set(key: K, value: V): void
– sets the value ofkey
tovalue
map.getEntry(key: K): TypedMapEntry<K, V> | null
– returns the key-value pair for akey
ornull
if thekey
does not exist in the mapmap.get(key: K): V | null
– returns the value for akey
ornull
if thekey
does not exist in the mapmap.isSet(key: K): bool
– returnstrue
if thekey
exists in the map andfalse
if it does not
import { Bytes } from '@graphprotocol/graph-ts'
Bytes
is used to represent arbitrary-length arrays of bytes. This includes Ethereum values of type bytes
, bytes32
, etc.
The Bytes
class extends AssemblyScript's and this supports all the Uint8Array
functionality, plus the following new methods:
Construction
fromHexString(hex: string) : Bytes
- Convert the stringhex
which must consist of an even number of hexadecimal digits to aByteArray
. The stringhex
can optionally start with0x
fromI32(i: i32) : Bytes
- Converti
to an array of bytes
Type conversions
b.toHex()
– returns a hexadecimal string representing the bytes in the arrayb.toString()
– converts the bytes in the array to a string of unicode charactersb.toBase58()
– turns an Ethereum Bytes value to base58 encoding (used for IPFS hashes)
Operators
b.concat(other: Bytes) : Bytes
- - return newBytes
consisting ofthis
directly followed byother
b.concatI32(other: i32) : ByteArray
- return newBytes
consisting ofthis
directly follow by the byte representation ofother
import { Address } from '@graphprotocol/graph-ts'
Address
extends Bytes
to represent Ethereum address
values.
It adds the following method on top of the Bytes
API:
Address.fromString(s: string): Address
– creates anAddress
from a hexadecimal stringAddress.fromBytes(b: Bytes): Address
– create anAddress
fromb
which must be exactly 20 bytes long. Passing in a value with fewer or more bytes will result in an error
import { store } from '@graphprotocol/graph-ts'
The store
API allows to load, save and remove entities from and to the Graph Node store.
Entities written to the store map one-to-one to the @entity
types defined in the subgraph's GraphQL schema. To make working with these entities convenient, the graph codegen
command provided by the generates entity classes, which are subclasses of the built-in Entity
type, with property getters and setters for the fields in the schema as well as methods to load and save these entities.
El siguiente es un patrón común para crear entidades a partir de eventos de Ethereum.
// Import the Transfer event class generated from the ERC20 ABIimport { Transfer as TransferEvent } from '../generated/ERC20/ERC20'// Import the Transfer entity type generated from the GraphQL schemaimport { Transfer } from '../generated/schema'// Transfer event handlerexport function handleTransfer(event: TransferEvent): void {// Create a Transfer entity, using the transaction hash as the entity IDlet id = event.transaction.hashlet transfer = new Transfer(id)// Set properties on the entity, using the event parameterstransfer.from = event.params.fromtransfer.to = event.params.totransfer.amount = event.params.amount// Save the entity to the storetransfer.save()}
When a Transfer
event is encountered while processing the chain, it is passed to the handleTransfer
event handler using the generated Transfer
type (aliased to TransferEvent
here to avoid a naming conflict with the entity type). This type allows accessing data such as the event's parent transaction and its parameters.
Cada entidad debe tener un identificador único para evitar colisiones con otras entidades. Es bastante común que los parámetros de los eventos incluyan un identificador único que pueda ser utilizado. Nota: El uso del hash de la transacción como ID asume que ningún otro evento en la misma transacción crea entidades con este hash como ID.
Si una entidad ya existe, se puede cargar desde el store con lo siguiente:
let id = event.transaction.hash // or however the ID is constructedlet transfer = Transfer.load(id)if (transfer == null) {transfer = new Transfer(id)}// Use the Transfer entity as before
As the entity may not exist in the store yet, the load
method returns a value of type Transfer | null
. It may thus be necessary to check for the null
case before using the value.
Note: Loading entities is only necessary if the changes made in the mapping depend on the previous data of an entity. See the next section for the two ways of updating existing entities.
As of graph-node
v0.31.0, @graphprotocol/graph-ts
v0.30.0 and @graphprotocol/graph-cli
v0.49.0 the loadInBlock
method is available on all entity types.
La API de almacenamiento facilita la recuperación de entidades que se crearon o actualizaron en el bloque actual. Una situación típica para esto es cuando un handler crea una Transacción a partir de algún evento en la cadena, y un handler posterior quiere acceder a esta transacción si existe. En el caso de que la transacción no exista, el subgrafo tendrá que ir a la base de datos solo para averiguar que la entidad no existe; si el autor del subgrafo ya sabe que la entidad debe haber sido creada en el mismo bloque, el uso de loadInBlock evita este viaje de ida y vuelta a la base de datos. Para algunos subgrafos, estas búsquedas perdidas pueden contribuir significativamente al tiempo de indexación.
let id = event.transaction.hash // or however the ID is constructedlet transfer = Transfer.load(id)if (transfer == null) {transfer = new Transfer(id)}// Usa la identidad de transferencia como se indica arriba
Note: If there is no entity created in the given block, loadInBlock
will return null
even if there is an entity with the given ID in the store.
As of graph-node
v0.31.0, @graphprotocol/graph-ts
v0.31.0 and @graphprotocol/graph-cli
v0.51.0 the loadRelated
method is available.
This enables loading derived entity fields from within an event handler. For example, given the following schema:
type Token @entity {id: ID!holder: Holder!color: String}type Holder @entity {id: ID!tokens: [Token!]! @derivedFrom(field: "holder")}
The following code will load the Token
entity that the Holder
entity was derived from:
let holder = Holder.load('test-id')// Load the Token entities associated with a given holderlet tokens = holder.tokens.load()
Hay dos maneras de actualizar una entidad existente:
- Load the entity with e.g.
Transfer.load(id)
, set properties on the entity, then.save()
it back to the store. - Simply create the entity with e.g.
new Transfer(id)
, set properties on the entity, then.save()
it to the store. If the entity already exists, the changes are merged into it.
Cambiar las propiedades es sencillo en la mayoría de los casos, gracias a los definidores de propiedades generados:
let transfer = new Transfer(id)transfer.from = ...transfer.to = ...transfer.amount = ...
También es posible desajustar las propiedades con una de las dos instrucciones siguientes:
transfer.from.unset()transfer.from = null
This only works with optional properties, i.e. properties that are declared without a !
in GraphQL. Two examples would be owner: Bytes
or amount: BigInt
.
Updating array properties is a little more involved, as the getting an array from an entity creates a copy of that array. This means array properties have to be set again explicitly after changing the array. The following assumes entity
has a numbers: [BigInt!]!
field.
// This won't workentity.numbers.push(BigInt.fromI32(1))entity.save()// This will worklet numbers = entity.numbersnumbers.push(BigInt.fromI32(1))entity.numbers = numbersentity.save()
There is currently no way to remove an entity via the generated types. Instead, removing an entity requires passing the name of the entity type and the entity ID to store.remove
:
import { store } from '@graphprotocol/graph-ts'...let id = event.transaction.hashstore.remove('Transfer', id)
La API de Ethereum proporciona acceso a los contratos inteligentes, a las variables de estado públicas, a las funciones de los contratos, a los eventos, a las transacciones, a los bloques y a la codificación/decodificación de los datos de Ethereum.
As with entities, graph codegen
generates classes for all smart contracts and events used in a subgraph. For this, the contract ABIs need to be part of the data source in the subgraph manifest. Typically, the ABI files are stored in an abis/
folder.
With the generated classes, conversions between Ethereum types and the take place behind the scenes so that subgraph authors do not have to worry about them.
El siguiente ejemplo lo ilustra. Dado un esquema de subgrafos como
type Transfer @entity {id: Bytes!from: Bytes!to: Bytes!amount: BigInt!}
and a Transfer(address,address,uint256)
event signature on Ethereum, the from
, to
and amount
values of type address
, address
and uint256
are converted to Address
and BigInt
, allowing them to be passed on to the Bytes!
and BigInt!
properties of the Transfer
entity:
let id = event.transaction.hashlet transfer = new Transfer(id)transfer.from = event.params.fromtransfer.to = event.params.totransfer.amount = event.params.amounttransfer.save()
Ethereum events passed to event handlers, such as the Transfer
event in the previous examples, not only provide access to the event parameters but also to their parent transaction and the block they are part of. The following data can be obtained from event
instances (these classes are a part of the ethereum
module in graph-ts
):
class Event {address: AddresslogIndex: BigInttransactionLogIndex: BigIntlogType: string | nullblock: Blocktransaction: Transactionparameters: Array<EventParam>receipt: TransactionReceipt | null}class Block {hash: BytesparentHash: BytesunclesHash: Bytesauthor: AddressstateRoot: BytestransactionsRoot: BytesreceiptsRoot: Bytesnumber: BigIntgasUsed: BigIntgasLimit: BigInttimestamp: BigIntdifficulty: BigInttotalDifficulty: BigIntsize: BigInt | nullbaseFeePerGas: BigInt | null}class Transaction {hash: Bytesindex: BigIntfrom: Addressto: Address | nullvalue: BigIntgasLimit: BigIntgasPrice: BigIntinput: Bytesnonce: BigInt}class TransactionReceipt {transactionHash: BytestransactionIndex: BigIntblockHash: BytesblockNumber: BigIntcumulativeGasUsed: BigIntgasUsed: BigIntcontractAddress: Addresslogs: Array<Log>status: BigIntroot: ByteslogsBloom: Bytes}class Log {address: Addresstopics: Array<Bytes>data: BytesblockHash: BytesblockNumber: BytestransactionHash: BytestransactionIndex: BigIntlogIndex: BigInttransactionLogIndex: BigIntlogType: stringremoved: bool | null}
The code generated by graph codegen
also includes classes for the smart contracts used in the subgraph. These can be used to access public state variables and call functions of the contract at the current block.
Un patrón común es acceder al contrato desde el que se origina un evento. Esto se consigue con el siguiente código:
// Import the generated contract class and generated Transfer event classimport { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'// Import the generated entity classimport { Transfer } from '../generated/schema'export function handleTransfer(event: TransferEvent) {// Bind the contract to the address that emitted the eventlet contract = ERC20Contract.bind(event.address)// Access state variables and functions by calling themlet erc20Symbol = contract.symbol()}
Transfer
is aliased to TransferEvent
here to avoid a naming conflict with the entity type
As long as the ERC20Contract
on Ethereum has a public read-only function called symbol
, it can be called with .symbol()
. For public state variables a method with the same name is created automatically.
Cualquier otro contrato que forme parte del subgrafo puede ser importado desde el código generado y puede ser vinculado a una dirección válida.
If the read-only methods of your contract may revert, then you should handle that by calling the generated contract method prefixed with try_
. For example, the Gravity contract exposes the gravatarToOwner
method. This code would be able to handle a revert in that method:
let gravity = Gravity.bind(event.address)let callResult = gravity.try_gravatarToOwner(gravatar)if (callResult.reverted) {log.info('getGravatar reverted', [])} else {let owner = callResult.value}
Ten en cuenta que un nodo Graph conectado a un cliente Geth o Infura puede no detectar todas las reversiones, si confías en esto te recomendamos que utilices un nodo Graph conectado a un cliente Parity.
Data can be encoded and decoded according to Ethereum's ABI encoding format using the encode
and decode
functions in the ethereum
module.
import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'let tupleArray: Array<ethereum.Value> = [ethereum.Value.fromAddress(Address.fromString('0x0000000000000000000000000000000000000420')),ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(62)),]let tuple = tupleArray as ethereum.Tuplelet encoded = ethereum.encode(ethereum.Value.fromTuple(tuple))!let decoded = ethereum.decode('(address,uint256)', encoded)
Para mas informacion:
The native token balance of an address can be retrieved using the ethereum
module. This feature is available from apiVersion: 0.0.9
which is defined subgraph.yaml
. The getBalance()
retrieves the balance of the specified address as of the end of the block in which the event is triggered.
import { ethereum } from '@graphprotocol/graph-ts'let address = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')let balance = ethereum.getBalance(address) // returns balance in BigInt
To check whether an address is a smart contract address or an externally owned address (EOA), use the hasCode()
function from the ethereum
module which will return boolean
. This feature is available from apiVersion: 0.0.9
which is defined subgraph.yaml
.
import { ethereum } from '@graphprotocol/graph-ts'let contractAddr = Address.fromString('0x2E645469f354BB4F5c8a05B3b30A929361cf77eC')let isContract = ethereum.hasCode(contractAddr).inner // returns truelet eoa = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')let isContract = ethereum.hasCode(eoa).inner // returns false
import { log } from '@graphprotocol/graph-ts'
The log
API allows subgraphs to log information to the Graph Node standard output as well as Graph Explorer. Messages can be logged using different log levels. A basic format string syntax is provided to compose log messages from argument.
The log
API includes the following functions:
log.debug(fmt: string, args: Array<string>): void
- logs a debug message.log.info(fmt: string, args: Array<string>): void
- logs an informational message.log.warning(fmt: string, args: Array<string>): void
- logs a warning.log.error(fmt: string, args: Array<string>): void
- logs an error message.log.critical(fmt: string, args: Array<string>): void
– logs a critical message and terminates the subgraph.
The log
API takes a format string and an array of string values. It then replaces placeholders with the string values from the array. The first {}
placeholder gets replaced by the first value in the array, the second {}
placeholder gets replaced by the second value and so on.
log.info('Message to be displayed: {}, {}, {}', [value.toString(), anotherValue.toString(), 'already a string'])
In the example below, the string value "A" is passed into an array to become['A']
before being logged:
let myValue = 'A'export function handleSomeEvent(event: SomeEvent): void {// Displays : "My value is: A"log.info('My value is: {}', [myValue])}
En el ejemplo siguiente, sólo se registra el primer valor de la matriz de argumentos, a pesar de que la matriz contiene tres valores.
let myArray = ['A', 'B', 'C']export function handleSomeEvent(event: SomeEvent): void {// Displays : "My value is: A" (Even though three values are passed to `log.info`)log.info('My value is: {}', myArray)}
Each entry in the arguments array requires its own placeholder {}
in the log message string. The below example contains three placeholders {}
in the log message. Because of this, all three values in myArray
are logged.
let myArray = ['A', 'B', 'C']export function handleSomeEvent(event: SomeEvent): void {// Displays : "My first value is: A, second value is: B, third value is: C"log.info('My first value is: {}, second value is: {}, third value is: {}', myArray)}
Para mostrar un valor específico en el array, se debe proporcionar el valor indexado.
export function handleSomeEvent(event: SomeEvent): void {// Displays : "My third value is C"log.info('My third value is: {}', [myArray[2]])}
El ejemplo siguiente registra el número de bloque, el hash del bloque y el hash de la transacción de un evento:
import { log } from '@graphprotocol/graph-ts'export function handleSomeEvent(event: SomeEvent): void {log.debug('Block number: {}, block hash: {}, transaction hash: {}', [event.block.number.toString(), // "47596000"event.block.hash.toHexString(), // "0x..."event.transaction.hash.toHexString(), // "0x..."])}
import { ipfs } from '@graphprotocol/graph-ts'
Smart contracts occasionally anchor IPFS files on chain. This allows mappings to obtain the IPFS hashes from the contract and read the corresponding files from IPFS. The file data will be returned as Bytes
, which usually requires further processing, e.g. with the json
API documented later on this page.
Dado un hash o ruta de IPFS, la lectura de un archivo desde IPFS se realiza de la siguiente manera:
// Colocar esto dentro del gestor de eventos del mapeolet hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'let data = ipfs.cat(hash)// Rutas como `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`// que incluye documentos en directorios que son soportadoslet path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'let data = ipfs.cat(path)
Note: ipfs.cat
is not deterministic at the moment. If the file cannot be retrieved over the IPFS network before the request times out, it will return null
. Due to this, it's always worth checking the result for null
.
It is also possible to process larger files in a streaming fashion with ipfs.map
. The function expects the hash or path for an IPFS file, the name of a callback, and flags to modify its behavior:
import { JSONValue, Value } from '@graphprotocol/graph-ts'export function processItem(value: JSONValue, userData: Value): void {// See the JSONValue documentation for details on dealing// with JSON valueslet obj = value.toObject()let id = obj.get('id')let title = obj.get('title')if (!id || !title) {return}// Callbacks can also created entitieslet newItem = new Item(id)newItem.title = title.toString()newitem.parent = userData.toString() // Set parent to "parentId"newitem.save()}// Put this inside an event handler in the mappingipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])// Alternatively, use `ipfs.mapJSON`ipfs.mapJSON('Qm...', 'processItem', Value.fromString('parentId'))
The only flag currently supported is json
, which must be passed to ipfs.map
. With the json
flag, the IPFS file must consist of a series of JSON values, one value per line. The call to ipfs.map
will read each line in the file, deserialize it into a JSONValue
and call the callback for each of them. The callback can then use entity operations to store data from the JSONValue
. Entity changes are stored only when the handler that called ipfs.map
finishes successfully; in the meantime, they are kept in memory, and the size of the file that ipfs.map
can process is therefore limited.
On success, ipfs.map
returns void
. If any invocation of the callback causes an error, the handler that invoked ipfs.map
is aborted, and the subgraph is marked as failed.
import { crypto } from '@graphprotocol/graph-ts'
The crypto
API makes a cryptographic functions available for use in mappings. Right now, there is only one:
crypto.keccak256(input: ByteArray): ByteArray
import { json, JSONValueKind } from '@graphprotocol/graph-ts'
JSON data can be parsed using the json
API:
json.fromBytes(data: Bytes): JSONValue
– parses JSON data from aBytes
array interpreted as a valid UTF-8 sequencejson.try_fromBytes(data: Bytes): Result<JSONValue, boolean>
– safe version ofjson.fromBytes
, it returns an error variant if the parsing failedjson.fromString(data: string): JSONValue
– parses JSON data from a valid UTF-8String
json.try_fromString(data: string): Result<JSONValue, boolean>
– safe version ofjson.fromString
, it returns an error variant if the parsing failed
The JSONValue
class provides a way to pull values out of an arbitrary JSON document. Since JSON values can be booleans, numbers, arrays and more, JSONValue
comes with a kind
property to check the type of a value:
let value = json.fromBytes(...)if (value.kind == JSONValueKind.BOOL) {...}
In addition, there is a method to check if the value is null
:
value.isNull(): boolean
When the type of a value is certain, it can be converted to a using one of the following methods:
value.toBool(): boolean
value.toI64(): i64
value.toF64(): f64
value.toBigInt(): BigInt
value.toString(): string
value.toArray(): Array<JSONValue>
- (and then convertJSONValue
with one of the 5 methods above)
Source(s) | Destination | Conversion function |
---|---|---|
Address | Bytes | none |
Address | String | s.toHexString() |
BigDecimal | String | s.toString() |
BigInt | BigDecimal | s.toBigDecimal() |
BigInt | String (hexadecimal) | s.toHexString() or s.toHex() |
BigInt | String (unicode) | s.toString() |
BigInt | i32 | s.toI32() |
Boolean | Boolean | none |
Bytes (signed) | BigInt | BigInt.fromSignedBytes(s) |
Bytes (unsigned) | BigInt | BigInt.fromUnsignedBytes(s) |
Bytes | String (hexadecimal) | s.toHexString() or s.toHex() |
Bytes | String (unicode) | s.toString() |
Bytes | String (base58) | s.toBase58() |
Bytes | i32 | s.toI32() |
Bytes | u32 | s.toU32() |
Bytes | JSON | json.fromBytes(s) |
int8 | i32 | none |
int32 | i32 | none |
int32 | BigInt | BigInt.fromI32(s) |
uint24 | i32 | none |
int64 - int256 | BigInt | none |
uint32 - uint256 | BigInt | none |
JSON | boolean | s.toBool() |
JSON | i64 | s.toI64() |
JSON | u64 | s.toU64() |
JSON | f64 | s.toF64() |
JSON | BigInt | s.toBigInt() |
JSON | string | s.toString() |
JSON | Array | s.toArray() |
JSON | Object | s.toObject() |
String | Address | Address.fromString(s) |
Bytes | Address | Address.fromBytes(s) |
String | BigInt | BigInt.fromString(s) |
String | BigDecimal | BigDecimal.fromString(s) |
String (hexadecimal) | Bytes | ByteArray.fromHexString(s) |
String (UTF-8) | Bytes | ByteArray.fromUTF8(s) |
You can inspect the contract address, network and context of the data source that invoked the handler through the dataSource
namespace:
dataSource.address(): Address
dataSource.network(): string
dataSource.context(): DataSourceContext
The base Entity
class and the child DataSourceContext
class have helpers to dynamically set and get fields:
setString(key: string, value: string): void
setI32(key: string, value: i32): void
setBigInt(key: string, value: BigInt): void
setBytes(key: string, value: Bytes): void
setBoolean(key: string, value: bool): void
setBigDecimal(key, value: BigDecimal): void
getString(key: string): string
getI32(key: string): i32
getBigInt(key: string): BigInt
getBytes(key: string): Bytes
getBoolean(key: string): boolean
getBigDecimal(key: string): BigDecimal
The context
section within dataSources
allows you to define key-value pairs that are accessible within your subgraph mappings. The available types are Bool
, String
, Int
, Int8
, BigDecimal
, Bytes
, List
, and BigInt
.
Here is a YAML example illustrating the usage of various types in the context
section:
dataSources:- kind: ethereum/contractname: ContractNamenetwork: mainnetcontext:bool_example:type: Booldata: truestring_example:type: Stringdata: 'hello'int_example:type: Intdata: 42int8_example:type: Int8data: 127big_decimal_example:type: BigDecimaldata: '10.99'bytes_example:type: Bytesdata: '0x68656c6c6f'list_example:type: Listdata:- type: Intdata: 1- type: Intdata: 2- type: Intdata: 3big_int_example:type: BigIntdata: '1000000000000000000000000'
Bool
: Specifies a Boolean value (true
orfalse
).String
: Specifies a String value.Int
: Specifies a 32-bit integer.Int8
: Specifies an 8-bit integer.BigDecimal
: Specifies a decimal number. Must be quoted.Bytes
: Specifies a hexadecimal string.List
: Specifies a list of items. Each item needs to specify its type and data.BigInt
: Specifies a large integer value. Must be quoted due to its large size.
This context is then accessible in your subgraph mapping files, enabling more dynamic and configurable subgraphs.