Release Notes & Upgrade Guides > Guia de Migração do AssemblyScript

Guia de Migração do AssemblyScript

Reading time: 10 min

Até agora, os subgraphs têm usado uma das primeiras versões do AssemblyScript (v0.6). Finalmente, adicionamos apoio à versão mais recente disponível (v.0.19.10)! 🎉

Isto permitirá que os programadores de subgraph usem recursos mais novos da linguagem AS e da sua biblioteca normal.

Este guia se aplica a quem usar o graph-cli/graph-ts antes da versão 0.22.0. Se já está numa versão maior (ou igual) àquela, já está a usar a versão 0.19.10 do AssemblyScript 🙂

Nota: Desde a versão 0.24.0, o graph-node pode apoiar ambas as versões, dependente da apiVersion especificada no manifest do subgraph.

Recursos

Link para esta seção

Novas funcionalidades

Link para esta seção
  • TypedArrays podem ser construídos de ArrayBuffers com o novo método estático wrap (v0.8.1)
  • Novas funções de biblioteca normais: String#toUpperCase, String#toLowerCase, String#localeCompare e TypedArray#set (v0.9.0)
  • Suporte para x GenericClass (v0.9.2)
  • StaticArray<T>, uma variante de arranjo mais eficiente (v0.9.3)
  • Array<T>#flat (v0.10.0)
  • Implementado o argumento radix no Number#toString (v0.10.1)
  • Suporte para separadores em literais de ponto flutuante (v0.13.7)
  • Suporte para funções de primeira classe (v0.14.0)
  • Embutidos: i32/i64/f32/f64.add/sub/mul (v0.14.13)
  • Array/TypedArray/String#at (v0.18.2)
  • Suporte para strings literais de modelos (v0.18.17)
  • encodeURI(Component) e decodeURI(Component) (v0.18.27)
  • toString, toDateString e toTimeString ao Date (v0.18.29)
  • toUTCString para a Date (v0.18.30)
  • Tipo embutido nonnull/NonNullable (v0.19.2)

Otimizações

Link para esta seção
  • Funções Math como exp, exp2, log, log2 e pow foram substituídas por variantes mais rápidas (v0.9.0)
  • Otimizado levemente o Math.mod (v0.17.1)
  • Cacheing de mais acessos de campos em Map e Set (v0.17.8)
  • Otimização para poderes de dois no ipow32/64 (v0.18.2)
  • O tipo de um literal de arranjos agora pode ser inferido dos seus conteúdos (v0.9.0)
  • stdlib atualizado ao Unicode 13.0.0 (v0.10.0)

Como atualizar?

Link para esta seção
  1. Mude os seus mapeamentos de apiVersion no subgraph.yaml para 0.0.6:
...
dataSources:
...
mapping:
...
apiVersion: 0.0.6
...
  1. Atualize o graph-cli que usa à versão mais recente (latest) com:
# caso o tenha instalado globalmente
npm install --global @graphprotocol/graph-cli@latest
# ou no seu subgraph se o tiver como dependência de desenvolvimento
npm install --save-dev @graphprotocol/graph-cli@latest
  1. Faça o mesmo para o graph-ts, mas em vez de instalar globalmente, salve-o nas suas dependências principais:
npm install --save @graphprotocol/graph-ts@latest
  1. Siga o resto do guia para consertar as mudanças frágeis na linguagem.
  2. Execute codegen e deploy novamente.

Breaking changes (mudanças frágeis)

Link para esta seção

Anulabilidade

Link para esta seção

Na versão mais antiga do AssemblyScript, podias criar códigos assim:

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

Mas na versão mais nova, como o valor é anulável, ele exige que confira, assim:

let maybeValue = load()
if (maybeValue) {
maybeValue.aMethod() // `maybeValue` is not null anymore
}

...ou o force deste jeito:

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

Se não tiver certeza de qual escolher, é sempre bom usar a versão segura. Se o valor não existir, pode fazer uma declaração if precoce com um retorno no seu handler de subgraph.

Sombreamento Varíavel

Link para esta seção

