21 minutos
API AssemblyScript
Note: If you created a Subgraph prior to graph-cli/graph-ts version 0.22.0, then you’re using an older version of AssemblyScript. It is recommended to review the Migration Guide.
Learn what built-in APIs can be used when writing Subgraph mappings. There are two kinds of APIs available out of the box:
- A biblioteca do Graph TypeScript (
graph-ts) - Code generated from Subgraph files by 
graph codegen 
Você também pode adicionar outras bibliotecas como dependências, contanto que sejam compatíveis com AssemblyScript.
Já que os mapeamentos de linguagem são escritos em AssemblyScript, vale a pena consultar os recursos padrão de linguagem e biblioteca da wiki do AssemblyScript.
Referência da API
A biblioteca @graphprotocol/graph-ts fornece as seguintes APIs:
- Uma API 
ethereumpara trabalhar com contratos inteligentes, eventos, blocos, transações, e valores no Ethereum. - Uma API 
storepara carregar e guardar entidades dentro e fora do armazenamento do Graph Node. - Uma API 
logpara gravar mensagens ao resultado do Graph Node e ao Graph Explorer. - Uma API 
ipfspara carregar arquivos do IPFS. - Uma API 
jsonpara analisar dados em JSON. - Uma API 
cryptopara usar funções criptográficas. - Primitivos de baixo nível para traduzir entre sistemas de tipos diferentes, como Ethereum, JSON, GraphQL e AssemblyScript.
 
