Release Notes & Upgrade Guides > Guide de migration de l'AssemblyScript

Guide de migration de l'AssemblyScript

Reading time: 11 min

Jusqu'à présent, les subgraphs utilisaient l'une des premières versions d'AssemblyScript (v0.6). Enfin, nous avons ajouté la prise en charge du le plus récent disponible (v0.19.10) ! 🎉

Cela permettra aux développeurs de subgraph d'utiliser les nouvelles fonctionnalités du langage AS et de la bibliothèque standard.

Ce guide s'applique à toute personne utilisant graph-cli/graph-ts sous la version 0.22.0. Si vous utilisez déjà une version supérieure (ou égale), vous utilisez déjà la version 0.19.10 d'AssemblyScript 🙂

Remarque : Depuis 0.24.0, graph-node peut prendre en charge les deux versions, en fonction de la apiVersion spécifiée dans le manifeste du subgraph.

Fonctionnalités

Lien vers cette section

Nouvelle fonctionnalité

Lien vers cette section
  • Les TypedArray peuvent désormais être construits à partir de ArrayBuffer en utilisant les nouvelle méthode statique wrap (v0.8.1)
  • Nouvelles fonctions de bibliothèque standard: String#toUpperCase, String#toLowerCase, String#localeCompareand TypedArray#set (v0.9.0)
  • Ajout de la prise en charge de x instanceof GenericClass (v0.9.2)
  • Ajout de StaticArray<T>, une variante de tableau plus efficace (v0.9.3)
  • Ajout de Array<T>#flat (v0.10.0)
  • Implémentation de radix argument on Number#toString (v0.10.1)
  • Ajout de la prise en charge des séparateurs dans les littéraux à virgule flottante (v0.13.7)
  • Ajout du support pour les fonctions de première classe (v0.14.0)
  • Ajout des fonctions intégrées : i32/i64/f32/f64.add/sub/mul (v0.14.13)
  • Implementation de Array/TypedArray/String#at (v0.18.2)
  • Ajout de la prise en charge des chaînes littérales de modèle (v0.18.17)
  • Ajout de encodeURI(Component) et decodeURI(Component) (v0.18.27)
  • Ajout de toString, toDateString et toTimeString à Date (v0.18.29)
  • Ajout de toUTCString pour Date (v0.18.30)
  • Ajout du type intégré nonnull/NonNullable (v0.19.2)

Optimizations

Lien vers cette section
  • Les fonctions Math telles que exp, exp2, log, log2 et pow ont été remplacées par des variantes plus rapides (v0.9.0)
  • Légère optimisation de Math.mod (v0.17.1)
  • Mise en cache de plus d'accès aux champs dans std Map et Set (v0.17.8)
  • Optimiser pour des puissances de deux ipow32/64 (v0.18.2)
  • Le type d'un littéral de tableau peut désormais être déduit de son contenu (v0.9.0)
  • Stdlib mis à jour vers Unicode 13.0.0 (v0.10.0)

Comment mettre à niveau ?

Lien vers cette section
  1. Modifiez vos mappages apiVersion dans subgraph.yaml en 0.0.6 :
...
dataSources:
...
mapping:
...
apiVersion: 0.0.6
...
  1. Mettez à jour le graph-cli que vous utilisez vers la version dernière en exécutant :
# si vous l'avez installé globalement
npm install --global @graphprotocol/graph-cli@latest
# ou dans votre subgraph si vous l'avez comme dépendance de développement
npm install --save-dev @graphprotocol/graph-cli@latest
  1. Faites de même pour graph-ts, mais au lieu de l'installer globalement, enregistrez-le dans vos dépendances principales :
npm install --save @graphprotocol/graph-ts@latest
  1. Suivez le reste du guide pour corriger les changements de langue.
  2. Exécutez codegen et deploy à nouveau.

Modifications radicales

Lien vers cette section

Sur l'ancienne version d'AssemblyScript, vous pouviez créer du code comme celui-ci :

function load(): Value | null { ... }
let maybeValue = load();
maybeValue.aMethod();

Cependant, sur la version la plus récente, comme la valeur est nullable, vous devez vérifier, comme ceci :

let maybeValue = load()
if (maybeValue) {
maybeValue.aMethod() // `maybeValue` n'est plus nul
}

Ou forcez-le comme ceci :