Antes, ao fazer sombreamentos variáveis, códigos assim funcionavam bem:

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

Porém, isto não é mais possível, e o compilador retorna este erro:

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

Renomeie as suas variáveis duplicadas, se tinha o sombreamento variável.

Comparações de Nulos

Link para esta seção

Ao fazer a atualização no seu subgraph, às vezes aparecem erros como este:

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)

Para resolver isto, basta mudar a declaração if para algo assim:

if (!decimals) {
// or
if (decimals === null) {

O mesmo acontece se fizer o != em vez de ==.

Casting (Conversão de tipos)

Link para esta seção

Antigamente, casting era normalmente feito com a palavra-chave as, assim:

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

Porém, isto só funciona em dois casos:

  • Casting primitivo (entre tipos como u8, i32, bool; por ex.: let b: isize = 10; b as usize);
  • Upcasting em herança de classe (subclass → superclass)

Exemplos:

// primitive casting
let a: usize = 10
let b: isize = 5
let c: usize = a + (b as usize)
// upcasting em herança de classe
class Bytes extends Uint8Array {}
let bytes = new Bytes(2)
// <Uint8Array>bytes // mesmo que: bytes como Uint8Array

Há dois cenários onde casting é possível, mas usar as/<T>var não é seguro:

  • Downcasting em herança de classe (superclass → subclass)
  • Entre dois tipos que compartilham uma superclasse
// downcasting em herança de classe
class Bytes extends Uint8Array {}
let uint8Array = new Uint8Array(2)
// <Bytes>uint8Array // quebra no runtime :(
// entre dois tipos que compartilham uma superclasse
class Bytes extends Uint8Array {}
class ByteArray extends Uint8Array {}
let bytes = new Bytes(2)
// <ByteArray>bytes // quebra no runtime :(

Nestes casos, vale usar a função changetype<T>:

// downcasting em herança de classe
class Bytes extends Uint8Array {}
let uint8Array = new Uint8Array(2)
changetype<Bytes>(uint8Array) // funciona :)
// entre dois tipos que compartilham uma superclasse
class Bytes extends Uint8Array {}
class ByteArray extends Uint8Array {}
let bytes = new Bytes(2)
changetype<ByteArray>(bytes) // funciona :)

Se só quiser tirar a anulabilidade, pode continuar a usar o operador as (ou <T>variable), mas tenha ciência de que sabe que o valor não pode ser nulo, ou ele falhará.

// remove anulabilidade
let previousBalance = AccountBalance.load(balanceId) // AccountBalance | null
if (previousBalance != null) {
return previousBalance as AccountBalance // safe remove null
}
let newBalance = new AccountBalance(balanceId)

Para o caso de anulabilidade, é bom dar uma olhada no recurso de verificação de anulabilidade, pois ele deixará o seu código mais limpinho 🙂

Também adicionamos alguns métodos estáticos em alguns tipos para facilitar o casting, sendo:

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

Checagem de anulabilidade com acesso à propriedade

Link para esta seção

Para usar a checagem de anulabilidade, dá para usar declarações if ou o operador ternário (? e :) assim:

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

Mas isto só funciona ao fazer o ternário if / numa variável, e não num acesso de propriedade, assim:

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

O que retorna este erro:

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

Para consertar este problema, vale criar uma variável para aquele acesso à propriedade, para que o compilador faça a magia da checagem de anulabilidade:

class Container {
data: string | null
}
let container = new Container()
container.data = 'data'
let data = container.data
let somethingOrElse: string = data ? data : 'else' // compiles just fine :)

Sobrecarga de operador com acesso à propriedade

Link para esta seção

Se tentar somar (por exemplo) um tipo anulável (de um acesso de propriedade) com um não-anulável, em vez de soltar um aviso de erro de tempo de compilação a dizer que um dos valores é anulável, o compilador AssemblyScript só compilará em silêncio, o que abre chances para o código quebrar em meio ao tempo de execução.

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 // dá o erro de tempo de complação sobre anulabilidade
let wrapper = new Wrapper(y)
wrapper.n = wrapper.n + x // não dá erros de tempo de compilação como deveria

Nós abrimos um problema no compilador AssemblyScript para isto, mas por enquanto, se fizer estes tipos de operações nos seus mapeamentos de subgraph, vale mudá-las para fazer uma checagem de anulação antes delas.

let wrapper = new Wrapper(y)
if (!wrapper.n) {
wrapper.n = BigInt.fromI32(0)
}
wrapper.n = wrapper.n + x // agora o `n` é garantido a ser um BigInt

Inicialização de valor

Link para esta seção

Se tiver algum código como este:

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

Ele fará a compilação, mas quebrará no tempo de execução porque o valor não foi inicializado. Tenha certeza de que o seu subgraph inicializou os seus valores, como assim:

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

E também se tiver propriedades anuláveis numa entidade GraphQL, como assim:

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

E tiver código parecido com este:

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

Inicialize o valor total.amount, porque se tentar acessar como na última linha para a soma, ele irá travar. Então — ou inicializas primeiro:

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 pode simplesmente mudar o seu schema GraphQL para que não use um tipo anulável para esta propriedade, e o inicialize como zero no passo codegen 😉

type Total @entity {
id: Bytes!
amount: BigInt!
}
let total = Total.load('latest')
if (total === null) {
total = new Total('latest') // já inicializa propriedades não-anuláveis
}
total.amount = total.amount + BigInt.fromI32(1)

Iniciação de propriedade de classe

Link para esta seção

Se exportar quaisquer classes com propriedades que sejam outras classes (declaradas por você ou pela biblioteca padrão) como esta:

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

O compilador dará em erro, porque precisa adicionar um iniciador às propriedades que são classes, ou adicionar o operador !:

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
}

Iniciação de arranjo

Link para esta seção

A classe Array (arranjo) ainda aceita um número para iniciar o comprimento da lista, mas tome cuidado — porque operações como .push aumentarão o tamanho em vez de adicionar ao começo, por exemplo:

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

Dependendo dos tipos que usa, por ex., anuláveis, e como os acessa, pode encontrar um erro de tempo de execução como este:

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

Para empurrar no começo, inicialize o Array com o tamanho zero, assim:

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

Ou pode mudá-lo através do index:

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

Schema GraphQL

Link para esta seção

Isto não é uma mudança direta no AssemblyScript, mas pode ser que precise atualizar o seu arquivo schema.graphql.

Agora não há mais como definir campos nos seus tipos que são Listas Não Anuláveis. Se tiver um schema como este:

type Something @entity {
id: Bytes!
}
type MyEntity @entity {
id: Bytes!
invalidField: [Something]! # no longer valid
}

Adicione um ! ao membro do tipo de Lista, como:

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

Isto mudou por diferenças de anulabilidade entre versões do AssemblyScript, e tem relação ao arquivo src/generated/schema.ts (caminho padrão, talvez tenha mudado).

Outras informações

Link para esta seção
  • Alinhados Map#set e Set#add, com retorno de this (v0.9.2)
  • Os arranjos não herdam mais do ArrayBufferView, mas agora são distintos (v0.10.0)
  • Classes inicializadas de literais de objeto não podem mais definir um construtor (v0.10.0)
  • O resultado de uma operação binária, se ambos os operandos forem inteiros, ** agora é o inteiro denominador comum. Antes, o resultado era um float, como se chamasse o Math/f.pow (v0.11.0)
  • Coagir o NaN ao false ao converter em bool (v0.14.9)
  • Ao mudar um valor inteiro pequeno do tipo i8/u8 ou i16/u16, apenas os 3 respectivamente 4 bits menos significantes do valor RHS afetarão o resultado, análogo ao resultado de um i32.shl só a ser afetado pelos 5 bits menos significantes do valor RHS. Por exemplo: someI8 << 8 antes produzia o valor 0, mas agora produz o somel8 por mascarar o RHS como 8 & 7 = 0 (3 bits) (v0.17.0)
  • Consertado um erro de comparações relacionais de string quando os tamanhos diferem (v0.17.8)
Editar página

Anterior
Guia de migração de Validações GraphQL
Próximo
Substreams
Editar página