Versões
The apiVersion in the Subgraph manifest specifies the mapping API version which is run by Graph Node for a given Subgraph.
| Versão | Notas de atualização | 
|---|---|
| 0.0.9 | Adiciona novas funções de host eth_get_balance & hasCode | 
| 0.0.8 | Adiciona validação para existência de campos no schema ao salvar uma entidade. | 
| 0.0.7 | Classes TransactionReceipt e Log adicionadas aos tipos do EthereumCampo Campo receipt adicionado ao objeto Ethereum Event | 
| 0.0.6 | Campo nonce adicionado ao objeto Ethereum TransactionCampo baseFeePerGas adicionado ao objeto Ethereum Block | 
| 0.0.5 | AssemblyScript upgraded to version 0.19.10 (this includes breaking changes, please see the Migration Guide)ethereum.transaction.gasUsed renamed to ethereum.transaction.gasLimit | 
| 0.0.4 | Campo functionSignature adicionado ao objeto Ethereum SmartContractCall | 
| 0.0.3 | Campo from adicionado ao objeto de chamada no EthereumCallethereum.call.address renomeado para ethereum.call.to | 
| 0.0.2 | Campo input adicionado ao objeto Ethereum Transaction | 
Tipos Embutidos
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.
ByteArray
1import { 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 umu32little-endian. Não funciona em caso de overflow.toI32(): i32- Interpreta o arranjo de byte como umi32little-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 novoByteArrayque consiste dethisdiretamente seguido deotherconcatI32(other: i32) : ByteArray— retorna um novoByteArrayque consiste dethisdiretamente seguido pela representação em byte deother
BigDecimal
1import { 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 umBigDecimala partir de umBigInt.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 comox + y.minus(y: BigDecimal): BigDecimal— pode ser escrito comox - y.times(y: BigDecimal): BigDecimal— pode serx * y.div(y: BigDecimal): BigDecimal— pode ser escrito comox / y.equals(y: BigDecimal): bool— pode serx == y.notEqual(y: BigDecimal): bool— pode serx != y.lt(y: BigDecimal): bool— pode serx < y.le(y: BigDecimal): bool– pode serx <= y.gt(y: BigDecimal): bool– pode serx > y.ge(y: BigDecimal): bool– pode serx >= y.neg(): BigDecimal- pode ser-x.
BigInt
1import { 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 umBigIntde umi32. - 
BigInt.fromString(s: string): BigInt– Analisa umBigIntde uma cadeia. - 
BigInt.fromUnsignedBytes(x: Bytes): BigInt— Interpretabytescomo um inteiro little-endian, não assinado. Se a sua entrada for big-endian, chame pelo.reverse()primeiro. - 
BigInt.fromSignedBytes(x: Bytes): BigInt— Interpretabytescomo um inteiro little-endian, assinado. Se a sua entrada for big-endian, chame pelo.reverse()primeiro.Conversões de tipo
 - 
x.toHex(): string— transforma oBigIntnuma cadeia de caracteres hexadecimais. - 
x.toString(): string— transforma oBigIntnuma cadeia de números decimais. - 
x.toI32(): i32— retorna oBigIntcomo umi32; falha se o valor não couber noi32. É bom verificar ox.isI32()primeiro. - 
x.toBigDecimal(): BigDecimal— converte num decimal sem fracionário. 
Matemática
x.plus(y: BigInt): BigInt– pode ser escrito comox + y.x.minus(y: BigInt): BigInt– pode ser escrito comox - y.x.times(y: BigInt): BigInt– pode ser escrito comox * y.x.div(y: BigInt): BigInt– pode ser escrito comox / y.x.mod(y: BigInt): BigInt– pode ser escrito comox % y.x.equals(y: BigInt): bool– pode ser escrito comox == y.x.notEqual(y: BigInt): bool– pode ser escrito comox != y.x.lt(y: BigInt): bool– pode ser escrito comox < y.x.le(y: BigInt): bool– pode ser escrito comox <= y.x.gt(y: BigInt): bool– pode ser escrito comox > y.x.ge(y: BigInt): bool– pode ser escrito comox >= 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 umi32.x.abs(): BigInt— Valor absoluto.x.pow(exp: u8): BigInt— Exponenciação.bitOr(x: BigInt, y: BigInt): BigInt— pode ser escrito comox | y.bitAnd(x: BigInt, y: BigInt): BigInt— pode ser escrito comox & y.leftShift(x: BigInt, bits: u8): BigInt– pode ser escrito comox << y.rightShift(x: BigInt, bits: u8): BigInt– pode ser escrito comox >> y.
TypedMap
1import { 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 tipoKe valores do tipoVmap.set(key: K, value: V): void— coloca o valor dokeycomovaluemap.getEntry(key: K): TypedMapEntry<K, V> | null— retorna o par de valor-chave para umkeyounullse okeynão existir no mapamap.get(key: K): V | null— retorna o valor para umkeyounullse okeynão existir no mapamap.isSet(key: K): bool— retornatruese okeyexistir no mapa efalsese não existir
Bytes
1import { 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 cadeiahex, que deve consistir de um número par de dígitos hexadecimais para umByteArray. Opcionalmente, a cadeiahexpode começar com0xfromI32(i: i32) : Bytes- Converte oiem um arranjo de bytes
Conversões de tipo
b.toHex()— retorna uma cadeia hexadecimal que representa os bytes no arranjob.toString()— converte os bytes no arranjo para uma cadeia de caracteres em unicodeb.toBase58()— transforma um valor de Ethereum Bytes numa codificação base58 (usado para hashes IPFS)
Operadores
b.concat(other: Bytes) : Bytes- - retorna um novoBytesque consiste dethisdiretamente seguido porotherb.concatI32(other: i32) : ByteArray— retorna um novoBytesque consiste dethisdiretamente seguido pela representação em byte deother
Address
1import { 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 umAddressa partir de uma cadeia hexadecimalAddress.fromBytes(b: Bytes): Address— cria umAddressa partir dob, que deve ter o comprimento exato de 20 bytes. Preencher um valor com menos ou mais bytes causará um erro
Armazenamento da API
1import { store } from '@graphprotocol/graph-ts'A API store permite carregar, salvar e remover entidades do/para o armazenamento do Graph Node.
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 Graph CLI 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.
Como criar entidades
Este é um padrão comum para a criação de entidades de eventos do Ethereum.
1// Importar a classe de evento de transferência gerada da ABI ERC202import { Transfer as TransferEvent } from '../generated/ERC20/ERC20'34// Importar o tipo de entidade de transferência gerado do schema do GraphQL5import { Transfer } from '../generated/schema'67// Handler de evento de transferência8export function handleTransfer(event: TransferEvent): void {9  // Criar uma entidade de Transferência, usando o hash da transação como a ID da entidade10  let id = event.transaction.hash11  let transfer = new Transfer(id)1213  // Determinar propriedades na entidade, usando os parâmetros do evento14  transfer.from = event.params.from15  transfer.to = event.params.to16  transfer.amount = event.params.amount1718  // Salvar a entidade no armazenamento19  transfer.save()20}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 identificador exclusivo para evitar colisões com outras entidades. É bastante comum que parâmetros de evento incluam um identificador exclusivo que pode ser usado.
Nota: Usar o hash de transação como ID supõe que nenhum outro evento na mesma transação cria entidades com este hash como o ID.
Como carregar entidades a partir do armazenamento
Se uma entidade já existe, ela pode ser carregada do armazenamento com os seguintes comandos:
1let id = event.transaction.hash // ou como a ID for construída2let transfer = Transfer.load(id)3if (transfer == null) {4  transfer = new Transfer(id)5}67// Use a entidade Transfer como antesComo 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
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 a recuperação de entidades que já foram criadas ou atualizadas no bloco atual. Uma situação típica para isso é que um manipulador cria uma transação a partir de algum evento em cadeia, e um handler posterior quer acessar esta transação — se ela existir.
- In the case where the transaction does not exist, the Subgraph will have to go to the database simply to find out that the entity does not exist. If the Subgraph author already knows that the entity must have been created in the same block, using 
loadInBlockavoids this database roundtrip. - For some Subgraphs, these missed lookups can contribute significantly to the indexing time.
 
1let id = event.transaction.hash // ou como a ID for construída2let transfer = Transfer.load(id)3if (transfer == null) {4  transfer = new Transfer(id)5}67// Use a entidade Transfer como antesNota: 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
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:
1type Token @entity {2  id: ID!3  holder: Holder!4  color: String5}67type Holder @entity {8  id: ID!9  tokens: [Token!]! @derivedFrom(field: "holder")10}O código a seguir carregará a entidade Token de que foi derivada a entidade Holder:
1let holder = Holder.load('test-id')2// Carrega as entidades de Token associadas com um titular dado3let tokens = holder.tokens.load()Como atualizar entidades existentes
Há duas maneiras de atualizar uma entidade existente:
- 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. - 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:
1let transfer = new Transfer(id)2transfer.from = ...3transfer.to = ...4transfer.amount = ...Também é possível cancelar propriedades com uma das seguintes instruções:
1transfer.from.unset()2transfer.from = nullIsto 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!]!.
1// Isto não funcionará2entity.numbers.push(BigInt.fromI32(1))3entity.save()45// Isto funcionará6let numbers = entity.numbers7numbers.push(BigInt.fromI32(1))8entity.numbers = numbers9entity.save()Como remover entidades do armazenamento
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:
1import { store } from '@graphprotocol/graph-ts'2...3let id = event.transaction.hash4store.remove('Transfer', id)API do Ethereum
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
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 built-in types take place behind the scenes so that Subgraph authors do not have to worry about them.
The following example illustrates this. Given a Subgraph schema like
1type Transfer @entity {2  id: Bytes!3  from: Bytes!4  to: Bytes!5  amount: BigInt!6}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:
1let id = event.transaction.hash2let transfer = new Transfer(id)3transfer.from = event.params.from4transfer.to = event.params.to5transfer.amount = event.params.amount6transfer.save()Eventos e Dados de Blocos/Transações
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):
1class Event {2  address: Address3  logIndex: BigInt4  transactionLogIndex: BigInt5  logType: string | null6  block: Block7  transaction: Transaction8  parameters: Array<EventParam>9  receipt: TransactionReceipt | null10}1112class Block {13  hash: Bytes14  parentHash: Bytes15  unclesHash: Bytes16  author: Address17  stateRoot: Bytes18  transactionsRoot: Bytes19  receiptsRoot: Bytes20  number: BigInt21  gasUsed: BigInt22  gasLimit: BigInt23  timestamp: BigInt24  difficulty: BigInt25  totalDifficulty: BigInt26  size: BigInt | null27  baseFeePerGas: BigInt | null28}2930class Transaction {31  hash: Bytes32  index: BigInt33  from: Address34  to: Address | null35  value: BigInt36  gasLimit: BigInt37  gasPrice: BigInt38  input: Bytes39  nonce: BigInt40}4142class TransactionReceipt {43  transactionHash: Bytes44  transactionIndex: BigInt45  blockHash: Bytes46  blockNumber: BigInt47  cumulativeGasUsed: BigInt48  gasUsed: BigInt49  contractAddress: Address50  logs: Array<Log>51  status: BigInt52  root: Bytes53  logsBloom: Bytes54}5556class Log {57  address: Address58  topics: Array<Bytes>59  data: Bytes60  blockHash: Bytes61  blockNumber: Bytes62  transactionHash: Bytes63  transactionIndex: BigInt64  logIndex: BigInt65  transactionLogIndex: BigInt66  logType: string67  removed: bool | null68}Acesso ao Estado do Contrato Inteligente
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.
É comum acessar o contrato de qual origina um evento. Isto é feito com o seguinte código:
1// Importar a classe do contrato gerado e a classe do evento de transferência gerado2import { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'3// Import the generated entity class4import { Transfer } from '../generated/schema'56export function handleTransfer(event: TransferEvent) {7  // Ligar o contrato ao endereço que emitiu o evento8  let contract = ERC20Contract.bind(event.address)910  // Acessar variáveis e funções de estado fazendo chamadas11  let erc20Symbol = contract.symbol()12}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.
Any other contract that is part of the Subgraph can be imported from the generated code and can be bound to a valid address.
Como Lidar com Chamadas Revertidas
Se houver reversão dos métodos somente-leitura do seu contrato, cuide disso chamando o método do contrato gerado prefixado com try_.
- Por exemplo, o contrato da Gravity expõe o método 
gravatarToOwner. Este código poderia manusear uma reversão nesse método: 
1let gravity = Gravity.bind(event.address)2let callResult = gravity.try_gravatarToOwner(gravatar)3if (callResult.reverted) {4  log.info('getGravatar reverted', [])5} else {6  let owner = callResult.value7}Observe que um Graph Node conectado a um cliente Geth ou Infura pode não detetar todas as reversões; se depender disto, recomendamos usar um Graph Node conectado a um cliente Parity.
ABI de Codificação/Decodificaçã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.
1import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'23let tupleArray: Array<ethereum.Value> = [4  ethereum.Value.fromAddress(Address.fromString('0x0000000000000000000000000000000000000420')),5  ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(62)),6]78let tuple = tupleArray as ethereum.Tuple910let encoded = ethereum.encode(ethereum.Value.fromTuple(tuple))!1112let decoded = ethereum.decode('(address,uint256)', encoded)Para mais informações:
- ABI Spec
 - Codificação e decodificação da biblioteca/CLI do Rust
 - Um exemplo mais complexo.
 
Saldo de um Endereç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.
1import { ethereum } from '@graphprotocol/graph-ts'23let address = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')4let balance = ethereum.getBalance(address) // retorna o saldo em BigIntComo Conferir Se Um Endereço é Um Contrato Ou Um EOA
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.
1import { ethereum } from '@graphprotocol/graph-ts'23let contractAddr = Address.fromString('0x2E645469f354BB4F5c8a05B3b30A929361cf77eC')4let isContract = ethereum.hasCode(contractAddr).inner // returns true56let eoa = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')7let isContract = ethereum.hasCode(eoa).inner // retorna falseAPI de Logging
1import { 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.
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– logs a critical message and terminates the 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.
1log.info('Message to be displayed: {}, {}, {}', [value.toString(), anotherValue.toString(), 'already a string'])Como logar um ou mais valores
Como logar um único valor
No exemplo abaixo, o valor de string “A” é passado a um arranjo para tornar-se ['A'] antes de ser registado no log:
1let myValue = 'A'23export function handleSomeEvent(event: SomeEvent): void {4  // Mostra : "My value is: A"5  log.info('My value is: {}', [myValue])6}Como logar uma única entrada de um arranjo existente
No exemplo abaixo, só é logado o primeiro valor do arranjo do argumento, apesar de haver três valores no arranjo.
1let myArray = ['A', 'B', 'C']23export function handleSomeEvent(event: SomeEvent): void {4  // Displays : "My value is: A"  (Apesar de três valores serem passados ao `log.info`)5  log.info('My value is: {}', myArray)6}Como logar várias entradas de um arranjo existente
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.
1let myArray = ['A', 'B', 'C']23export function handleSomeEvent(event: SomeEvent): void {4  // Mostra : "My first value is: A, second value is: B, third value is: C"5  log.info('My first value is: {}, second value is: {}, third value is: {}', myArray)6}Como logar uma entrada específica de um arranjo existente
Para mostrar um valor específico no arranjo, forneça o valor indexado.
1export function handleSomeEvent(event: SomeEvent): void {2  // Mostra : "My third value is C"3  log.info('My third value is: {}', [myArray[2]])4}Como logar informações de eventos
O exemplo abaixo loga o número do bloco, hash do bloco e o hash da transação de um evento:
1import { log } from '@graphprotocol/graph-ts'23export function handleSomeEvent(event: SomeEvent): void {4  log.debug('Block number: {}, block hash: {}, transaction hash: {}', [5    event.block.number.toString(), // "47596000"6    event.block.hash.toHexString(), // "0x..."7    event.transaction.hash.toHexString(), // "0x..."8  ])9}API do IPFS
1import { 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:
1// Coloque isto dentro de um handler de evento no mapeamento2let hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'3let data = ipfs.cat(hash)45// Locais como `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`6// que incluem arquivos em diretorias também são apoiados7let path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'8let 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:
1import { JSONValue, Value } from '@graphprotocol/graph-ts'23export function processItem(value: JSONValue, userData: Value): void {4  // Ver a documentação do JSONValue para detalhes sobre5  // como lidar com valores JSON6  let obj = value.toObject()7  let id = obj.get('id')8  let title = obj.get('title')910  if (!id || !title) {11    return12  }1314  // Callbacks também podem criar entidades15  let newItem = new Item(id)16  newItem.title = title.toString()17  newitem.parent = userData.toString() // Set parent to "parentId"18  newitem.save()19}2021// Coloque isto dentro de um handler de evento no mapeamento22ipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])2324// Como alternativa, use `ipfs.mapJSON`25ipfs.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.
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.
API de Criptografia
1import { 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
API JSON
1import { 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 arranjoBytesinterpretado como uma sequência válida de UTF-8json.try_fromBytes(data: Bytes): Result<JSONValue, boolean>— versão segura dojson.fromBytes, retorna um erro se houver falha no parsingjson.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 dojson.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:
1let value = json.fromBytes(...)2if (value.kind == JSONValueKind.BOOL) {3  ...4}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(): booleanvalue.toI64(): i64value.toF64(): f64value.toBigInt(): BigIntvalue.toString(): stringvalue.toArray(): Array<JSONValue>- (e depois converter oJSONValuecom um dos 5 métodos acima)
Referência de Conversões de Tipos
| Fonte(s) | Destino | Função de conversão | 
|---|---|---|
| Address | Bytes | nenhum | 
| Address | String | s.toHexString() | 
| BigDecimal | String | s.toString() | 
| BigInt | BigDecimal | s.toBigDecimal() | 
| BigInt | String (hexadecimal) | s.toHexString() ou s.toHex() | 
| BigInt | String (unicode) | s.toString() | 
| BigInt | i32 | s.toI32() | 
| Boolean | Boolean | nenhum | 
| Bytes (assinado) | BigInt | BigInt.fromSignedBytes(s) | 
| Bytes (não assinado) | BigInt | BigInt.fromUnsignedBytes(s) | 
| Bytes | String (hexadecimal) | s.toHexString() ou 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 | nenhum | 
| int32 | i32 | nenhum | 
| int32 | BigInt | BigInt.fromI32(s) | 
| uint24 | i32 | nenhum | 
| int64 - int256 | BigInt | nenhum | 
| uint32 - uint256 | BigInt | nenhum | 
| 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) | 
Metadados de Fontes de Dados
É 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(): AddressdataSource.network(): stringdataSource.context(): DataSourceContext
Entidade e DataSourceContext
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): voidsetI32(key: string, value: i32): voidsetBigInt(key: string, value: BigInt): voidsetBytes(key: string, value: Bytes): voidsetBoolean(key: string, value: bool): voidsetBigDecimal(key, value: BigDecimal): voidgetString(key: string): stringgetI32(key: string): i32getBigInt(key: string): BigIntgetBytes(key: string): BytesgetBoolean(key: string): booleangetBigDecimal(key: string): BigDecimal
DataSourceContext no Manifest
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.
Aqui está um exemplo de YAML que ilustra o uso de vários tipos na seção context:
1dataSources:2  - kind: ethereum/contract3    name: ContractName4    network: mainnet5    context:6      bool_example:7        type: Bool8        data: true9      string_example:10        type: String11        data: 'hello'12      int_example:13        type: Int14        data: 4215      int8_example:16        type: Int817        data: 12718      big_decimal_example:19        type: BigDecimal20        data: '10.99'21      bytes_example:22        type: Bytes23        data: '0x68656c6c6f'24      list_example:25        type: List26        data:27          - type: Int28            data: 129          - type: Int30            data: 231          - type: Int32            data: 333      big_int_example:34        type: BigInt35        data: '1000000000000000000000000'Bool: Especifica um valor Boolean (trueoufalse).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.
This context is then accessible in your Subgraph mapping files, enabling more dynamic and configurable Subgraphs.