Developing > AssemblyScript API > API AssemblyScript

API AssemblyScript

Reading time: 21 min

Nota: Se criou um subgraph antes da versão 0.22.0 do graph-cli/graph-ts, está a usar uma versão mais antiga do AssemblyScript. Favor conferir o Guia de Migração

Esta página documenta quais APIs embutidas podem ser usadas ao escrever mapeamentos de subgraph. Há dois tipos de API disponíveis do início:

  • a biblioteca do Graph TypeScript (graph-ts) e
  • códigos gerados a partir dos arquivos do subgraph por graph codegen.

Também é possível adicionar outras bibliotecas como dependências, desde que sejam compatíveis com o AssemblyScript. Como esta é a linguagem na qual são escritos os mapeamentos, a wiki do AssemblyScript é uma boa referência para a linguagem e as características comuns das bibliotecas.

Referência da API

Link para esta seção

A biblioteca @graphprotocol/graph-ts fornece as seguintes APIs:

  • Uma API ethereum para trabalhar com contratos inteligentes, eventos, blocos, transações, e valores no Ethereum.
  • Uma API store para carregar e guardar entidades dentro e fora do armazenamento do Graph Node.
  • Uma API log para gravar mensagens ao resultado do Graph Node e ao Graph Explorer.
  • Uma API ipfs para carregar arquivos do IPFS.
  • Uma API json para analisar dados em JSON.
  • Uma API crypto para usar funções criptográficas.
  • Primitivos de baixo nível para traduzir entre sistemas de tipos diferentes, como Ethereum, JSON, GraphQL e AssemblyScript.

No manifest do subgraph, apiVersion especifica a versão da API de mapeamento, executada pelo Graph Node para um subgraph.

VersãoNotas de atualização
0.0.9Adiciona novas funções de host eth_get_balance & hasCode
0.0.8Adiciona validação para existência de campos no schema ao salvar uma entidade.
0.0.7Classes TransactionReceipt e Log adicionadas aos tipos do EthereumCampo
Campo receipt adicionado ao objeto Ethereum Event
0.0.6Campo nonce adicionado ao objeto Ethereum TransactionCampo
baseFeePerGas adicionado ao objeto Ethereum Block
0.0.5AssemblyScript atualizado à versão 0.19.10 (inclui mudanças recentes, favor ler o Guia de Migração)
ethereum.transaction.gasUsed renomeado para ethereum.transaction.gasLimit
0.0.4Campo functionSignature adicionado ao objeto Ethereum SmartContractCall
0.0.3Campo from adicionado ao objeto Ethereum
Calletherem.call.address renomeado para ethereum.call.to
0.0.2Campo input adicionado ao objeto Ethereum Transaction

Tipos Embutidos

Link para esta seção

A documentação sobre os tipos de base embutidos no AssemblyScript está na wiki do AssemblyScript.

Os seguintes tipos adicionais são fornecidos pelo @graphprotocol/graph-ts.

import { ByteArray } from '@graphprotocol/graph-ts'

ByteArray representa um arranjo de u8.

Construção

  • fromI32(x: i32): ByteArray - Decompõe x em bytes.
  • fromHexString(hex: string): ByteArray — O comprimento da entrada deve ser par. É facultativo prefixar com 0x.

Conversões de tipo

  • toHexString(): string — Converte numa cadeia de caracteres hex prefixada com 0x`.
  • toString(): string — Interpreta os bytes como uma cadeia de caracteres UTF-8.
  • toBase58(): string — Codifica os bytes como uma cadeia base58.
  • toU32(): u32 — Interpreta os bytes como um u32 little-endian. Não funciona em caso de overflow.
  • toI32(): i32 - Interpreta o arranjo de byte como um i32 little-endian. Não funciona em caso de overflow.

Operadores

  • equals(y: ByteArray): bool — pode ser escrito como x == y`.
  • concat(other: ByteArray) : ByteArray — retorna um novo ByteArray que consiste de this diretamente seguido de other
  • concatI32(other: i32) : ByteArray — retorna um novo ByteArray que consiste de this diretamente seguido pela representação em byte de other
import { BigDecimal } from '@graphprotocol/graph-ts'

O BigDecimal é usado para representar decimais de precisão arbitrária.

