20 minutes
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:
- 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
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.
Versioni
The apiVersion
in the Subgraph manifest specifies the mapping API version which is run by Graph Node for a given Subgraph.
Versione | Note di rilascio |
---|---|
0.0.9 | Adds new host functions eth_get_balance & hasCode |
0.0.8 | Adds validation for existence of fields in the schema when saving an entity. |
0.0.7 | Aggiunte le classi TransactionReceipt e Log ai tipi di EthereumAggiunto il campo receipt all’oggetto Ethereum Event |
0.0.6 | Aggiunto il campo nonce all’oggetto Ethereum TransactionAggiunto baseFeePerGas all’oggetto 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 | Aggiunto il campo functionSignature all’oggetto Ethereum SmartContractCall |
0.0.3 | Added from field to the Ethereum Call objectethereum.call.address renamed to ethereum.call.to |
0.0.2 | Aggiunto il campo input all’oggetto Ethereum Transaction |
Tipi integrati
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
.
ByteArray
1importare { ByteArray } da '@graphprotocol/graph-ts'
ByteArray
rappresenta un array di u8
.
Construction
fromI32(x: i32): ByteArray
- Decomponex
in byte.fromHexString(hex: string): ByteArray
- La lunghezza dell’input deve essere pari. Il prefisso0x
è facoltativo.
Type conversions
toHexString(): string
- Converte in una stringa esadecimale con prefisso0x
.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 unu32
little-endian. Viene lanciata in caso di overflow.toI32(): i32
- Interpreta l’array di byte come uni32
little-endian. Viene lanciata in caso di overflow.
Operators
- equals(y: ByteArray): bool
- può essere scritto come
x == y`. concat(other: ByteArray) : ByteArray
- restituisce un nuovoByteArray
costituito dathis
direttamente seguito daother
concatI32(other: i32) : ByteArray
- restituisce un nuovoByteArray
costituito dathis
direttamente seguito dalla rappresentazione in byte diother
BigDecimal
1import { 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 unBigDecimal
da unBigInt
.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 comex + y
.minus(y: BigDecimal): BigDecimal
- può essere scritto comex - y
.times(y: BigDecimal): BigDecimal
- può essere scritto comex * y
.div(y: BigDecimal): BigDecimal
- può essere scritto comex / y
.equals(y: BigDecimal): bool
- può essere scritto comex == y
.notEqual(y: BigDecimal): bool
- può essere scritto comex != y
.lt(y: BigDecimal): bool
– può essere scritto comex < y
.le(y: BigDecimal): bool
– può essere scritto comex <= y
.gt(y: BigDecimal): bool
– può essere scritto comex > y
.ge(y: BigDecimal): bool
– può essere scritto comex >= y
.neg(): BigDecimal
- può essere scritto come-x
.
BigInt
1import { 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 unBigInt
da uni32
. -
BigInt.fromString(s: string): BigInt
- Analizza unBigInt
da una stringa. -
BigInt.fromUnsignedBytes(x: Bytes): BigInt
- Interpretabytes
come un intero senza segno, little-endian. Se l’input è big-endian, chiamare prima.reverse()
. -
BigInt.fromSignedBytes(x: Bytes): BigInt
- Interpretabytes
come un intero firmato, little-endian. Se l’input è big-endian, chiamare prima.reverse()
.Type conversions
-
x.toHex(): string
- trasformaBigInt
in una stringa di caratteri esadecimali. -
x.toString(): string
- trasformaBigInt
in una stringa di numeri decimali. -
x.toI32(): i32
- restituisce ilBigInt
comei32
; fallisce se il valore non entra ini32
. È una buona idea controllare primax.isI32()
. -
x.toBigDecimal(): BigDecimal
- converte in un decimale senza parti frazionarie.
Math
x.plus(y: BigInt): BigInt
- può essere scritto comex + y
.x.minus(y: BigInt): BigInt
- può essere scritto comex - y
.x.times(y: BigInt): BigInt
- può essere scritto comex * y
.x.div(y: BigInt): BigInt
- può essere scritto comex / y
.x.mod(y: BigInt): BigInt
- può essere scritto comex % y
.x.equals(y: BigInt): bool
- può essere scritto comex == y
.x.notEqual(y: BigInt): bool
- può essere scritto comex != y
.x.lt(y: BigInt): bool
- può essere scritto comex < y
.x.le(y: BigInt): bool
- può essere scritto comex <= y
.x.gt(y: BigInt): bool
- può essere scritto comex > y
.x.ge(y: BigInt): bool
- può essere scritto comex >= 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 uni32
.x.abs(): BigInt
- Valore assoluto.x.pow(exp: u8): BigInt
- Esponenziale.bitOr(x: BigInt, y: BigInt): BigInt
- può essere scritto comex | y
.bitAnd(x: BigInt, y: BigInt): BigInt
- può essere scritto comex & y
.leftShift(x: BigInt, bits: u8): BigInt
- può essere scritto comex << y
.rightShift(x: BigInt, bits: u8): BigInt
- può essere scritto comex >> y
.
TypedMap
1import { 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 tipoK
e valori di tipoV
map.set(chiave: K, valore: V): void
- imposta il valore dichiave
suvalore
map.getEntry(key: K): TypedMapEntry<K, V> | null
- restituisce la coppia key-value per unakey
onull
se lakey
non esiste nella mappamap.get(chiave: K): V | null
- restituisce il valore di unachiave
onull
se lachiave
non esiste nella mappamap.isSet(key: K): bool
- restituiscetrue
se lakey
esiste nella mappa efalse
se non esiste
Bytes
1import { 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 stringahex
, che deve essere composta da un numero pari di cifre esadecimali, in unByteArray
. La stringahex
può facoltativamente iniziare con0x
fromI32(i: i32) : Bytes
- Convertei
in un array di byte
Type conversions
b.toHex()
- restituisce una stringa esadecimale che rappresenta i byte nell’arrayb.toString()
- converte i byte dell’array in una stringa di caratteri unicodeb.toBase58()
- trasforma un valore Ethereum Bytes in una codifica base58 (usata per gli hash IPFS)
Operators
b.concat(other: Bytes) : Bytes
- - restituisce nuoviBytes
costituiti dathis
direttamente seguito daother
b.concatI32(other: i32) : ByteArray
- restituisce nuoviByte
costituiti dathis
direttamente seguito dalla rappresentazione in byte diother
Address
1import { 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 unAddress
a partire da una stringa esadecimaleAddress.fromBytes(b: Bytes): Address
- crea unAddress
dab
che deve essere lungo esattamente 20 byte. Se si passa un valore con un numero di byte inferiore o superiore, risulterà in un errore
Store API
1import { 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à
Quello che segue è un modello comune per la creazione di entità a partire da eventi Ethereum.
1// Import the Transfer event class generated from the ERC20 ABI2import { Transfer as TransferEvent } from '../generated/ERC20/ERC20'34// Import the Transfer entity type generated from the GraphQL schema5import { Transfer } from '../generated/schema'67// Transfer event handler8export function handleTransfer(event: TransferEvent): void {9 // Create a Transfer entity, using the transaction hash as the entity ID10 let id = event.transaction.hash11 let transfer = new Transfer(id)1213 // Set properties on the entity, using the event parameters14 transfer.from = event.params.from15 transfer.to = event.params.to16 transfer.amount = event.params.amount1718 // Save the entity to the store19 transfer.save()20}
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
Se un’entità esiste già, può essere caricata dall’archivio con la seguente procedura:
1let id = event.transaction.hash // or however the ID is constructed2let transfer = Transfer.load(id)3if (transfer == null) {4 transfer = new Transfer(id)5}67// 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
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 onchain 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.
1let id = event.transaction.hash // or however the ID is constructed2let transfer = Transfer.loadInBlock(id)3if (transfer == null) {4 transfer = new Transfer(id)5}67// 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
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:
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}
Il codice seguente carica l’entità Token
da cui è derivata l’entità Holder
:
1let holder = Holder.load('test-id')2// Load the Token entities associated with a given holder3let tokens = holder.tokens.load()
Aggiornamento di entità esistenti
Esistono due modi per aggiornare un’entità esistente:
- Caricare l’entità con, ad esempio,
Transfer.load(id)
, impostare le proprietà sull’entità, quindi.save()
riportarla in archivio. - È 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:
1let transfer = new Transfer(id)2transfer.from = ...3transfer.to = ...4transfer.amount = ...
È anche possibile disattivare le proprietà con una delle due istruzioni seguenti:
1transfer.from.unset()2transfer.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!]!
.
1// This won't work2entity.numbers.push(BigInt.fromI32(1))3entity.save()45// This will work6let numbers = entity.numbers7numbers.push(BigInt.fromI32(1))8entity.numbers = numbers9entity.save()
Rimozione di entità dal negozio
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
:
1import { store } from '@graphprotocol/graph-ts'2...3let id = event.transaction.hash4store.remove('Transfer', id)
API Ethereum
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
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 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
:
1let id = event.transaction.hash2let transfer = new Transfer(id)3transfer.from = event.params.from4transfer.to = event.params.to5transfer.amount = event.params.amount6transfer.save()
Eventi e dati di blocco/transazione
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
):
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}
Accesso allo stato dello smart contract
The code generated by graph codegen
also includes classes for the smart contracts used in the Subgraph. These can be used to access public state variables and call functions of the contract at the current block.
Un modello comune è quello di accedere al contratto da cui proviene un evento. Questo si ottiene con il seguente codice:
1// Import the generated contract class and generated Transfer event class2import { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'3// Import the generated entity class4import { Transfer } from '../generated/schema'56export function handleTransfer(event: TransferEvent) {7 // Bind the contract to the address that emitted the event8 let contract = ERC20Contract.bind(event.address)910 // Access state variables and functions by calling them11 let erc20Symbol = contract.symbol()12}
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.
Any other contract that is part of the Subgraph can be imported from the generated code and can be bound to a valid address.
Gestione delle chiamate annullate
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:
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}
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
I dati possono essere codificati e decodificati secondo il formato di codifica ABI di Ethereum utilizzando le funzioni encode
e decode
del modulo 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)
Per maggiori informazioni:
- ABI Spec
- Codifica/decodifica libreria Rust/CLI
- More complex example.
Balance of an Address
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.
1import { ethereum } from '@graphprotocol/graph-ts'23let address = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')4let balance = ethereum.getBalance(address) // returns balance in BigInt
Check if an Address is a Contract or EOA
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
.
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 // returns false
API di registrazione
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.
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
– logs a critical message and terminates the 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.
1log.info('Message to be displayed: {}, {}, {}', [value.toString(), anotherValue.toString(), 'already a string'])
Registrazione di uno o più valori
Registrazione di un singolo valore
Nell’esempio seguente, il valore stringa “A” viene passato in un array per diventare ['A']
prima di essere registrato:
1let myValue = 'A'23export function handleSomeEvent(event: SomeEvent): void {4 // Displays : "My value is: A"5 log.info('My value is: {}', [myValue])6}
Registrazione di una singola voce da un array esistente
Nell’esempio seguente, viene registrato solo il primo valore dell’array di argomenti, nonostante l’array contenga tre valori.
1let myArray = ['A', 'B', 'C']23export function handleSomeEvent(event: SomeEvent): void {4 // Displays : "My value is: A" (Even though three values are passed to `log.info`)5 log.info('My value is: {}', myArray)6}
Registrazione di più voci da una matrice esistente
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.
1let myArray = ['A', 'B', 'C']23export function handleSomeEvent(event: SomeEvent): void {4 // Displays : "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}
Registrazione di una voce specifica da un array esistente
Per visualizzare un valore specifico della matrice, è necessario fornire il valore indicizzato.
1export function handleSomeEvent(event: SomeEvent): void {2 // Displays : "My third value is C"3 log.info('My third value is: {}', [myArray[2]])4}
Registrazione delle informazioni sugli eventi
L’esempio seguente registra il numero di blocco, l’hash del blocco e l’hash della transazione di un 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}
IPFS API
1import { ipfs } from '@graphprotocol/graph-ts'
Smart contracts occasionally anchor IPFS files onchain. This allows mappings to obtain the IPFS hashes from the contract and read the corresponding files from IPFS. The file data will be returned as Bytes
, which usually requires further processing, e.g. with the json
API documented later on this page.
Dato un hash o un percorso IPFS, la lettura di un file da IPFS avviene come segue:
1// Put this inside an event handler in the mapping2let hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'3let data = ipfs.cat(hash)45// Paths like `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`6// that include files in directories are also supported7let path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'8let 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:
1import { JSONValue, Value } from '@graphprotocol/graph-ts'23export function processItem(value: JSONValue, userData: Value): void {4 // See the JSONValue documentation for details on dealing5 // with JSON values6 let obj = value.toObject()7 let id = obj.get('id')8 let title = obj.get('title')910 if (!id || !title) {11 return12 }1314 // Callbacks can also created entities15 let newItem = new Item(id)16 newItem.title = title.toString()17 newitem.parent = userData.toString() // Set parent to "parentId"18 newitem.save()19}2021// Put this inside an event handler in the mapping22ipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])2324// Alternatively, use `ipfs.mapJSON`25ipfs.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.
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.
Crypto API
1import { 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
JSON API
1import { 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 diBytes
interpretati come una sequenza UTF-8 validajson.try_fromBytes(data: Bytes): Result<JSONValue, boolean>
- versione sicura dijson.fromBytes
, restituisce una variante di errore se il parsing è fallitojson.fromString(data: string): JSONValue
- analizza i dati JSON da unastring
UTF-8 validajson.try_fromString(data: string): Result<JSONValue, boolean>
- versione sicura dijson.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:
1let value = json.fromBytes(...)2if (value.kind == JSONValueKind.BOOL) {3 ...4}
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 convertireJSONValue
con uno dei 5 metodi precedenti)
Riferimento alle conversioni di tipo
Fonte(i) | Destinazione | Funzione di conversione |
---|---|---|
Address | Bytes | none |
Address | String | s.toHexString() |
BigDecimal | String | s.toString() |
BigInt | BigDecimal | s.toBigDecimal() |
BigInt | String (hexadecimal) | s.toHexString() o s.toHex() |
BigInt | String (unicode) | s.toString() |
BigInt | i32 | s.toI32() |
Boolean | Boolean | none |
Bytes (signed) | BigInt | BigInt.fromSignedBytes(s) |
Bytes (unsigned) | BigInt | BigInt.fromUnsignedBytes(s) |
Bytes | String (hexadecimal) | s.toHexString() o s.toHex() |
Bytes | String (unicode) | s.toString() |
Bytes | String (base58) | s.toBase58() |
Bytes | i32 | s.toI32() |
Bytes | u32 | s.toU32() |
Bytes | JSON | json.fromBytes(s) |
int8 | i32 | none |
int32 | i32 | none |
int32 | BigInt | BigInt.fromI32(s) |
uint24 | i32 | none |
int64 - int256 | BigInt | none |
uint32 - uint256 | BigInt | none |
JSON | boolean | s.toBool() |
JSON | i64 | s.toI64() |
JSON | u64 | s.toU64() |
JSON | f64 | s.toF64() |
JSON | BigInt | s.toBigInt() |
JSON | string | s.toString() |
JSON | Array | s.toArray() |
JSON | Object | s.toObject() |
String | Address | Address.fromString(s) |
Bytes | Address | Address.fromBytes(s) |
String | BigInt | BigInt.fromString(s) |
String | BigDecimal | BigDecimal.fromString(s) |
String (hexadecimal) | Bytes | ByteArray.fromHexString(s) |
String (UTF-8) | Bytes | ByteArray.fromUTF8(s) |
Metadati della Data Source
È 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
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
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
.
Ecco un esempio YAML che illustra l’uso di vari tipi nella sezione 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
: Specifica un valore booleano (true
ofalse
).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.
This context is then accessible in your Subgraph mapping files, enabling more dynamic and configurable Subgraphs.