subgraphs > Developing > Creating > AssemblyScript API > API AssemblyScript

API AssemblyScript

Reading time: 21 min

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:

  • The Graph TypeScript library (graph-ts)
  • Code generated from subgraph files by graph codegen

You can also add other libraries as dependencies, as long as they are compatible with AssemblyScript.

Since language mappings are written in AssemblyScript, it is useful to review the language and standard library features from the AssemblyScript wiki.

Riferimento API

Collegamento a questa sezione

La libreria @graphprotocol/graph-ts fornisce le seguenti API:

  • Un'API ethereum per lavorare con gli smart contract di Ethereum, gli eventi, i blocchi, le transazioni e i valori di Ethereum.
  • Un'API store per caricare e salvare entità da e verso il Graph Node store.
  • A log API to log messages to the Graph Node output and Graph Explorer.
  • Un'API ipfs per caricare i file da IPFS.
  • Un'API json per analizzare i dati JSON.
  • Un'API crypto per utilizzare le funzioni crittografiche.
  • Primitive di basso livello per tradurre tra diversi sistemi di tipi come Ethereum, JSON, GraphQL e AssemblyScript.

La apiVersion nel manifest del subgraph specifica la versione dell'API di mappatura che viene eseguita da the Graph Node per un dato subgraph.

VersioneNote di rilascio
0.0.9Adds new host functions eth_get_balance & hasCode
0.0.8Adds validation for existence of fields in the schema when saving an entity.
0.0.7Aggiunte le classi TransactionReceipt e Log ai tipi di Ethereum
Aggiunto il campo receipt all'oggetto Ethereum Event
0.0.6Aggiunto il campo nonce all'oggetto Ethereum Transaction
Aggiunto baseFeePerGas all'oggetto Ethereum Block
0.0.5AssemblyScript aggiornato alla versione 0.19.10 (questo include modifiche di rottura, consultare la Guida alla migrazione)
ethereum.transaction.gasUsed rinominato in ethereum.transaction.gasLimit
0.0.4Aggiunto il campo functionSignature all'oggetto Ethereum SmartContractCall
0.0.3Aggiunto il campo from all'oggetto Ethereum Call
etherem.call.address rinominato in ethereum.call.to
0.0.2Aggiunto il campo input all'oggetto Ethereum Transaction

Documentation on the base types built into AssemblyScript can be found in the AssemblyScript wiki.

I seguenti tipi aggiuntivi sono forniti da @graphprotocol/graph-ts.

importare { ByteArray } da '@graphprotocol/graph-ts'

ByteArray rappresenta un array di u8.

Construction

  • fromI32(x: i32): ByteArray - Decompone x in byte.
  • fromHexString(hex: string): ByteArray - La lunghezza dell'input deve essere pari. Il prefisso 0x è facoltativo.

Type conversions

  • toHexString(): string - Converte in una stringa esadecimale con prefisso 0x.
  • toString(): string - Interpreta i byte come una string UTF-8.
  • toBase58(): string - Codifica i byte in una stringa base58.
  • toU32(): u32 - Interpreta i byte come un u32 little-endian. Viene lanciata in caso di overflow.
  • toI32(): i32 - Interpreta l'array di byte come un i32 little-endian. Viene lanciata in caso di overflow.