let maybeValue = load()! // breaks in runtime if value is null
maybeValue.aMethod()

Si vous ne savez pas lequel choisir, nous vous recommandons de toujours utiliser la version sécurisée. Si la valeur n'existe pas, vous souhaiterez peut-être simplement effectuer une instruction if précoce avec un retour dans votre gestionnaire de subgraph.

Ombrage variable

Lien vers cette section

Avant de pouvoir faire de l'observation de variables et un code comme celui-ci fonctionnerait :

let a = 10
let b = 20
let a = a + b

Cependant, cela n'est plus possible et le compilateur renvoie cette erreur :

ERROR TS2451: Cannot redeclare block-scoped variable 'a'
let a = a + b;
~~~~~~~~~~~~~
in assembly/index.ts(4,3)

Vous devrez renommer vos variables en double si vous conservez une observation de variables.

Comparaisons nulles

Lien vers cette section

En effectuant la mise à niveau sur votre subgraph, vous pouvez parfois obtenir des erreurs comme celles-ci :

ERROR TS2322: Type '~lib/@graphprotocol/graph-ts/common/numbers/BigInt | null' is not assignable to type '~lib/@graphprotocol/graph-ts/common/numbers/BigInt'.
if (decimals == null) {
~~~~
in src/mappings/file.ts(41,21)

Pour résoudre, vous pouvez simplement remplacer l'instruction if par quelque chose comme ceci :

if (!decimals) {
// ou bien
if (decimals === null) {

La même chose s'applique si vous faites != au lieu de ==.

Auparavant, la manière courante de faire du casting consistait simplement à utiliser le mot-clé as, comme ceci :

let byteArray = new ByteArray(10)
let uint8Array = byteArray as Uint8Array // equivalent to: <Uint8Array>byteArray

Cependant, cela ne fonctionne que dans deux scénarios :

  • Casting primitif (entre des types tels que u8, i32, bool ; par exemple : let b : isize = 10 ; b as usize);
  • Upcasting sur l'héritage de classe (sous-classe → superclasse)

Les Exemples:

// primitive casting
let a: usize = 10
let b: isize = 5
let c: usize = a + (b as usize)
//upcasting lors de l'héritage de classe
class Bytes extends Uint8Array {}
let bytes = new Bytes(2)
// <Uint8Array>bytes // équivalent à : bytes as Uint8Array

Il existe deux scénarios dans lesquels vous souhaiterez peut-être diffuser du contenu, mais l'utilisation de as/<T>var n'est pas sûre :

  • Downcasting sur l'héritage de classe (superclasse → sous-classe)
  • Entre deux types qui partagent une superclasse
//downcasting lors de l'héritage de classe
class Bytes extends Uint8Array {}
let uint8Array = new Uint8Array(2)
// <Bytes>uint8Array // plante à l'exécution :(
// entre deux types qui partagent une superclasse
class Bytes extends Uint8Array {}
class ByteArray extends Uint8Array {}
let bytes = new Bytes(2)
// <ByteArray>bytes // plante à l'exécution :(

Dans ces cas-là, vous pouvez utiliser la fonction changetype<T> :

//downcasting lors de l'héritage de classe
class Bytes extends Uint8Array {}
let uint8Array = new Uint8Array(2)
changetype<Bytes>(uint8Array) // fonctionne :)
// entre deux types qui partagent une superclasse
class Bytes extends Uint8Array {}
class ByteArray extends Uint8Array {}
let bytes = new Bytes(2)
changetype<ByteArray>(bytes) // fonctionne :)

Si vous souhaitez simplement supprimer la nullité, vous pouvez continuer à utiliser l'opérateur as (ou <T>variable), mais assurez-vous de savoir que la valeur ne peut pas être nulle. sinon ça va casser.

// supprimer la possibilité de valeur nulle (nullability)
let previousBalance = AccountBalance.load(balanceId) // AccountBalance | null
if (previousBalance != null) {
return previousBalance as AccountBalance // suppression sûre de null
}
let newBalance = new AccountBalance(balanceId)

Pour le cas de nullité, nous vous recommandons de jeter un œil à la fonctionnalité de vérification de la nullité, cela rendra votre code plus propre 🙂

Nous avons également ajouté quelques méthodes statiques supplémentaires dans certains types pour faciliter la diffusion, à savoir :

  • Bytes.fromByteArray
  • Bytes.fromUint8Array
  • BigInt.fromByteArray
  • ByteArray.fromBigInt

Vérification de nullité avec accès à la propriété

Lien vers cette section

Pour utiliser la fonctionnalité de vérification de nullité, vous pouvez utiliser soit les instructions if, soit l'opérateur ternaire (? et :) comme ce:

let something: string | null = 'data'
let somethingOrElse = something ? something : 'else'
// ou
let somethingOrElse
if (something) {
somethingOrElse = something
} else {
somethingOrElse = 'else'
}

Cependant, cela ne fonctionne que lorsque vous effectuez le if / ternaire sur une variable, pas sur un accès à une propriété, comme ceci :

class Container {
data: string | null
}
let container = new Container()
container.data = 'data'
let somethingOrElse: string = container.data ? container.data : 'else' // ne compile pas

Ce qui génère cette erreur :

ERROR TS2322: Type '~lib/string/String | null' is not assignable to type '~lib/string/String'.
let somethingOrElse: string = container.data ? container.data : "else";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Pour résoudre ce problème, vous pouvez créer une variable pour l'accès à cette propriété afin que le compilateur puisse effectuer la vérification magique de la nullité :

class Container {
data: string | null
}
let container = new Container()
container.data = 'data'
let data = container.data
let somethingOrElse: string = data ? data : 'else' // compile sans problème :)

Surcharge de l'opérateur avec accès à la propriété

Lien vers cette section

Si vous essayez de additionner (par exemple) un type nullable (à partir d'un accès à une propriété) avec un type non nullable, le compilateur AssemblyScript au lieu de donner une erreur de compilation avertissant que l'une des valeurs est nullable, il compile simplement silencieusement, donnant une chance pour que le code soit interrompu au moment de l'exécution.

class BigInt extends Uint8Array {
@operator('+')
plus(other: BigInt): BigInt {
// ...
}
}
class Wrapper {
public constructor(public n: BigInt | null) {}
}
let x = BigInt.fromI32(2)
let y: BigInt | null = null
x + y // donne une erreur de compilation concernant la nullité
let wrapper = new Wrapper(y)
wrapper.n = wrapper.n + x // ne donne pas d'erreurs de compilation comme il se doit

Nous avons ouvert un problème sur le compilateur AssemblyScript pour cela, mais pour l'instant, si vous effectuez ce type d'opérations dans vos mappages de subgraph, vous devez les modifier pour effectuer une vérification nulle avant.

let wrapper = new Wrapper(y)
if (!wrapper.n) {
wrapper.n = BigInt.fromI32(0)
}
wrapper.n = wrapper.n + x // maintenant `n` est garanti comme étant un BigInt

Initialisation de valeur

Lien vers cette section

Si vous avez un code comme celui-ci :

var value: Type // null
value.x = 10
value.y = 'content'

Il sera compilé mais s'arrêtera au moment de l'exécution, cela se produit parce que la valeur n'a pas été initialisée, alors assurez-vous que votre subgraph a initialisé ses valeurs, comme ceci :

var value = new Type() // initialized
value.x = 10
value.y = 'content'

De plus, si vous avez des propriétés nullables dans une entité GraphQL, comme ceci :

type Total @entity {
id: Bytes!
amount: BigInt
}

Et vous avez un code similaire à celui-ci :

let total = Total.load('latest')
if (total === null) {
total = new Total('latest')
}
total.amount = total.amount + BigInt.fromI32(1)

Vous devrez vous assurer d'initialiser la valeur total.amount, car si vous essayez d'accéder à la dernière ligne pour la somme, elle plantera. Donc soit vous l'initialisez d'abord :

let total = Total.load('latest')
if (total === null) {
total = new Total('latest')
total.amount = BigInt.fromI32(0)
}
total.tokens = total.tokens + BigInt.fromI32(1)

Ou vous pouvez simplement modifier votre schéma GraphQL pour ne pas utiliser de type nullable pour cette propriété, puis nous l'initialiserons à zéro à l'étape codegen 😉

type Total @entity {
id: Bytes!
amount: BigInt!
}
let total = Total.load('latest')
if (total === null) {
total = new Total('latest') // initialise déjà les propriétés non-nullables
}
total.amount = total.amount + BigInt.fromI32(1)

Initialisation de la propriété de classe

Lien vers cette section

Si vous exportez des classes avec des propriétés qui sont d'autres classes (déclarées par vous ou par la bibliothèque standard), comme ceci :

class Thing {}
export class Something {
value: Thing
}

Le compilateur générera une erreur car vous devez soit ajouter un initialiseur pour les propriétés qui sont des classes, soit ajouter l'opérateur ! :

export class Something {
constructor(public value: Thing) {}
}
// ou
export class Something {
value: Thing
constructor(value: Thing) {
this.value = value
}
}
// ou
export class Something {
value!: Thing
}

Initialisation du tableau

Lien vers cette section

La classe Array accepte toujours un nombre pour initialiser la longueur de la liste, mais vous devez faire attention car des opérations comme .push augmenteront en fait la taille au lieu de l'ajouter au début. , Par exemple:

let arr = new Array<string>(5) // ["", "", "", "", ""]
arr.push('something') // ["", "", "", "", "", "something"] // taille 6 :(

En fonction des types que vous utilisez, par exemple les types nullables, et de la manière dont vous y accédez, vous pourriez rencontrer une erreur d'exécution comme celle-ci :

ERRO Handler skipped due to execution failure, error: Mapping aborted at ~lib/array.ts, line 110, column 40, with message: Element type must be nullable if array is holey wasm backtrace: 0: 0x19c4 - <unknown>!~lib/@graphprotocol/graph-ts/index/format 1: 0x1e75 - <unknown>!~lib/@graphprotocol/graph-ts/common/collections/Entity#constructor 2: 0x30b9 - <unknown>!node_modules/@graphprotocol/graph-ts/global/global/id_of_type

Pour réellement pousser au début, vous devez soit initialiser le Array avec une taille zéro, comme ceci :

let arr = new Array<string>(0) // []
arr.push('quelque chose') // ["quelque chose"]

Ou vous devriez le muter via index :

let arr = new Array<string>(5) // ["", "", "", "", ""]
arr[0] = 'quelque chose' // ["quelque chose", "", "", "", ""]

Schéma GraphQL

Lien vers cette section

Il ne s'agit pas d'une modification directe d'AssemblyScript, mais vous devrez peut-être mettre à jour votre fichier schema.graphql.

Vous ne pouvez désormais plus définir de champs dans vos types qui sont des listes non nullables. Si vous avez un schéma comme celui-ci :

type Something @entity {
id: Bytes!
}
type MyEntity @entity {
id: Bytes!
invalidField: [Something]! # n'est plus valide
}

Vous devrez ajouter un ! au membre de type List, comme ceci :

type Something @entity {
id: Bytes!
}
type MyEntity @entity {
id: Bytes!
invalidField: [Something!]! # valide
}

Cela a changé en raison des différences de nullité entre les versions d'AssemblyScript et est lié au fichier src/generated/schema.ts (chemin par défaut, vous avez peut-être modifié cela).

  • Alignement de Map#set et Set#add avec la spécification, en retournant this (v0.9.2)
  • Les tableaux n'héritent plus d'ArrayBufferView, mais sont désormais distincts (v0.10.0)
  • Les classes initialisées à partir de littéraux d'objet ne peuvent plus définir de constructeur (v0.10.0)
  • Le résultat d'une opération binaire ** est désormais le dénominateur commun entier si les deux opérandes sont des entiers. Auparavant, le résultat était un float comme si vous appeliez Math/f.pow (v0.11.0)
  • Convertir NaN en false lors de la conversion en bool (v0.14.9)
  • Lors du décalage d'une petite valeur entière de type i8/u8 ou i16/u16, seuls les 3 respectivement 4 les plus petits les bits significatifs de la valeur RHS affectent le résultat, de la même manière que le résultat d'un i32.shl n'est affecté que par les 5 bits les moins significatifs de la valeur RHS. Exemple : someI8 << 8 produisait auparavant la valeur 0, mais produit désormais someI8 en raison du masquage du RHS comme 8 & 7 = 0 (3 bits) (v0.17.0)
  • Correction d'un bug des comparaisons de chaînes relationnelles lorsque les tailles diffèrent (v0.17.8)
Modifier une page

Précédente
Mise à niveau d'un subgraph existant vers le réseau Graph
Suivante
Guide de migration des validations GraphQL
Modifier une page