Nota: Internalmente, o BigDecimal é armazenado no formato de ponto flutuante IEEE-754 decimal128, que apoia 34 dígitos decimais de significando. Portanto, o BigDecimal não serve para representar tipos de ponto fixo que possam exceder 34 dígitos, como um (ufixed256x18)[https://docs.soliditylang.org/en/latest/types.html#fixed-point-numbers] em Solidity ou equivalente.

Construção

  • constructor(bigInt: BigInt) – cria um BigDecimal a partir de um BigInt.
  • static fromString(s: string): BigDecimal — faz uma análise sintática a partir de uma cadeia decimal.

Conversões de tipo

  • toString(): string — imprime para uma cadeia decimal.

Matemática

  • plus(y: BigDecimal): BigDecimal — pode ser escrito como x + y.
  • minus(y: BigDecimal): BigDecimal — pode ser escrito como x - y.
  • times(y: BigDecimal): BigDecimal — pode ser x * y.
  • div(y: BigDecimal): BigDecimal — pode ser escrito como x / y.
  • equals(y: BigDecimal): bool — pode ser x == y.
  • notEqual(y: BigDecimal): bool — pode ser x != y.
  • lt(y: BigDecimal): bool — pode ser x < y.
  • le(y: BigDecimal): bool – pode ser x <= y.
  • gt(y: BigDecimal): bool – pode ser x > y.
  • ge(y: BigDecimal): bool – pode ser x >= y.
  • neg(): BigDecimal - pode ser -x.
import { BigInt } from '@graphprotocol/graph-ts'

O BigInt é usado para representar números inteiros grandes, inclusive valores em Ethereum de uint32 até uint256, e int64 até int256. Tudo abaixo de uint32, como o int32, uint24 ou int8 é representado como i32.

A classe BigInt tem a seguinte API:

Construção

  • BigInt.fromI32(x: i32): BigInt – cria um BigInt de um i32.

  • BigInt.fromString(s: string): BigInt– Analisa um BigInt de uma cadeia.

  • BigInt.fromUnsignedBytes(x: Bytes): BigInt — Interpreta bytes como um inteiro little-endian, não assinado. Se a sua entrada for big-endian, chame pelo .reverse() primeiro.

  • BigInt.fromSignedBytes(x: Bytes): BigInt — Interpreta bytes como um inteiro little-endian, assinado. Se a sua entrada for big-endian, chame pelo .reverse() primeiro.

    Conversões de tipo

  • x.toHex(): string — transforma o BigInt numa cadeia de caracteres hexadecimais.

  • x.toString(): string — transforma o BigInt numa cadeia de números decimais.

  • x.toI32(): i32 — retorna o BigInt como um i32; falha se o valor não couber no i32. É bom verificar o x.isI32() primeiro.

  • x.toBigDecimal(): BigDecimal — converte num decimal sem fracionário.

Matemática

  • x.plus(y: BigInt): BigInt – pode ser escrito como x + y.
  • x.minus(y: BigInt): BigInt – pode ser escrito como x - y.
  • x.times(y: BigInt): BigInt – pode ser escrito como x * y.
  • x.div(y: BigInt): BigInt – pode ser escrito como x / y.
  • x.mod(y: BigInt): BigInt – pode ser escrito como x % y.
  • x.equals(y: BigInt): bool – pode ser escrito como x == y.
  • x.notEqual(y: BigInt): bool – pode ser escrito como x != y.
  • x.lt(y: BigInt): bool – pode ser escrito como x < y.
  • x.le(y: BigInt): bool – pode ser escrito como x <= y.
  • x.gt(y: BigInt): bool – pode ser escrito como x > y.
  • x.ge(y: BigInt): bool – pode ser escrito como x >= y.
  • x.neg(): BigInt – pode ser escrito como -x.
  • x.divDecimal(y: BigDecimal): BigDecimal – divide por um decimal e dá um resultado decimal.
  • x.isZero(): bool — Conveniência para conferir se o número é zero.
  • x.isl32(): bool — Confere se o número cabe em um i32.
  • x.abs(): BigInt — Valor absoluto.
  • x.pow(exp: u8): BigInt — Exponenciação.
  • bitOr(x: BigInt, y: BigInt): BigInt — pode ser escrito como x | y.
  • bitAnd(x: BigInt, y: BigInt): BigInt — pode ser escrito como x & y.
  • leftShift(x: BigInt, bits: u8): BigInt – pode ser escrito como x << y.
  • rightShift(x: BigInt, bits: u8): BigInt – pode ser escrito como x >> y.
import { TypedMap } from '@graphprotocol/graph-ts'

O TypedMap pode servir para armazenar pares de chave e valor (key e value ). Confira este exemplo.

A classe TypedMap tem a seguinte API:

  • new TypedMap<K, V>() — cria um mapa vazio com chaves do tipo K e valores do tipo V
  • map.set(key: K, value: V): void — coloca o valor do key como value
  • map.getEntry(key: K): TypedMapEntry<K, V> | null — retorna o par de valor-chave para um key ou null se o key não existir no mapa
  • map.get(key: K): V | null — retorna o valor para um key ou null se o key não existir no mapa
  • map.isSet(key: K): bool — retorna true se o key existir no mapa e false se não existir
import { Bytes } from '@graphprotocol/graph-ts'

O Bytes serve para representar arranjos de comprimento arbitrário de bytes. Isto inclui valores do Ethereum do tipo bytes, bytes32, etc.

A classe Bytes estende o Uint8Array do AssemblyScript. Isto apoia toda a funcionalidade Uint8Array, além dos novos métodos a seguir:

Construção

  • fromHexString(hex: string) : Bytes — Converte a cadeia hex, que deve consistir de um número par de dígitos hexadecimais para um ByteArray. Opcionalmente, a cadeia hex pode começar com 0x
  • fromI32(i: i32) : Bytes - Converte o i em um arranjo de bytes

Conversões de tipo

  • b.toHex() — retorna uma cadeia hexadecimal que representa os bytes no arranjo
  • b.toString() — converte os bytes no arranjo para uma cadeia de caracteres em unicode
  • b.toBase58() — transforma um valor de Ethereum Bytes numa codificação base58 (usado para hashes IPFS)

Operadores

  • b.concat(other: Bytes) : Bytes - - retorna um novo Bytes que consiste de this diretamente seguido por other
  • b.concatI32(other: i32) : ByteArray — retorna um novo Bytes que consiste de this diretamente seguido pela representação em byte de other
import { Address } from '@graphprotocol/graph-ts'

Address estende o Bytes para representar valores de address do Ethereum.

Ele adiciona o seguinte método em cima da API Bytes:

  • Address.fromString(s: string): Address — cria um Address a partir de uma cadeia hexadecimal
  • Address.fromBytes(b: Bytes): Address — cria um Address a partir do b, que deve ter o comprimento exato de 20 bytes. Preencher um valor com menos ou mais bytes causará um erro

Armazenamento da API

Link para esta seção
import { store } from '@graphprotocol/graph-ts'

A API store permite carregar, salvar e remover entidades do/para o armazenamento do Graph Node.

As entidades escritas no armazenamento mapeam um-por-um com os tipos de @entity definidos no schema GraphQL do subgraph. Para trabalhar com estas entidades de forma conveniente, o comando graph codegen fornecido pelo Graph CLI gera classes de entidades, que são subclasses do tipo embutido Entity, com getters e setters de propriedade para os campos no schema e métodos para carregar e salvar estas entidades.

Como criar entidades

Link para esta seção

Este é um padrão comum para a criação de entidades de eventos do Ethereum.

// Importar a classe de evento de transferência gerada da ABI ERC20
import { Transfer as TransferEvent } from '../generated/ERC20/ERC20'
// Importar o tipo de entidade de transferência gerado do schema do GraphQL
import { Transfer } from '../generated/schema'
// Handler de evento de transferência
export function handleTransfer(event: TransferEvent): void {
// Criar uma entidade de Transferência, usando o hash da transação como a ID da entidade
let id = event.transaction.hash
let transfer = new Transfer(id)
// Determinar propriedades na entidade, usando os parâmetros do evento
transfer.from = event.params.from
transfer.to = event.params.to
transfer.amount = event.params.amount
// Salvar a entidade no armazenamento
transfer.save()
}

Quando um evento Transfer é encontrado durante o processamento da chain, ele é passado para o handler de evento handleTransfer com o tipo Transfer gerado (apelidado de TransferEvent aqui, para evitar confusões com o tipo de entidade). Este tipo permite o acesso a dados como a transação parente do evento e seus parâmetros.

Cada entidade deve ter um ID única para evitar colisões com outras entidades. É bem comum que parâmetros de eventos incluam um identificador único a ser usado. Nota: usar o mesmo hash de transação como ID presume que nenhum outro evento na mesma transação criará entidades a usar este hash como o ID.

Como carregar entidades a partir do armazenamento

Link para esta seção

Se uma entidade já existe, ela pode ser carregada do armazenamento com os seguintes comandos:

let id = event.transaction.hash // ou como a ID for construída
let transfer = Transfer.load(id)
if (transfer == null) {
transfer = new Transfer(id)
}
// Use a entidade Transfer como antes

Como a entidade pode ainda não existir no armazenamento, o método load retorna um valor de tipo Transfer | null. Portanto, é bom prestar atenção ao caso null antes de usar o valor.

Nota: Só é necessário carregar entidades se as mudanças feitas no mapeamento dependem dos dados anteriores de uma entidade. Veja a próxima seção para ver as duas maneiras de atualizar entidades existentes.

Como consultar entidades criadas dentro de um bloco

Link para esta seção

Desde o graph-node v0.31.0, o @graphprotocol/graph-ts v0.30.0 e o @graphprotocol/graph-cli v0.49.0, o método loadInBlock está disponível em todos os tipos de entidade.

A API do armazenamento facilita o resgate de entidades que foram criadas ou atualizadas no bloco atual. Um caso comum: um handler cria uma Transação de algum evento on-chain, e um handler seguinte quer acessar esta transação caso ela exista. Se a transação não existe, o subgraph deve acessar o banco de dados para descobrir que a entidade não existe; se o autor do subgraph já souber que a entidade deve ter sido criada no mesmo bloco, o uso do loadInBlock evita esta volta pelo banco de dados. Para alguns subgraphs, estas consultas perdidas podem contribuir muito para o tempo de indexação.

let id = event.transaction.hash // ou como a ID for construída
let transfer = Transfer.load(id)
if (transfer == null) {
transfer = new Transfer(id)
}
// Use a entidade Transfer como antes

Nota: se não houver nenhuma entidade criada no bloco dado, o loadInBlock retornará um null mesmo se houver uma entidade com o ID dado no armazenamento.

Como buscar entidades derivadas

Link para esta seção

A partir da versão 0.31.0 do graph-node, @graphprotocol/graph-ts v0.31.0 e a versão 0.51.0 do @graphprotocol/graph-cli, está disponível o método loadRelated.

Isto permite o carregamento de campos de entidade derivada a partir de um event handler. Por exemplo, considerando o schema a seguir:

type Token @entity {
id: ID!
holder: Holder!
color: String
}
type Holder @entity {
id: ID!
tokens: [Token!]! @derivedFrom(field: "holder")
}

O código a seguir carregará a entidade Token de que foi derivada a entidade Holder:

let holder = Holder.load('test-id')
// Carrega as entidades de Token associadas com um titular dado
let tokens = holder.tokens.load()

Como atualizar entidades existentes

Link para esta seção

Há duas maneiras de atualizar uma entidade existente:

  1. Carregar a entidade com, por ex., Transfer.load(id), determinar as propriedades da entidade, e então usar o .save() para colocá-la no armazenamento.
  2. Simplesmente criar a entidade com, por ex., new Transfer(id), determinar as propriedades da entidade, e depois colocá-la no armazenamento com .save(). Se a entidade já existir, as mudanças serão integradas a ela.

Geralmente é simples mudar propriedades, graças aos setters de propriedade gerados:

let transfer = new Transfer(id)
transfer.from = ...
transfer.to = ...
transfer.amount = ...

Também é possível cancelar propriedades com uma das seguintes instruções:

transfer.from.unset()
transfer.from = null

Isto só funciona com propriedades opcionais; por ex., propriedades declaradas sem um ! no GraphQL. Dois exemplos seriam owner: Bytes ou amount: BigInt.

Atualizar propriedades de arranjos é um processo um pouco mais envolvido, pois pegar um arranjo de uma entidade cria uma cópia deste mesmo arranjo. Isto significa que as propriedades de arranjos devem ser impostas explicitamente após mudar um arranjo. O seguinte assume que o entity tem um campo numbers: [BigInt!]!.

// Isto não funcionará
entity.numbers.push(BigInt.fromI32(1))
entity.save()
// Isto funcionará
let numbers = entity.numbers
numbers.push(BigInt.fromI32(1))
entity.numbers = numbers
entity.save()

Como remover entidades do armazenamento

Link para esta seção

Atualmente, não há como remover uma entidade através dos tipos gerados. Em vez disto, o processo requer a passagem do ID da entidade e do nome do tipo da mesma ao store.remove:

import { store } from '@graphprotocol/graph-ts'
...
let id = event.transaction.hash
store.remove('Transfer', id)

API do Ethereum

Link para esta seção

A API do Ethereum fornece acesso a contratos inteligentes, variáveis de estado público, funções de contrato, eventos, transações, blocos e a codificação/decodificação de dados no Ethereum.

Apoio para Tipos no Ethereum

Link para esta seção

Assim como em entidades, o graph codegen gera classes para todos os contratos inteligentes e eventos usados em um subgraph. Para isto, as ABIs dos contratos devem ser parte da fonte de dados no manifest do subgraph. Tipicamente, os arquivos da ABI são armazenados em uma pasta abis/.

Com as classes geradas, conversões entre tipos no Ethereum e os tipos embutidos acontecem em segundo plano para que os autores de subgraphs não precisem se preocupar com elas.

Veja um exemplo a seguir. Considerando um schema de subgraph como

type Transfer @entity {
id: Bytes!
from: Bytes!
to: Bytes!
amount: BigInt!
}

e uma assinatura de evento Transfer(address,address,uint256) na Ethereum, os valores from, to e amount do tipo address, address and uint256 são convertidos para Address and BigInt, o que permite que sejam passados para as propriedades Bytes! and BigInt! da entidade Transfer:

let id = event.transaction.hash
let transfer = new Transfer(id)
transfer.from = event.params.from
transfer.to = event.params.to
transfer.amount = event.params.amount
transfer.save()

Eventos e Dados de Blocos/Transações

Link para esta seção

Eventos de Ethereum passados para handlers de eventos, como o evento Transfer nos exemplos anteriores, não só permitem acessar os parâmetros de evento, mas também sua transação parente e o bloco de qual fazem parte. Os seguintes dados podem ser obtidos de instâncias event (estas classes são parte do módulo ethereum no graph-ts):

class Event {
address: Address
logIndex: BigInt
transactionLogIndex: BigInt
logType: string | null
block: Block
transaction: Transaction
parameters: Array<EventParam>
receipt: TransactionReceipt | null
}
class Block {
hash: Bytes
parentHash: Bytes
unclesHash: Bytes
author: Address
stateRoot: Bytes
transactionsRoot: Bytes
receiptsRoot: Bytes
number: BigInt
gasUsed: BigInt
gasLimit: BigInt
timestamp: BigInt
difficulty: BigInt
totalDifficulty: BigInt
size: BigInt | null
baseFeePerGas: BigInt | null
}
class Transaction {
hash: Bytes
index: BigInt
from: Address
to: Address | null
value: BigInt
gasLimit: BigInt
gasPrice: BigInt
input: Bytes
nonce: BigInt
}
class TransactionReceipt {
transactionHash: Bytes
transactionIndex: BigInt
blockHash: Bytes
blockNumber: BigInt
cumulativeGasUsed: BigInt
gasUsed: BigInt
contractAddress: Address
logs: Array<Log>
status: BigInt
root: Bytes
logsBloom: Bytes
}
class Log {
address: Address
topics: Array<Bytes>
data: Bytes
blockHash: Bytes
blockNumber: Bytes
transactionHash: Bytes
transactionIndex: BigInt
logIndex: BigInt
transactionLogIndex: BigInt
logType: string
removed: bool | null
}

Acesso ao Estado do Contrato Inteligente

Link para esta seção

O código gerado pelo graph codegen também inclui classes para os contratos inteligentes usados no subgraph. Estes servem para acessar variáveis de estado público e funções de chamada do contrato no bloco atual.

É comum acessar o contrato de qual origina um evento. Isto é feito com o seguinte código:

// Importar a classe do contrato gerado e a classe do evento de transferência gerado
import { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'
// Import the generated entity class
import { Transfer } from '../generated/schema'
export function handleTransfer(event: TransferEvent) {
// Ligar o contrato ao endereço que emitiu o evento
let contract = ERC20Contract.bind(event.address)
// Acessar variáveis e funções de estado fazendo chamadas
let erc20Symbol = contract.symbol()
}

O Transfer é apelidado de TransferEvent aqui para evitar confusões de nomenclatura com o tipo da entidade

Enquanto o ERC20Contract no Ethereum tiver uma função pública de apenas-leitura chamada symbol, ele pode ser chamado com o .symbol(). Para variáveis de estado público, um método com o mesmo nome é criado automaticamente.

Qualquer outro contrato que seja parte do subgraph pode ser importado do código gerado e ligado a um endereço válido.

Como Lidar com Chamadas Revertidas

Link para esta seção

Se os métodos de apenas-leitura do seu contrato forem revertidos, chame o método do contrato gerado prefixado com try_. Por exemplo, o contrato do Gravity expõe o método gravatarToOwner. Este código poderia lidar com uma reversão naquele método:

let gravity = Gravity.bind(event.address)
let callResult = gravity.try_gravatarToOwner(gravatar)
if (callResult.reverted) {
log.info('getGravatar reverted', [])
} else {
let owner = callResult.value
}

Note que um Graph Node conectado a um cliente Geth ou Infura pode não detectar todas as reversões; se depender disto, recomendamos usar um Graph Node conectado a um cliente Parity.

ABI de Codificação/Decodificação

Link para esta seção

Dados podem ser codificados e decodificados conforme o formato de codificação da ABI do Ethereum, através das funções encode e decode no módulo ethereum.

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.Tuple
let encoded = ethereum.encode(ethereum.Value.fromTuple(tuple))!
let decoded = ethereum.decode('(address,uint256)', encoded)

Para mais informações:

Saldo de um Endereço

Link para esta seção

O saldo de token nativo de um endereço pode ser resgatado com o módulo ethereum. Este recurso está disponível a partir do apiVersion: 0.0.9, definido no subgraph.yaml. O getBalance() resgata o saldo do endereço especificado como o do fim do bloco em que o evento é adicionado.

import { ethereum } from '@graphprotocol/graph-ts'
let address = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
let balance = ethereum.getBalance(address) // retorna o saldo em BigInt

Como Conferir Se Um Endereço é Um Contrato Ou Um EOA

Link para esta seção

Para conferir se um endereço é um de contrato inteligente ou um endereço titulado externamente (sigla em português para EOA), use a função hasCode() do módulo ethereum que retornará boolean. Este recurso está disponível a partir do apiVersion: 0.0.9, definido no subgraph.yaml.

import { ethereum } from '@graphprotocol/graph-ts'
let contractAddr = Address.fromString('0x2E645469f354BB4F5c8a05B3b30A929361cf77eC')
let isContract = ethereum.hasCode(contractAddr).inner // returns true
let eoa = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
let isContract = ethereum.hasCode(eoa).inner // retorna false

API de Logging

Link para esta seção
import { log } from '@graphprotocol/graph-ts'

A API log permite que os subgraphs gravem informações à saída padrão do Graph Node, assim como ao Graph Explorer. Mensagens podem ser gravadas com níveis diferentes de log. É fornecida uma sintaxe básica de formatação de strings para compor mensagens de log do argumento.

A API log inclui as seguintes funções:

  • log.debug(fmt: string, args: Array<string>): void — loga uma mensagem de debug.
  • log.info(fmt: string, args: Array<string>): void - loga uma mensagem de debug.
  • log.warning(fmt: string, args: Array<string>): void - loga um aviso.
  • log.error(fmt: string, args: Array<string>): void - loga uma mensagem de erro.
  • log.critical(fmt: string, args: Array<string>): void – loga uma mensagem crítica e encerra o subgraph.

A API log toma um string de formato e um arranjo de valores de string. Ele então substitui os temporários com os valores de strings do arranjo. O primeiro {} temporário é substituído pelo primeiro valor no arranjo, o segundo {} temporário é substituído pelo segundo valor, e assim por diante.

log.info('Message to be displayed: {}, {}, {}', [value.toString(), anotherValue.toString(), 'already a string'])

Como logar um ou mais valores

Link para esta seção
Como logar um único valor
Link para esta seção

No exemplo abaixo, o valor de string "A" é passado a um arranjo para tornar-se ['A'] antes de ser registado no log:

let myValue = 'A'
export function handleSomeEvent(event: SomeEvent): void {
// Mostra : "My value is: A"
log.info('My value is: {}', [myValue])
}
Como logar uma única entrada de um arranjo existente
Link para esta seção

No exemplo abaixo, só é logado o primeiro valor do arranjo do argumento, apesar de haver três valores no arranjo.

let myArray = ['A', 'B', 'C']
export function handleSomeEvent(event: SomeEvent): void {
// Displays : "My value is: A" (Apesar de três valores serem passados ao `log.info`)
log.info('My value is: {}', myArray)
}

Como logar várias entradas de um arranjo existente

Link para esta seção

Cada entrada no arranjo dos argumentos exige o seu próprio {} no string de mensagens de log. O exemplo abaixo contém três {} temporários na mensagem de log. Por causa disto, são registados todos os três valores no myArray.

let myArray = ['A', 'B', 'C']
export function handleSomeEvent(event: SomeEvent): void {
// Mostra : "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)
}
Como logar uma entrada específica de um arranjo existente
Link para esta seção

Para mostrar um valor específico no arranjo, forneça o valor indexado.

export function handleSomeEvent(event: SomeEvent): void {
// Mostra : "My third value is C"
log.info('My third value is: {}', [myArray[2]])
}
Como logar informações de eventos
Link para esta seção

O exemplo abaixo loga o número do bloco, hash do bloco e o hash da transação de um 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'

Contratos inteligentes ocasionalmente ancoram arquivos IPFS on-chain. Assim, os mapeamentos obtém os hashes IPFS do contrato e lêem os arquivos correspondentes do IPFS. Os dados dos arquivos serão retornados como Bytes, o que costuma exigir mais processamento; por ex., com a API json documentada mais abaixo nesta página.

Considerando um hash ou local IPFS, um arquivo do IPFS é lido da seguinte maneira:

// Coloque isto dentro de um handler de evento no mapeamento
let hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'
let data = ipfs.cat(hash)
// Locais como `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`
// que incluem arquivos em diretorias também são apoiados
let path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'
let data = ipfs.cat(path)

Nota: O ipfs.cat não é determinístico no momento. Se o arquivo não puder ser retirado sobre a rede IPFS antes do tempo do pedido acabar, ele retornará um null. Com isto, sempre vale a pena procurar o null no resultado.

Também é possível processar arquivos maiores em streaming com o ipfs.map. A função espera o hash ou local de um arquivo IPFS, o nome de um callback, e flags para modificar o seu comportamento:

import { JSONValue, Value } from '@graphprotocol/graph-ts'
export function processItem(value: JSONValue, userData: Value): void {
// Ver a documentação do JSONValue para detalhes sobre
// como lidar com valores JSON
let obj = value.toObject()
let id = obj.get('id')
let title = obj.get('title')
if (!id || !title) {
return
}
// Callbacks também podem criar entidades
let newItem = new Item(id)
newItem.title = title.toString()
newitem.parent = userData.toString() // Set parent to "parentId"
newitem.save()
}
// Coloque isto dentro de um handler de evento no mapeamento
ipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])
// Como alternativa, use `ipfs.mapJSON`
ipfs.mapJSON('Qm...', 'processItem', Value.fromString('parentId'))