Operators

  • equals(y: ByteArray): bool- può essere scritto comex == y`.
  • concat(other: ByteArray) : ByteArray - restituisce un nuovo ByteArray costituito da this direttamente seguito da other
  • concatI32(other: i32) : ByteArray - restituisce un nuovo ByteArray costituito da this direttamente seguito dalla rappresentazione in byte di other
import { BigDecimal } from '@graphprotocol/graph-ts'

BigDecimal è usato per rappresentare decimali di precisione arbitraria.

Note: Internally BigDecimal is stored in IEEE-754 decimal128 floating-point format, 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 ufixed256x18 or equivalent.

Construction

  • constructor(bigInt: BigInt) - crea un BigDecimal da un BigInt.
  • static fromString(s: string): BigDecimal - analizza una stringa decimale.

Type conversions

  • toString(): string - stampa una stringa decimale.

Math

  • plus(y: BigDecimal): BigDecimal - può essere scritto come x + y.
  • minus(y: BigDecimal): BigDecimal - può essere scritto come x - y.
  • times(y: BigDecimal): BigDecimal - può essere scritto come x * y.
  • div(y: BigDecimal): BigDecimal - può essere scritto come x / y.
  • equals(y: BigDecimal): bool - può essere scritto come x == y.
  • notEqual(y: BigDecimal): bool - può essere scritto come x != y.
  • lt(y: BigDecimal): bool – può essere scritto come x < y.
  • le(y: BigDecimal): bool – può essere scritto come x <= y.
  • gt(y: BigDecimal): bool – può essere scritto come x > y.
  • ge(y: BigDecimal): bool – può essere scritto come x >= y.
  • neg(): BigDecimal - può essere scritto come -x.
import { BigInt } from '@graphprotocol/graph-ts'

BigInt è usato per rappresentare i grandi numeri interi. Questo include i valori Ethereum di tipo da uint32 a uint256 e da int64 a int256. Tutto ciò che è inferiore a uint32, come int32, uint24 o int8 è rappresentato come i32.

La classe BigInt ha la seguente API:

Construction

  • BigInt.fromI32(x: i32): BigInt - crea un BigInt da un i32.

  • BigInt.fromString(s: string): BigInt- Analizza un BigInt da una stringa.

  • BigInt.fromUnsignedBytes(x: Bytes): BigInt - Interpreta bytes come un intero senza segno, little-endian. Se l'input è big-endian, chiamare prima .reverse().

  • BigInt.fromSignedBytes(x: Bytes): BigInt - Interpreta bytes come un intero firmato, little-endian. Se l'input è big-endian, chiamare prima .reverse().

    Type conversions

  • x.toHex(): string - trasforma BigInt in una stringa di caratteri esadecimali.

  • x.toString(): string - trasforma BigInt in una stringa di numeri decimali.

  • x.toI32(): i32 - restituisce il BigInt come i32; fallisce se il valore non entra in i32. È una buona idea controllare prima x.isI32().

  • x.toBigDecimal(): BigDecimal - converte in un decimale senza parti frazionarie.

Math

  • x.plus(y: BigInt): BigInt - può essere scritto come x + y.
  • x.minus(y: BigInt): BigInt - può essere scritto come x - y.
  • x.times(y: BigInt): BigInt - può essere scritto come x * y.
  • x.div(y: BigInt): BigInt - può essere scritto come x / y.
  • x.mod(y: BigInt): BigInt - può essere scritto come x % y.
  • x.equals(y: BigInt): bool - può essere scritto come x == y.
  • x.notEqual(y: BigInt): bool - può essere scritto come x != y.
  • x.lt(y: BigInt): bool - può essere scritto come x < y.
  • x.le(y: BigInt): bool - può essere scritto come x <= y.
  • x.gt(y: BigInt): bool - può essere scritto come x > y.
  • x.ge(y: BigInt): bool - può essere scritto come x >= y.
  • x.neg(): BigInt - può essere scritto come -x.
  • x.divDecimal(y: BigDecimal): BigDecimal - divide per un decimale, dando un risultato decimale.
  • x.isZero(): bool - Conviene prima verificare se il numero è zero.
  • x.isI32(): bool - Controlla se il numero è compreso in un i32.
  • x.abs(): BigInt - Valore assoluto.
  • x.pow(exp: u8): BigInt - Esponenziale.
  • bitOr(x: BigInt, y: BigInt): BigInt - può essere scritto come x | y.
  • bitAnd(x: BigInt, y: BigInt): BigInt - può essere scritto come x & y.
  • leftShift(x: BigInt, bits: u8): BigInt - può essere scritto come x << y.
  • rightShift(x: BigInt, bits: u8): BigInt - può essere scritto come x >> y.
import { TypedMap } from '@graphprotocol/graph-ts'

TypedMap può essere usato per memorizzare coppie key-value. Vedere [questo esempio] (https://github.com/graphprotocol/aragon-subgraph/blob/29dd38680c5e5104d9fdc2f90e740298c67e4a31/individual-dao-subgraph/mappings/constants.ts#L51).

La classe TypedMap ha la seguente API:

  • new TypedMap<K, V>() - crea una mappa vuota con chiavi di tipo K e valori di tipo V
  • map.set(chiave: K, valore: V): void - imposta il valore di chiave su valore
  • map.getEntry(key: K): TypedMapEntry<K, V> | null - restituisce la coppia key-value per una key o null se la key non esiste nella mappa
  • map.get(chiave: K): V | null - restituisce il valore di una chiave o null se la chiave non esiste nella mappa
  • map.isSet(key: K): bool - restituisce true se la key esiste nella mappa e false se non esiste
import { Bytes } from '@graphprotocol/graph-ts'

Bytes è usato per rappresentare array di byte di lunghezza arbitraria. Questo include i valori Ethereum di tipo bytes, bytes32, ecc.

La classe Bytes estende Uint8Array di AssemblyScript e supporta tutte le funzionalità di Uint8Array, oltre ai seguenti nuovi metodi:

Construction

  • fromHexString(hex: string) : Bytes - Converte la stringa hex, che deve essere composta da un numero pari di cifre esadecimali, in un ByteArray. La stringa hex può facoltativamente iniziare con 0x
  • fromI32(i: i32) : Bytes - Converte i in un array di byte

Type conversions

  • b.toHex() - restituisce una stringa esadecimale che rappresenta i byte nell'array
  • b.toString() - converte i byte dell'array in una stringa di caratteri unicode
  • b.toBase58() - trasforma un valore Ethereum Bytes in una codifica base58 (usata per gli hash IPFS)

Operators

  • b.concat(other: Bytes) : Bytes - - restituisce nuovi Bytes costituiti da this direttamente seguito da other
  • b.concatI32(other: i32) : ByteArray - restituisce nuovi Byte costituiti da this direttamente seguito dalla rappresentazione in byte di other
import { Address } from '@graphprotocol/graph-ts'

Address estende Bytes per rappresentare i valori di address di Ethereum.

Aggiunge il seguente metodo all'API Bytes:

  • Address.fromString(s: string): Address - crea un Address a partire da una stringa esadecimale
  • Address.fromBytes(b: Bytes): Address - crea un Address da b che deve essere lungo esattamente 20 byte. Se si passa un valore con un numero di byte inferiore o superiore, risulterà in un errore
import { store } from '@graphprotocol/graph-ts'

L'API store consente di caricare, salvare e rimuovere entità da e verso il 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 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.

Creazione di entità

Collegamento a questa sezione

Quello che segue è un modello comune per la creazione di entità a partire da eventi Ethereum.

// Import the Transfer event class generated from the ERC20 ABI
import { Transfer as TransferEvent } from '../generated/ERC20/ERC20'
// Import the Transfer entity type generated from the GraphQL schema
import { Transfer } from '../generated/schema'
// Transfer event handler
export function handleTransfer(event: TransferEvent): void {
// Create a Transfer entity, using the transaction hash as the entity ID
let id = event.transaction.hash
let transfer = new Transfer(id)
// Set properties on the entity, using the event parameters
transfer.from = event.params.from
transfer.to = event.params.to
transfer.amount = event.params.amount
// Save the entity to the store
transfer.save()
}

Quando un evento Transfer viene incontrato durante l'elaborazione della chain, viene passato al gestore dell'evento handleTransfer usando il tipo Transfer generato (qui alias TransferEvent per evitare un conflitto di nomi con il tipo di entità). Questo tipo consente di accedere a dati quali la transazione genitore dell'evento e i suoi parametri.

Each entity must have a unique ID to avoid collisions with other entities. It is fairly common for event parameters to include a unique identifier that can be used.

Note: Using the transaction hash as the ID assumes that no other events in the same transaction create entities with this hash as the ID.

Caricare le entità dallo store

Collegamento a questa sezione

Se un'entità esiste già, può essere caricata dall'archivio con la seguente procedura:

let id = event.transaction.hash // or however the ID is constructed
let 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 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.

Ricerca delle entità create all'interno di un blocco

Collegamento a questa sezione

A partire da graph-node v0.31.0, @graphprotocol/graph-ts v0.30.0 e @graphprotocol/graph-cli v0.49.0 il metodo loadInBlock è disponibile per tutti i tipi di entità.

The store API facilitates the retrieval of entities that were created or updated in the current block. A typical situation for this is that one handler creates a transaction from some on-chain event, and a later handler wants to access this transaction if it exists.

  • 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 loadInBlock avoids this database roundtrip.
  • For some subgraphs, these missed lookups can contribute significantly to the indexing time.
let id = event.transaction.hash // or however the ID is constructed
let transfer = Transfer.loadInBlock(id)
if (transfer == null) {
transfer = new Transfer(id)
}
// Use the Transfer entity as before

Nota: Se non esiste un'entità creata nel blocco dato, loadInBlock restituirà null anche se esiste un'entità con l'ID dato nel negozio.

Ricerca di entità derivate

Collegamento a questa sezione

A partire da graph-node v0.31.0, @graphprotocol/graph-ts v0.31.0 e @graphprotocol/graph-cli v0.51.0 è disponibile il metodo loadRelated.

Ciò consente di caricare i campi entità derivati da un gestore di eventi. Per esempio, dato il seguente schema:

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

Il codice seguente carica l'entità Token da cui è derivata l'entità Holder:

let holder = Holder.load('test-id')
// Load the Token entities associated with a given holder
let tokens = holder.tokens.load()

Aggiornamento di entità esistenti

Collegamento a questa sezione

Esistono due modi per aggiornare un'entità esistente:

  1. Caricare l'entità con, ad esempio, Transfer.load(id), impostare le proprietà sull'entità, quindi .save() riportarla in archivio.
  2. È sufficiente creare l'entità con, ad esempio, new Transfer(id), impostare le proprietà sull'entità e quindi .save() nel negozio. Se l'entità esiste già, le modifiche vengono unite ad essa.

La modifica delle proprietà è semplice nella maggior parte dei casi, grazie ai setter di proprietà generati:

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

È anche possibile disattivare le proprietà con una delle due istruzioni seguenti:

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

Questo funziona solo con le proprietà opzionali, cioè quelle dichiarate senza un ! in GraphQL. Due esempi potrebbero essere owner: Bytes o amount: BigInt.

L'aggiornamento delle proprietà degli array è un po' più complicato, poiché l'ottenimento di un array da un'entità crea una copia di tale array. Ciò significa che le proprietà dell'array devono essere impostate di nuovo in modo esplicito dopo aver modificato l'array. Quanto segue presuppone che l'entità abbia un campo numbers: [BigInt!]!.

// This won't work
entity.numbers.push(BigInt.fromI32(1))
entity.save()
// This will work
let numbers = entity.numbers
numbers.push(BigInt.fromI32(1))
entity.numbers = numbers
entity.save()

Rimozione di entità dal negozio

Collegamento a questa sezione

Attualmente non c'è modo di rimuovere un'entità tramite i tipi generati. Per rimuovere un'entità è necessario passare il nome del tipo di entità e l'ID dell'entità a store.remove:

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

L'API di Ethereum fornisce l'accesso agli smart contract, alle variabili di stato pubbliche, alle funzioni dei contratti, agli eventi, alle transazioni, ai blocchi e alla codifica/decodifica dei dati di Ethereum.

Supporto per i tipi di Ethereum

Collegamento a questa sezione

Come per le entità, graph codegen genera classi per tutti gli smart contract e gli eventi utilizzati in un subgraph. Per questo, gli ABI dei contratti devono far parte dell'origine dati nel manifest del subgraph. In genere, i file ABI sono memorizzati in una cartella abis/.

Con le classi generate, le conversioni tra i tipi di Ethereum e i tipi incorporati avvengono dietro le quinte, in modo che gli autori dei subgraph non debbano preoccuparsene.

L'esempio seguente lo illustra. Dato uno schema di subgraph come

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

e una firma di evento Transfer(address,address,uint256) su Ethereum, i valori from, to e amount di tipo address, address e uint256 sono convertiti in Address e BigInt, consentendo di passarli alle proprietà Bytes! e BigInt! dell'entità 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()

Eventi e dati di blocco/transazione

Collegamento a questa sezione

Gli eventi di Ethereum passati ai gestori di eventi, come l'evento Transfer negli esempi precedenti, non solo forniscono accesso ai parametri dell'evento, ma anche alla transazione genitore e al blocco di cui fanno parte. I seguenti dati possono essere ottenuti dalle istanze di event (queste classi fanno parte del modulo ethereum di 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
}

Accesso allo stato dello smart contract

Collegamento a questa sezione

Il codice generato da graph codegen include anche classi per gli smart contract utilizzati nel subgraph. Queste possono essere utilizzate per accedere alle variabili di stato pubbliche e per chiamare le funzioni del contratto nel blocco corrente.

Un modello comune è quello di accedere al contratto da cui proviene un evento. Questo si ottiene con il seguente codice:

// Import the generated contract class and generated Transfer event class
import { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'
// Import the generated entity class
import { Transfer } from '../generated/schema'
export function handleTransfer(event: TransferEvent) {
// Bind the contract to the address that emitted the event
let contract = ERC20Contract.bind(event.address)
// Access state variables and functions by calling them
let erc20Symbol = contract.symbol()
}

Transfer è alias di TransferEvent qui per evitare un conflitto di nomi con il tipo di entità

Finché il ERC20Contract su Ethereum ha una funzione pubblica di sola lettura chiamata symbol, questa può essere chiamata con .symbol(). Per le variabili di stato pubbliche viene creato automaticamente un metodo con lo stesso nome.

Qualsiasi altro contratto che faccia parte del subgraph può essere importato dal codice generato e può essere legato a un indirizzo valido.

Gestione delle chiamate annullate

Collegamento a questa sezione

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
}

Note: A Graph node connected to a Geth or Infura client may not detect all reverts. If you rely on this, we recommend using a Graph Node connected to a Parity client.

Codifica/decodifica ABI

Collegamento a questa sezione

I dati possono essere codificati e decodificati secondo il formato di codifica ABI di Ethereum utilizzando le funzioni encode e decode del modulo 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)

Per maggiori informazioni:

Balance of an Address

Collegamento a questa sezione

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

Check if an Address is a Contract or EOA

Collegamento a questa sezione

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 true
let eoa = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
let isContract = ethereum.hasCode(eoa).inner // returns false

API di registrazione

Collegamento a questa sezione
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.

L'API log include le seguenti funzioni:

  • log.debug(fmt: string, args: Array<string>): void - registra un messaggio di debug.
  • log.info(fmt: string, args: Array<string>): void - registra un messaggio informativo.
  • log.warning(fmt: string, args: Array<string>): void - registra un avviso.
  • log.error(fmt: string, args: Array<string>): void - registra un messaggio di errore.
  • log.critical(fmt: string, args: Array<string>): void - registra un messaggio critico and termina il subgraph.

L'API log accetta una stringa di formato e un array di valori stringa. Quindi sostituisce i segnaposto con i valori stringa dell'array. Il primo segnaposto {} viene sostituito dal primo valore dell'array, il secondo segnaposto {} viene sostituito dal secondo valore e così via.

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

Registrazione di uno o più valori

Collegamento a questa sezione
Registrazione di un singolo valore
Collegamento a questa sezione

Nell'esempio seguente, il valore stringa "A" viene passato in un array per diventare ['A'] prima di essere registrato:

let myValue = 'A'
export function handleSomeEvent(event: SomeEvent): void {
// Displays : "My value is: A"
log.info('My value is: {}', [myValue])
}
Registrazione di una singola voce da un array esistente
Collegamento a questa sezione

Nell'esempio seguente, viene registrato solo il primo valore dell'array di argomenti, nonostante l'array contenga tre valori.

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)
}

Registrazione di più voci da una matrice esistente

Collegamento a questa sezione

Ogni voce dell'array di argomenti richiede il proprio segnaposto {} nella stringa del messaggio di log. L'esempio seguente contiene tre segnaposto {} nel messaggio di log. Per questo motivo, tutti e tre i valori di myArray vengono registrati.

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)
}
Registrazione di una voce specifica da un array esistente
Collegamento a questa sezione

Per visualizzare un valore specifico della matrice, è necessario fornire il valore indicizzato.

export function handleSomeEvent(event: SomeEvent): void {
// Displays : "My third value is C"
log.info('My third value is: {}', [myArray[2]])
}
Registrazione delle informazioni sugli eventi
Collegamento a questa sezione

L'esempio seguente registra il numero di blocco, l'hash del blocco e l'hash della transazione di 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'

Gli smart contract di tanto in tanto ancorano i file IPFS sulla chain. Ciò consente alle mappature di ottenere gli hash IPFS dal contratto e di leggere i file corrispondenti da IPFS. I dati del file saranno restituiti come Byte, che di solito richiedono un'ulteriore elaborazione, ad esempio con l'API json documentata più avanti in questa pagina.

Dato un hash o un percorso IPFS, la lettura di un file da IPFS avviene come segue:

// Put this inside an event handler in the mapping
let hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'
let data = ipfs.cat(hash)
// Paths like `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`
// that include files in directories are also supported
let path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'
let data = ipfs.cat(path)

Nota: ipfs.cat non è deterministico al momento. Se il file non può essere recuperato attraverso la rete IPFS prima che la richiesta si esaurisca, restituirà null. Per questo motivo, vale sempre la pena di controllare che il risultato non sia null.

È anche possibile elaborare file di dimensioni maggiori in modo streaming con ipfs.map. La funzione si aspetta l'hash o il percorso di un file IPFS, il nome di un callback e i flag per modificare il suo comportamento:

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 values
let obj = value.toObject()
let id = obj.get('id')
let title = obj.get('title')
if (!id || !title) {
return
}
// Callbacks can also created entities
let 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 mapping
ipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])
// Alternatively, use `ipfs.mapJSON`
ipfs.mapJSON('Qm...', 'processItem', Value.fromString('parentId'))

L'unico flag attualmente supportato è json, che deve essere passato a ipfs.map. Con il flag json, il file IPFS deve essere costituito da una serie di valori JSON, un valore per riga. La chiamata a ipfs.map leggerà ogni riga del file, la deserializzerà in un JSONValue e chiamerà il callback per ognuno di essi. Il callback può quindi utilizzare le operazioni sulle entità per memorizzare i dati dal JSONValue. Le modifiche alle entità vengono memorizzate solo quando il gestore che ha chiamato ipfs.map termina con successo; nel frattempo, vengono mantenute in memoria e la dimensione del file che ipfs.map può elaborare è quindi limitata.

In caso di successo, ipfs.map restituisce void. Se una qualsiasi invocazione del callback causa un errore, il gestore che ha invocato ipfs.map viene interrotto e il subgraph viene contrassegnato come fallito.

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

L'API crypto rende disponibili funzioni crittografiche da usare nelle mappature. Al momento, ce n'è solo una:

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

I dati JSON possono essere analizzati utilizzando l'API json:

  • json.fromBytes(data: Bytes): JSONValue - analizza i dati JSON da un array di Bytes interpretati come una sequenza UTF-8 valida
  • json.try_fromBytes(data: Bytes): Result<JSONValue, boolean> - versione sicura di json.fromBytes, restituisce una variante di errore se il parsing è fallito
  • json.fromString(data: string): JSONValue - analizza i dati JSON da una string UTF-8 valida
  • json.try_fromString(data: string): Result<JSONValue, boolean> - versione sicura di json.fromString, restituisce una variante di errore se il parsing è fallito

La classe JSONValue fornisce un modo per estrarre valori da un documento JSON arbitrario. Poiché i valori JSON possono essere booleani, numeri, array e altro, JSONValue è dotato di una proprietà kind per verificare il tipo di valore:

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

Inoltre, esiste un metodo per verificare se il valore è null:

  • value.isNull(): boolean

Quando il tipo di un valore è certo, può essere convertito in un tipo incorporato usando uno dei seguenti metodi:

  • value.toBool(): boolean
  • value.toI64(): i64
  • value.toF64(): f64
  • value.toBigInt(): BigInt
  • value.toString(): string
  • value.toArray(): Array<JSONValue> - (e poi convertire JSONValue con uno dei 5 metodi precedenti)

Riferimento alle conversioni di tipo

Collegamento a questa sezione
Fonte(i)DestinazioneFunzione di conversione
AddressBytesnone
AddressStrings.toHexString()
BigDecimalStrings.toString()
BigIntBigDecimals.toBigDecimal()
BigIntString (hexadecimal)s.toHexString() o s.toHex()
BigIntString (unicode)s.toString()
BigInti32s.toI32()
BooleanBooleannone
Bytes (signed)BigIntBigInt.fromSignedBytes(s)
Bytes (unsigned)BigIntBigInt.fromUnsignedBytes(s)
BytesString (hexadecimal)s.toHexString() o s.toHex()
BytesString (unicode)s.toString()
BytesString (base58)s.toBase58()
Bytesi32s.toI32()
Bytesu32s.toU32()
BytesJSONjson.fromBytes(s)
int8i32none
int32i32none
int32BigIntBigInt.fromI32(s)
uint24i32none
int64 - int256BigIntnone
uint32 - uint256BigIntnone
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)

Metadati della Data Source

Collegamento a questa sezione

È possibile ispezionare l'indirizzo del contratto, la rete e il contesto dell'origine dati che ha invocato il gestore attraverso lo spazio dei nomi dataSource:

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

Entità e DataSourceContext

Collegamento a questa sezione

La classe base Entity e la classe figlia DataSourceContext hanno degli helper per impostare e ottenere dinamicamente i campi:

  • 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 nel manifesto

Collegamento a questa sezione

La sezione contesto all'interno di dataSources consente di definire coppie chiave-valore accessibili nelle mappature dei subgraph. I tipi disponibili sono Bool, String, Int, Int8, BigDecimal, Bytes, List e BigInt.

Ecco un esempio YAML che illustra l'uso di vari tipi nella sezione 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: Specifica un valore booleano (true o false).
  • String: Specifica un valore string.
  • Int: Specifica un numero intero a 32 bit.
  • Int8: Specifica un numero intero a 8 bit.
  • BigDecimal: Specifica un numero decimale. Deve essere quotato.
  • Bytes: Specifica una string esadecimale.
  • List: Specifica un elenco di elementi. Ogni elemento deve specificare il suo tipo e i suoi dati.
  • BigInt: Specifica un valore intero di grandi dimensioni. Deve essere quotato a causa delle sue grandi dimensioni.

Questo contesto è quindi accessibile nei file di mappatura dei subgraph, consentendo di ottenere subgraph più dinamici e configurabili.

Modifica pagina

Precedente
Advance Subgraph Features
Successivo
Problemi comuni di AssemblyScript
Modifica pagina