subgraphs > Developing > Creating > AssemblyScript API > API AssemblyScript

API AssemblyScript

Reading time: 22 min

Remarque : Si vous avez créé un subgraph avant la version graph-cli/graph-ts 0.22.0, alors vous utilisez une ancienne version d'AssemblyScript. Il est recommandé de consulter le [Guide de Migration ] (/resources/release-notes/assemblyscript-migration-guide/).

Découvrez quelles APIs intégrées peuvent être utilisées lors de l'écriture des mappages de subgraph. Il existe deux types d'APIs disponibles par défaut :

Vous pouvez également ajouter d'autres bibliothèques comme dépendances, à condition qu'elles soient compatibles avec [AssemblyScript] (https://github.com/AssemblyScript/assemblyscript).

Étant donné que les mappages de langage sont écrits en AssemblyScript, il est utile de consulter les fonctionnalités de langage et de bibliothèque standard dans le du [wiki AssemblyScript] (https://github.com/AssemblyScript/assemblyscript/wiki).

Référence API

Lien vers cette section

La bibliothèque @graphprotocol/graph-ts fournit les API suivantes :

  • Une API ethereum pour travailler avec les contrats intelligents Ethereum, les événements, les blocs, les transactions et les valeurs Ethereum.
  • Une API store pour charger et enregistrer des entités depuis et vers le magasin Graph Node.
  • Une API log pour enregistrer des messages dans la sortie Graph Node et Graph Explorer.
  • Une API ipfs pour charger des fichiers depuis IPFS.
  • Une API json pour analyser les données JSON.
  • Une API crypto pour utiliser des fonctions cryptographiques.
  • Primitives de bas niveau pour traduire entre différents systèmes de types tels que Ethereum, JSON, GraphQL et AssemblyScript.

La apiVersion dans le manifeste du subgraph spécifie la version de l'API de mappage exécutée par Graph Node pour un subgraph donné.

VersionNotes de version
0.0.9Ajout de nouvelles fonctions hôtes eth_get_balance & hasCode
0.0.8Ajout de la validation pour l'existence des champs dans le schéma lors de l'enregistrement d'une entité.
0.0.7Ajout des classes TransactionReceipt et Logaux types Ethereum
Ajout du champ receipt à l'objet Ethereum Event
0.0.6Ajout du champ nonce à l'objet Ethereum Transaction
Ajout de baseFeePerGas à l'objet Ethereum Block
0.0.5AssemblyScript a été mis à niveau vers la version 0.19.10 (ceci inclut des changements importants, veuillez consulter le Guide de Migration)
. ethereum.transaction.gasUsed est renommé en ethereum.transaction.gasLimit
0.0.4Ajout du champ functionSignature à l'objet Ethereum SmartContractCall
0.0.3Ajout du champ from à l'objet Ethereum Call
etherem.call.address est renommé en ethereum.call.to
0.0.2Ajout du champ input à l'objet Ethereum Transaction

Types intégrés

Lien vers cette section

La documentation sur les types de base intégrés dans AssemblyScript se trouve dans le wiki AssemblyScript.

Les types additionnels suivants sont fournis par @graphprotocol/graph-ts.

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

ByteArray représente un tableau de u8.

Construction

  • fromI32(x: i32): ByteArray - Décompose x en octets.
  • fromHexString(hex: string): ByteArray - La longueur de la saisie doit être paire. Le préfixe 0x est optionnel.

Conversions de type

  • toHexString(): string - Convertit en une chaîne de caractères hexadécimale ayant comme préfixe 0x.
  • toString(): string - Interprète les octets comme une chaîne UTF-8.
  • toBase58(): string` - Encode les octets en une chaîne de caractères de type base58.
  • toU32(): u32 - Interprète les octets comme un u32 en little-endian. Envoie une exception en cas de dépassement.
  • toI32(): i32 - Interprète le tableau d'octets comme un i32 en little-endian. Envoie une exception en cas de dépassement.

Operateurs

  • equals(y: ByteArray): bool – peut être écrit comme x == y.
  • concat(other: ByteArray) : ByteArray - renvoie un nouveau ByteArray constitué de this directement suivi par other
  • concatI32(other: i32) : ByteArray - retourne un nouveau ByteArray constitué de this directement suivi par la représentation en octets de other
import { BigDecimal } from '@graphprotocol/graph-ts'

BigDecimal est utilisé pour représenter des décimales à précision arbitraire.

Remarque: En interne BigDecimal est stocké au format IEEE-754 décimal128 à virgule flottante, qui supporte 34 chiffres significatifs. Cela rend BigDecimal inapproprié pour représenter des types à virgule fixe pouvant dépasser 34 chiffres, comme un Solidity ufixed256x18 ou équivalent.

Construction

  • constructor(bigInt: BigInt) – crée un BigDecimal à partir d'un BigInt.
  • static fromString(s: string): BigDecimal – analyse à partir d'une chaîne de caractères décimaux.

Conversions de type

  • toString(): string – affiche en une chaîne de caractères décimaux.

Math

  • plus(y: BigDecimal): BigDecimal – peut être écrit comme x + y.
  • minus(y: BigDecimal): BigDecimal – peut être écrit comme x - y.
  • times(y: BigDecimal): BigDecimal – peut être écrit comme x * y.
  • div(y: BigDecimal): BigDecimal – peut être écrit comme x / y.
  • equals(y: BigDecimal): bool – peut être écrit comme x == y.
  • notEqual(y: BigDecimal): bool – peut être écrit comme x != y.
  • lt(y: BigDecimal): bool – peut être écrit comme x < y.
  • le(y: BigDecimal): bool – peut être écrit comme x <= y.
  • gt(y: BigDecimal): bool – peut être écrit comme x > y.
  • ge(y: BigDecimal): bool – peut être écrit comme x >= y.
  • neg(): BigDecimal - peut être écrit comme -x.
importer { BigInt } depuis '@graphprotocol/graph-ts'

BigInt est utilisé pour représenter de grands entiers. Cela inclut les valeurs Ethereum de type uint32 à uint256 et int64 àint256. Tout ce qui est en dessous de uint32, tel que int32, uint24 ou int8 est représenté sous forme de i32.

La classe BigInt possède l'API suivante :

Construction

  • BigInt.fromI32(x: i32): BigInt – crée un BigInt à partir d'un i32.

  • BigInt.fromString(s: string): BigInt– Analyse un BigInt à partir d'une chaîne de caractères.

  • BigInt.fromUnsignedBytes(x: Bytes): BigInt – Interprète bytes comme un entier non signé en little-endian. Si votre saisie est en big-endian, appelez d'abord .reverse().

  • BigInt.fromSignedBytes(x: Bytes): BigInt – Interprète bytes comme un entier signé en little-endian. Si votre saisie est en big-endian, appelez d'abord .reverse().

    Conversions de type

  • x.toHex(): string – transforme BigInt en une chaîne de caractères hexadécimaux.

  • x.toString(): string – transformeBigInt en une chaîne de caractères de nombres décimaux.

  • x.toI32(): i32 – renvoie le BigInt comme un i32; échoue si la valeur ne rentre pas dans un i32. Il est conseillé de vérifier d'abord x.isI32().

  • x.toBigDecimal(): BigDecimal - convertit en un nombre décimal sans virgule.

Math

  • x.plus(y: BigInt): BigInt – peut être écrit comme x + y.
  • x.minus(y: BigInt): BigInt – peut être écrit comme x - y.
  • x.times(y: BigInt): BigInt – peut être écrit comme x * y.
  • x.div(y: BigInt): BigInt – peut être écrit comme x / y.
  • x.mod(y: BigInt): BigInt – peut être écrit comme x % y.
  • x.equals(y: BigInt): bool – peut être écrit comme x == y.
  • x.notEqual(y: BigInt): bool – peut être écrit comme x != y.
  • x.lt(y: BigInt): bool – peut être écrit comme x < y.
  • x.le(y: BigInt): bool – peut être écrit comme x <= y.
  • x.gt(y: BigInt): bool – peut être écrit comme x > y.
  • x.ge(y: BigInt): bool – peut être écrit comme x >= y.
  • x.neg(): BigInt – peut être écrit comme -x.
  • x.divDecimal(y: BigDecimal): BigDecimal – divise par un nombre décimal, donnant un résultat décimal.
  • x.isZero(): bool – Est pratique pour vérifier si le nombre est zéro.
  • x.isI32(): bool – Vérifie si le nombre rentre dans un i32.
  • x.abs(): BigInt – Valeur absolue.
  • x.pow(exp: u8): BigInt – Exponentiation.
  • bitOr(x: BigInt, y: BigInt): BigInt – peut être écrit comme x | y.
  • bitAnd(x: BigInt, y: BigInt): BigInt – peut être écrit comme x & y.
  • leftShift(x: BigInt, bits: u8): BigInt – peut être écrit comme x << y.
  • rightShift(x: BigInt, bits: u8): BigInt – peut être écrit comme x >> y.
import { TypedMap } from '@graphprotocol/graph-ts'

TypedMap peut être utilisé pour stocker des paires clé-valeur. Consultez cet exemple.

La classe TypedMap possède l'API suivante :

  • new TypedMap<K, V>() – crée une carte vide avec des clés de type K et des valeurs de type V
  • map.set(key: K, value: V): void – définit la valeur de key à value
  • map.getEntry(key: K): TypedMapEntry<K, V> | null – renvoie la paire clé-valeur pour une key ou null si la key n'existe pas dans la carte
  • map.get(key: K): V | null – renvoie la valeur pour une key ou null si la key n'existe pas dans la carte
  • map.isSet(key: K): bool – renvoie true si la key existe dans la carte et false si ce n'est pas le cas
import { Bytes } from '@graphprotocol/graph-ts'

Bytes est utilisé pour représenter des tableaux d'octets de longueur arbitraire. Ceci inclut les valeurs Ethereum de type bytes, bytes32, etc.

La classe Bytes hérite de Uint8Array d'AssemblyScript et prend en charge toutes les fonctionnalités de Uint8Array ainsi que les nouvelles méthodes suivantes :

Construction

  • fromHexString(hex: string) : Bytes - Convertit la chaîne de caractères hex qui doit comporter un nombre pair de chiffres hexadécimaux en un ByteArray. La chaîne de caractères hex peut de façon optionnelle commencer par 0x
  • fromI32(i: i32) : Bytes - Convertit i en un tableau de d'octets

Conversions de type

  • b.toHex() – renvoie une chaîne de caractères hexadécimale représentant les octets dans le tableau
  • b.toString() – convertit les octets dans le tableau en une chaîne de caractères unicode
  • b.toBase58() – convertit une valeur Ethereum Bytes en codage de type base58 (utilisé pour les hachages IPFS)

Operateurs

  • b.concat(other: Bytes) : Bytes - - renvoie un nouveau Bytes constitué de this suivi directement de other
  • b.concatI32(other: i32) : ByteArray - renvoie un nouveau Bytes constitué de this suivi directement de la représentation en octets de other
import { Address } du '@graphprotocol/graph-ts'

Address hérite de Bytes pour représenter les valeurs address d'Ethereum.

Cela ajoute la méthode suivante en plus de l'API Bytes :

  • Address.fromString(s: string): Address – crée une Address à partir d'une chaîne de caractères hexadécimale
  • Address.fromBytes(b: Bytes): Address – crée une Address à partir de b qui doit avoir exactement 20 octets de long. Passer une valeur avec moins ou plus d'octets entraînera une erreur
import { store } from '@graphprotocol/graph-ts'

L'API store permet de charger, sauvegarder et supprimer des entités dans et depuis le magasin Graph Node.

Les entités écrites dans le magasin correspondent directement aux types @entity définis dans le schéma GraphQL du subgraph. Pour faciliter le travail avec ces entités, la commande graph codegen fournie par Graph CLI génère des classes d'entités, qui sont des sous-classes du type Entity intégré, avec des accesseurs et des mutateurs pour les champs du schéma ainsi que des méthodes pour charger et sauvegarder ces entités.

Création d'entités

Lien vers cette section

Ce qui suit est un modèle courant pour créer des entités à partir d’événements Ethereum.

// Importer la classe Transfer générée à partir de l'ABI ERC20
import { Transfer as TransferEvent } from '../generated/ERC20/ERC20'
// Importer le type d'entité Transfer généré à partir du schéma GraphQL
import { Transfer } from '../generated/schema'
// Gestionnaire d'événement Transfer
export function handleTransfer(event: TransferEvent): void {
// Créer une entité Transfer, en utilisant le hash de la transaction comme ID de l'entité
let id = event.transaction.hash
let transfer = new Transfer(id)
// Définir les propriétés sur l'entité, en utilisant les paramètres de l'événement
transfer.from = event.params.from
transfer.to = event.params.to
transfer.amount = event.params.amount
// Sauvegarder l'entité dans le stockage
transfer.save()
}

Lorsqu'un événement Transfer est rencontré lors du traitement de la blockchain, il est transmis au gestionnaire d'événements handleTransfer en utilisant le type Transfer généré (ayant pour pseudonyme TransferEvent ici pour éviter un conflit de nom avec le type d'entité). Ce type permet d'accéder à des données telles que la transaction parente de l'événement et ses paramètres.

Chaque entité doit avoir un ID unique pour éviter les collisions avec d'autres entités. Il est assez courant que les paramètres des événements incluent un identifiant unique pouvant être utilisé.

Remarque : utiliser le hash de la transaction comme ID suppose qu'aucun autre événement dans la même transaction ne crée d'entités avec ce hash comme ID.

Chargement d'entités depuis le magasin

Lien vers cette section

Si une entité existe déjà, elle peut être chargée depuis le magasin avec les éléments suivants :

let id = event.transaction.hash // ou selon la manière dont l'ID est construit
let transfer = Transfer.load(id)
if (transfer == null) {
transfer = new Transfer(id)
}
// Utiliser l'entité Transfer comme précédemment

Comme l'entité peut ne pas encore exister dans le magasin, la méthode load renvoie une valeur de type Transfer | null. Il peut être nécessaire de vérifier le cas null avant d'utiliser la valeur.

Remarque : Le chargement des entités n'est nécessaire que si les modifications apportées dans le mappage dépendent des données précédentes d'une entité. Consultez la section suivante pour savoir les deux façons de mettre à jour les entités existantes.

Recherche d'entités créées dans un bloc

Lien vers cette section

Depuis graph-node v0.31.0, @graphprotocol/graph-ts v0.30.0 et @graphprotocol/graph-cli v0.49.0 la méthode loadInBlock est disponible pour tous les types d'entités.

L'API store facilite la récupération des entités qui ont été créées ou mises à jour dans le bloc actuel. Une situation typique est qu'un gestionnaire crée une transaction à partir d'un événement on-chain, et qu'un gestionnaire ultérieur souhaite accéder à cette transaction si elle existe.

  • Dans le cas où la transaction n'existe pas, le subgraph devra interroger la base de données pour découvrir que l'entité n'existe pas. Si l'auteur du subgraph sait déjà que l'entité doit avoir été créée dans le même bloc, utiliser loadInBlock évite ce détour par la base de données.
  • Pour certains subgraphs, ces recherches infructueuses peuvent contribuer de manière significative au temps d'indexation.
let id = event.transaction.hash // ou de toute autre manière dont l'ID est construit
let transfer = Transfer.loadInBlock(id)
if (transfer == null) {
transfer = new Transfer(id)
}
// Utiliser l'entité Transfer comme auparavant

Remarque : S'il n'y a pas d'entité créée dans le bloc donné, loadInBlock renverra null même s'il y a une entité avec l'ID donné dans le magasin.

Recherche d'entités dérivées

Lien vers cette section

Depuis graph-node v0.31.0, @graphprotocol/graph-ts v0.31.0 et@graphprotocol/graph-cli v0.51.0 la méthode loadRelated est disponible.

Cela permet de charger des champs d'entités dérivés à partir d'un gestionnaire d'événements. Par exemple, étant donné le schéma suivant :

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

Le code suivant chargera l'entité Token dont l'entité Holderest dérivée :

let holder = Holder.load('test-id')
// Charger les entités Token associées à un détenteur donné
let tokens = holder.tokens.load()

Mise à jour des entités existantes

Lien vers cette section

Il existe deux manières de mettre à jour une entité existante :

  1. Chargez l'entité avec, par exemple, Transfer.load(id), définissez des propriétés sur l'entité, puis .save() pour la sauvegarder dans le magasin.
  2. Créez simplement l'entité avec, par exemple, new Transfer(id), séfinissez des propriétés sur l'entité, puis .save() pour la sauvegarder dans le magasin. Si l'entité existe déjà, les modifications y sont fusionnées.

La modification des propriétés est simple dans la plupart des cas, grâce aux paramètres de propriétés générés :

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

Il est également possible de supprimer des propriétés avec l'une des deux instructions suivantes :

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

Ceci ne fonctionne qu'avec des propriétés optionnelles, c'est-à-dire des propriétés déclarées sans ! dans GraphQL. Deux exemples seraient owner: Bytes ou amount: BigInt.

La mise à jour des propriétés de tableau est un peu plus complexe, car obtenir un tableau à partir d'une entité crée une copie de ce tableau. Cela signifie que les propriétés de tableau doivent être définies à nouveau explicitement après la modification du tableau. Ce qui suit suppose que entity a un champ numbers: [BigInt!]! .

// Cela ne fonctionnera pas
entity.numbers.push(BigInt.fromI32(1))
entity.save()
// Cela fonctionnera
let numbers = entity.numbers
numbers.push(BigInt.fromI32(1))
entity.numbers = numbers
entity.save()

Supprimer des entités du magasin

Lien vers cette section

Il n'y a actuellement aucun moyen de supprimer une entité via les types générés. Au lieu de cela, la suppression d'une entité nécessite de passer le nom du type d'entité et l'ID de l'entité à store.remove:

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

L'API Ethereum donne accès aux contrats intelligents, aux variables d'état public, aux fonctions de contrat, aux événements, aux transactions, aux blocs et aux données d'encodage/décodage Ethereum.

Prise en charge des types Ethereum

Lien vers cette section

Comme pour les entités, graph codegen génère des classes pour tous les contrats intelligents et événements utilisés dans un subgraph. Pour cela, les ABIs des contrats doivent faire partie de la source de données dans le manifeste du subgraph. En général, les fichiers ABI sont stockés dans un dossier abis/ .

Avec les classes générées, les conversions entre les types Ethereum et les types intégrés se font en arrière-plan afin que les auteurs de subgraph n'aient pas à s'en soucier.

L’exemple suivant illustre cela. Étant donné un schéma de subgraph comme

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

et une signature d'événement Transfer(address,address,uint256) sur Ethereum, les valeurs from, to etamount de type address, address et uint256 sont enverties en Address et BigInt, leur permettant d'être passées aux propriétés Bytes! et BigInt! de l'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()

Événements et données de bloc/transaction

Lien vers cette section

Les événements Ethereum passés aux gestionnaires d'événements, comme l'événement Transfer dans les exemples précédents, fournissent non seulement l'accès aux paramètres de l'événement, mais également à leur transaction parente et au bloc auquel ils appartiennent. Les données suivantes peuvent être obtenues à partir des instances d' event (ces classes font partie du module ethereum dans 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
}

Accès à l'état du contrat intelligent

Lien vers cette section

Le code généré par graph codegen inclut également des classes pour les contrats intelligents utilisés dans le subgraph. Celles-ci peuvent être utilisées pour accéder aux variables d'état publiques et appeler des fonctions du contrat au bloc actuel.

Un modèle courant consiste à accéder au contrat dont provient un événement. Ceci est réalisé avec le code suivant :

// Importer la classe de contrat générée et la classe d'événement Transfer générée
import { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'
// Importer la classe d'entité générée
import { Transfer } from '../generated/schema'
export function handleTransfer(event: TransferEvent) {
// Lier le contrat à l'adresse qui a émis l'événement
let contract = ERC20Contract.bind(event.address)
// Accéder aux variables d'état et aux fonctions en les appelant
let erc20Symbol = contract.symbol()
}

Transfer est remplacé par TransferEvent ici pour éviter un conflit de nommage avec le type d'entité

Tant que le ERC20Contract sur Ethereum a une fonction publique en lecture seule appelée symbol, elle peut être appelée avec .symbol(). Pour les variables d'état publiques, une méthode du même nom est créée automatiquement.

Tout autre contrat faisant partie du subgraph peut être importé à partir du code généré et peut être lié à une adresse valide.

Gestion des appels retournés

Lien vers cette section

Si les méthodes en lecture seule de votre contrat peuvent échouer, vous devez gérer cela en appelant la méthode de contrat générée préfixée par try_.

  • Par exemple, le contrat Gravity expose la méthode gravatarToOwner . Ce code serait capable de gérer une erreur dans cette méthode :
let gravity = Gravity.bind(event.address)
let callResult = gravity.try_gravatarToOwner(gravatar)
if (callResult.reverted) {
log.info('getGravatar a été annulé', [])
} else {
let owner = callResult.value
}

Remarque : Un Graph Node connecté à un client Geth ou Infura peut ne pas détecter toutes les réversions (reverts). Si vous en dépendez, nous recommandons d'utiliser un Graph Node connecté à un client Parity.

Encodage/décodage ABI

Lien vers cette section

Les données peuvent être encodées et décodées selon le format de codage ABI d'Ethereum en utilisant les fonctions encode et decode du module 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)

Pour plus d'informations:

Solde d'une adresse

Lien vers cette section

Le solde de jetons natifs d'une adresse peut être récupéré en utilisant le module ethereum. Cette fonctionnalité est disponible à partir de apiVersion: 0.0.9 définie dans subgraph.yaml. La fonction getBalance() récupère le solde de l'adresse spécifiée à la fin du bloc où l'événement est déclenché.

import { ethereum } from '@graphprotocol/graph-ts'
let address = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
let balance = ethereum.getBalance(address) // renvoie le solde en BigInt

Vérifier si une adresse est une adresse de contrat intelligent ou une adresse détenue par des personnes (EOA)

Lien vers cette section

Pour vérifier si une adresse est une adresse de contrat intelligent ou une adresse détenue extérieurement (EOA), utilisez la fonction hasCode() du module ethereum qui retournera un boolean. Cette fonctionnalité est disponible à partir de apiVersion: 0.0.9 qui est définie dans subgraph.yaml.

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

L'API log permet aux subgraphs d'enregistrer des informations sur la sortie standard de Graph Node ainsi que sur Graph Explorer. Les messages peuvent être enregistrés en utilisant différents niveaux de journalisation. Une syntaxe de chaîne de caractère de format de base est fournie pour composer des messages de journal à partir de l'argument.

L'API log inclut les fonctions suivantes :

  • log.debug(fmt: string, args: Array<string>): void - enregistre un message de débogage.
  • log.info(fmt: string, args: Array<string>): void - enregistre un message d'information.
  • log.warning(fmt: string, args: Array<string>): void - enregistre un avertissement.
  • log.error(fmt: string, args: Array<string>): void - enregistre un message d'erreur.
  • log.critical(fmt: string, args: Array<string>): void – enregistre un message critique et met fin au subgraph.

L'API log prend une chaîne de caractères de format et un tableau de valeurs de chaîne de caractères. Elle remplace ensuite les espaces réservés par les valeurs de chaîne de caractères du tableau. Le premier espace réservé {} est remplacé par la première valeur du tableau, le second {} est remplacé par la deuxième valeur, et ainsi de suite.

log.info('Message à afficher : {}, {}, {}', [value.toString(), anotherValue.toString(), 'déjà une chaîne'])

Enregistrer une ou plusieurs valeurs

Lien vers cette section
Enregistrer une seule valeur
Lien vers cette section

Dans l'exemple ci-dessous, la valeur de chaîne de caractères "A" est passée dans un tableau pour devenir ['A'] avant d'être enregistrée:

let myValue = 'A'
export function handleSomeEvent(event: SomeEvent): void {
// Affiche : "Ma valeur est : A"
log.info('Ma valeur est : {}', [myValue])
}
Journalisation d'une seule entrée à partir d'un tableau existant
Lien vers cette section

Dans l'exemple ci-dessous, seule la première valeur du tableau d'arguments est journalisée, bien que le tableau contienne trois valeurs.

let myArray = ['A', 'B', 'C']
export function handleSomeEvent(event: SomeEvent): void {
// Affiche : "Ma valeur est : A" (Même si trois valeurs sont passées à `log.info`)
log.info('Ma valeur est : {}', myArray)
}

Journalisation de plusieurs entrées d'un tableau existant

Lien vers cette section

Chaque entrée dans le tableau des arguments nécessite son propre espace réservé {} dans la chaîne de caractères du message de log. L'exemple ci-dessous contient trois espaces réservés {} dans le message de log. À cause de cela, toutes les trois valeurs dans myArray sont enregistrées.

let myArray = ['A', 'B', 'C']
export function handleSomeEvent(event: SomeEvent): void {
// Affiche : "Ma première valeur est : A, la deuxième valeur est : B, la troisième valeur est : C"
log.info('Ma première valeur est : {}, la deuxième valeur est : {}, la troisième valeur est : {}', myArray)
}
Enregistrer une entrée spécifique à partir d'un tableau existant
Lien vers cette section

Pour afficher une valeur spécifique dans le tableau, la valeur indexée doit être fournie.

export function handleSomeEvent(event: SomeEvent): void {
// Affiche : "Ma troisième valeur est C"
log.info('Ma troisième valeur est : {}', [myArray[2]])
}
Journalisation des informations sur les événements
Lien vers cette section

L'exemple ci-dessous enregistre le numéro de bloc, le hachage de bloc et le hachage de transaction d'un événement :

import { log } from '@graphprotocol/graph-ts'
export function handleSomeEvent(event: SomeEvent): void {
log.debug('Numéro de bloc : {}, hachage de bloc : {}, hachage de transaction : {}', [
event.block.number.toString(), // "47596000"
event.block.hash.toHexString(), // "0x..."
event.transaction.hash.toHexString(), // "0x..."
])
}
import { ipfs } from '@graphprotocol/graph-ts'

Les contrats intelligents ancrent occasionnellement des fichiers IPFS sur la blockchain. Cela permet aux mappages d'obtenir les hashs IPFS du contrat et de lire les fichiers correspondants à partir d'IPFS. Les données du fichier seront retournées sous forme de Bytes, ce qui nécessite généralement un traitement supplémentaire, par exemple avec l'API json documentée plus loin sur cette page.

Étant donné un hachage ou un chemin IPFS, la lecture d'un fichier depuis IPFS se fait comme suit :

// Mettez ceci à l'intérieur d'un gestionnaire d'événements dans le mapping
let hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'
let data = ipfs.cat(hash)
// Les chemins comme `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`
// qui incluent des fichiers dans des répertoires sont également pris en charge
let path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'
let data = ipfs.cat(path)

Remarque: ipfs.cat n'est pas déterministe pour le moment. Si le fichier ne peut pas être récupéré sur le réseau IPFS avant l'expiration de la demande, il retournera null. Pour cette raison, il est toujours utile de vérifier le résultat pour null.

Il est également possible de traiter des fichiers plus volumineux en streaming avec ipfs.map. La fonction s'attend à recevoir un hash ou à un chemin pour un fichier IPFS, le nom d'un callback, et des indicateurs pour modifier son comportement :

import { JSONValue, Value } from '@graphprotocol/graph-ts'
export function processItem(value: JSONValue, userData: Value): void {
// Voir la documentation JSONValue pour les détails sur le traitement
// des valeurs JSON
let obj = value.toObject()
let id = obj.get('id')
let title = obj.get('title')
if (!id || !title) {
return
}
// Les callbacks peuvent également créer des entités
let newItem = new Item(id)
newItem.title = title.toString()
newItem.parent = userData.toString() // Définit le parent à "parentId"
newItem.save()
}
// Mettez ceci à l'intérieur d'un gestionnaire d'événements dans le mapping
ipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])
// Alternativement, utilisez `ipfs.mapJSON`
ipfs.mapJSON('Qm...', 'processItem', Value.fromString('parentId'))

Le seul indicateur actuellement pris en charge est json, qui doit être passé à ipfs.map. Avec l'indicateur json , le fichier IPFS doit consister en une série de valeurs JSON, une valeur par ligne. L'appel à ipfs.map lira chaque ligne du fichier, la désérialisera en un JSONValue et appellera le callback pour chacune d'entre elles. Le callback peut alors utiliser des opérations des entités pour stocker des données à partir du JSONValue. Les modifications d'entité ne sont enregistrées que lorsque le gestionnaire qui a appelé ipfs.map se termine avec succès ; en attendant, elles sont conservées en mémoire, et la taille du fichier que ipfs.map peut traiter est donc limitée.

En cas de succès, ipfs.map renvoie void. Si une invocation du callback provoque une erreur, le gestionnaire qui a invoqué ipfs.map est interrompu et le subgraph marqué comme échoué.

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

L'API crypto rend des fonctions cryptographiques disponibles pour une utilisation dans les mappages. Actuellement, il n'y en a qu'une seule :

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

Les données JSON peuvent être analysées en utilisant l'API json:

  • json.fromBytes(data: Bytes): JSONValue – analyse les données JSON à partir d'un tableau Bytes interprété comme une séquence UTF-8 valide
  • json.try_fromBytes(data: Bytes): Result<JSONValue, boolean> – version sécurisée de json.fromBytes, elle renvoie une variante d'erreur si l'analyse échoue
  • json.fromString(data: string): JSONValue – analyse les données JSON à partir d'une String UTF-8 valide
  • json.try_fromString(data: string): Result<JSONValue, boolean> – version sécurisée de json.fromString, elle renvoie une variante d'erreur si l'analyse échoue

La classe JSONValue fournit un moyen d'extraire des valeurs d'un document JSON arbitraire. Étant donné que les valeurs JSON peuvent être des booléens, des nombres, des tableaux et plus encore, JSONValue est accompagné d'une propriété kind pour vérifier le type d'une valeur :

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

De plus, il existe une méthode pour vérifier si la valeur est null:

  • value.isNull(): boolean

Lorsque le type d'une valeur est certain, il peut être converti en un type intégré n utilisant l'une des méthodes suivantes :

  • value.toBool(): boolean
  • value.toI64(): i64
  • value.toF64(): f64
  • value.toBigInt(): BigInt
  • value.toString(): string
  • value.toArray(): Array<JSONValue> - (et ensuite convertir JSONValue avec l'une des 5 méthodes ci-dessus)

Référence des conversions de types

Lien vers cette section
Source(s)DestinationFonctions de conversion
AddressBytesaucune
AddressStrings.toHexString()
BigDecimalStrings.toString()
BigIntBigDecimals.toBigDecimal()
BigIntString (hexadecimal)s.toHexString() or s.toHex()
BigIntString (unicode)s.toString()
BigInti32s.toI32()
BooleanBooleanaucune
Bytes (signé)BigIntBigInt.fromSignedBytes(s)
Bytes (non signé)BigIntBigInt.fromUnsignedBytes(s)
BytesString (hexadecimal)s.toHexString() or s.toHex()
BytesString (unicode)s.toString()
BytesString (base58)s.toBase58()
Bytesi32s.toI32()
Bytesu32s.toU32()
BytesJSONjson.fromBytes(s)
int8i32aucune
int32i32aucune
int32BigIntBigInt.fromI32(s)
uint24i32aucune
int64 - int256BigIntaucune
uint32 - uint256BigIntaucune
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)

Métadonnées de la source de données

Lien vers cette section

Vous pouvez inspecter l'adresse du contrat, le réseau et le contexte de la source de données qui a invoqué le gestionnaire grâce un namespace dataSource :

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

Entité et DataSourceContext

Lien vers cette section

La classe de base Entity et la classe enfant DataSourceContext disposent d'assistants pour définir et récupérer dynamiquement des champs :

  • 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 in Manifest

Lien vers cette section

La section context de dataSources vous permet de définir des paires clé-valeur qui sont accessibles dans vos mappages de subgraphs. Les types disponibles sont Bool, String, Int, Int8, BigDecimal, Bytes, List, et BigInt.

Voici un exemple YAML illustrant l'utilisation de différents types dans la section 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 : Spécifie une valeur booléenne (true ou false).
  • String : Spécifie une valeur de type chaîne de caractères.
  • Int : Spécifie un nombre entier de 32 bits.
  • Int8 : Spécifie un entier de 8 bits.
  • BigDecimal : Spécifie un nombre décimal. Doit être entre mis guillemets.
  • Bytes : Spécifie une chaîne de caractères hexadécimale.
  • List : Spécifie une liste d'éléments. Chaque élément doit spécifier son type et ses données.
  • BigInt : Spécifie une grande valeur entière. Elle doit être mise entre guillemets en raison de sa grande taille.

Ce contexte est ensuite accessible dans vos fichiers de mappage de subgraphs, permettant des subgraphs plus dynamiques et configurables.

Modifier une page

Précédente
Advance Subgraph Features
Suivante
Common AssemblyScript Issues
Modifier une page