O único flag atualmente apoiado é o json, que deve ser passado ao ipfs.map. Com o flag json, o arquivo IPFS deve consistir de uma série de valores JSON, com um valor por linha. Chamar ipfs.map, irá ler cada linha no arquivo, desserializá-lo em um JSONValue, e chamar o callback para cada linha. O callback pode então armazenar dados do JSONValue com operações de entidade. As mudanças na entidade só serão armazenadas quando o handler que chamou o ipfs.map concluir com sucesso; enquanto isso, elas ficam na memória, e o tamanho do arquivo que o ipfs.map pode processar é então limitado.

Em caso de sucesso, o ipfs.map retorna void. Se qualquer invocação do callback causar um erro, o handler que invocou o ipfs.map é abortado, e o subgraph é marcado como falho.

API de Criptografia

Link para esta seção
import { crypto } from '@graphprotocol/graph-ts'

A API crypto disponibiliza funções criptográficas para uso em mapeamentos. No momento, apenas um está disponível:

  • crypto.keccak256(input: ByteArray): ByteArray
import { json, JSONValueKind } from '@graphprotocol/graph-ts'

Dados em JSON podem ser analisados com a API json:

  • json.fromBytes(data: Bytes): JSONValue — analisa dados JSON de um arranjo Bytes interpretado como uma sequência válida de UTF-8
  • json.try_fromBytes(data: Bytes): Result<JSONValue, boolean>— versão segura do json.fromBytes, retorna um erro se houver falha no parsing
  • json.fromString(data: string): JSONValue — faz parsing de dados JSON de um String em UTF-8 válido`
  • json.try_fromString(data: string): Result<JSONValue, boolean> – versão segura do json.fromString, retorna um erro se houver falha no parsing

A classe JSONValue fornece uma maneira de retirar valores de um documento JSON arbitrário. Como valores JSON podem ser booleans, números, arranjos e mais, o JSONValue vem com uma propriedade kind para conferir o tipo de um valor:

let value = json.fromBytes(...)
if (value.kind == JSONValueKind.BOOL) {
...
}

Além disso, há um método para conferir se o valor é null:

  • value.isNull(): boolean

Quando o tipo de um valor é confirmado, ele pode ser convertido num tipo embutido usando um dos seguintes métodos:

  • value.toBool(): boolean
  • value.toI64(): i64
  • value.toF64(): f64
  • value.toBigInt(): BigInt
  • value.toString(): string
  • value.toArray(): Array<JSONValue> - (e depois converter o JSONValue com um dos 5 métodos acima)

Referência de Conversões de Tipos

Link para esta seção
Fonte(s)DestinoFunção de conversão
AddressBytesnenhum
AddressStrings.toHexString()
BigDecimalStrings.toString()
BigIntBigDecimals.toBigDecimal()
BigIntString (hexadecimal)s.toHexString() ou s.toHex()
BigIntString (unicode)s.toString()
BigInti32s.toI32()
BooleanBooleannenhum
Bytes (assinado)BigIntBigInt.fromSignedBytes(s)
Bytes (não assinado)BigIntBigInt.fromUnsignedBytes(s)
BytesString (hexadecimal)s.toHexString() ou s.toHex()
BytesString (unicode)s.toString()
BytesString (base58)s.toBase58()
Bytesi32s.toI32()
Bytesu32s.toU32()
BytesJSONjson.fromBytes(s)
int8i32nenhum
int32i32nenhum
int32BigIntBigInt.fromI32(s)
uint24i32nenhum
int64 - int256BigIntnenhum
uint32 - uint256BigIntnenhum
JSONbooleans.toBool()
JSONi64s.toI64()
JSONu64s.toU64()
JSONf64s.toF64()
JSONBigInts.toBigInt()
JSONstrings.toString()
JSONArrays.toArray()
JSONObjects.toObject()
StringAddressAddress.fromString(s)
BytesAddressAddress.fromBytes(s)
StringBigIntBigInt.fromString(s)
StringBigDecimalBigDecimal.fromString(s)
String (hexadecimal)BytesByteArray.fromHexString(s)
String (UTF-8)BytesByteArray.fromUTF8(s)

Metadados de Fontes de Dados

Link para esta seção

É possível inspecionar o endereço do contrato, a rede, e o contexto das fontes de dados que invocaram o handler através do namespace dataSource:

  • dataSource.address(): Address
  • dataSource.network(): string
  • dataSource.context(): DataSourceContext

Entidade e DataSourceContext

Link para esta seção

A classe base Entity e a subclasse DataSourceContext têm helpers para determinar e retornar campos de forma dinâmica:

  • 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

DataSourceContext no Manifest

Link para esta seção

A seção context dentro do dataSources lhe permite definir pares key-value acessíveis dentro dos seus mapeamentos de subgraph. Os tipos disponíveis são Bool, String, Int, Int8, BigDecimal, Bytes, List, e BigInt.

Aqui está um exemplo de YAML que ilustra o uso de vários tipos na seção context:

dataSources:
- kind: ethereum/contract
name: ContractName
network: mainnet
context:
bool_example:
type: Bool
data: true
string_example:
type: String
data: 'hello'
int_example:
type: Int
data: 42
int8_example:
type: Int8
data: 127
big_decimal_example:
type: BigDecimal
data: '10.99'
bytes_example:
type: Bytes
data: '0x68656c6c6f'
list_example:
type: List
data:
- type: Int
data: 1
- type: Int
data: 2
- type: Int
data: 3
big_int_example:
type: BigInt
data: '1000000000000000000000000'
  • Bool: Especifica um valor Boolean (true ou false).
  • String: Especifica um valor String.
  • Int: Especifica um integral de 32 bits.
  • Int8: Especifica um integral de 8 bits.
  • BigDecimal: Especifica um número decimal. Deve ser citado.
  • Bytes: Especifica um string hexadecimal.
  • List: Especifica uma lista de itens. Cada item deve especificar o seu tipo e dados.
  • BigInt: Especifica um valor integral largo. É necessário citar este devido ao seu grande tamanho.

Este contexto, então, pode ser acessado nos seus arquivos de mapeamento de subgraph, o que resulta em subgraphs mais dinâmicos e configuráveis.

Editar página

Anterior
Como criar um Subgraph
Próximo
Problemas Comuns no AssemblyScript
Editar página