subgraphs > Developing > Creating > AssemblyScript API > AssemblyScript API

AssemblyScript API

Reading time: 18 min

Примечание: Если Вы создали субграф до версии graph-cli/graph-ts 0.22.0, значит, Вы используете более старую версию AssemblyScript. Рекомендуется ознакомиться с [Руководством по миграции`](/resources/release-notes/assemblyscript-migration-guide/).

Узнайте, какие встроенные API можно использовать при написании мэппингов субграфов. По умолчанию доступны два типа API:

Вы также можете добавлять другие библиотеки в качестве зависимостей, если они совместимы с AssemblyScript.

Поскольку мэппинги языков написаны на AssemblyScript, полезно ознакомиться с функциями языка и стандартной библиотеки на вики-странице AssemblyScript.

Референс API

Ссылка на этот раздел

Библиотека @graphprotocol/graph-ts предоставляет следующие API:

  • API ethereum для работы со смарт-контактами Ethereum, событиями, блоками, транзакциями и значениями Ethereum.
  • API store для загрузки и сохранения объектов из хранилища the Graph Node и в него.
  • API log для регистрации сообщений в выходных данных the Graph Node и Graph Explorer.
  • API ipfs для загрузки файлов из IPFS.
  • API json для выполнения разбора данных в формате JSON.
  • API crypto для использования криптографических функций.
  • Низкоуровневые примитивы для перевода между системами различных типов, таких как Ethereum, JSON, GraphQL и AssemblyScript.

apiVersion в манифесте субграфа указывает версию мэппинга API, которая запускается посредством Graph Node для данного субграфа.

ВерсияПримечания к релизу
0.0.9Добавлены новые функции хоста eth_get_balance и hasCode
0.0.8Добавлена проверка наличия полей в схеме при сохранении объекта.
0.0.7К типам Ethereum добавлены классы TransactionReceipt и Log
К объекту Ethereum Event добавлено поле receipt
0.0.6В объект Ethereum Transaction добавлено поле nonce
В объект Ethereum Block добавлено поле baseFeePerGas
0.0.5AssemblyScript обновлен до версии 0.19.10 (включая критические изменения, см. Руководство по миграции)
ethereum.transaction.gasUsed переименован в ethereum.transaction.gasLimit
0.0.4В объект Ethereum SmartContractCall добавлено поле functionSignature
0.0.3В объект Ethereum Call добавлено поле from
etherem.call.address переименован в ethereum.call.to`
0.0.2В объект Ethereum Transaction добавлено поле input

Встроенные типы

Ссылка на этот раздел

Документацию по базовым типам, встроенным в AssemblyScript, можно найти в AssemblyScript wiki.

Дополнительные типы, предоставляемые @graphprotocol/graph-ts.

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

ByteArray представляет собой массив u8.

Конструкция

  • fromI32(x: i32): ByteArray — Разбивает x на байты.
  • fromHexString(hex: string): ByteArray - Длина ввода должна быть четной. Префикс 0x необязателен.

Преобразования типов

  • toHexString(): string - Преобразуется в шестнадцатеричную строку с префиксом 0x.
  • toString(): string - Интерпретирует байты как строку UTF-8.
  • to Base 58(): string - Кодирует байты в строку base58.
  • to U32(): u32 - Интерпретирует байты как little-endian u32. Выбрасывает в случае переполнения.
  • to I32(): i32 - Интерпретирует массив байтов как little-endian i32. Выбрасывает в случае переполнения.

Операторы

  • equals(y: ByteArray): bool – может быть записано как x == y.
  • concat(other: ByteArray) : ByteArray - возвращает новый ByteArray, состоящий из this, за которым непосредственно следует other
  • concatI32(other: i32) : ByteArray - возвращает новый ByteArray, состоящий из this, за которым непосредственно следует байтовое представление other
import { BigDecimal } from '@graphprotocol/graph-ts'

BigDecimal используется для представления десятичных знаков произвольной точности.

Примечание: Внутренне BigDecimal хранится в формате с плавающей точкой IEEE-754 decimal128, который поддерживает 34 десятичных знака после запятой. Это делает BigDecimal непригодным для представления типов с фиксированной точкой, которые могут охватывать более 34 знаков, таких как Solidity ufixed256x18 или его эквивалентов.

Конструкция

  • constructor(bigInt: BigInt) — создает BigDecimal из BigInt.
  • static fromString(s: string): BigDecimal – выполняет синтаксический разбор из десятичной строки.

Преобразования типов

  • toString(): string – выводит в виде десятичной строки.

Математика

  • plus(y: BigDecimal): BigDecimal – может быть записано как x + y.
  • minus(y: BigDecimal): BigDecimal – может быть записано как x - y.
  • times(y: BigDecimal): BigDecimal – может быть записано как x * y.
  • div(y: BigDecimal): BigDecimal – может быть записано как x / y.
  • equals(y: BigDecimal): bool – может быть записано как x == y.
  • notEqual(y: BigDecimal): bool – может быть записано как x != y.
  • lt(y: BigDecimal): bool – может быть записано как x < y.
  • le(y: BigDecimal): bool – может быть записано как x <= y.
  • gt(y: BigDecimal): bool – может быть записано как x > y.
  • ge(y: BigDecimal): bool – может быть записано как x >= y.
  • neg(): BigDecimal - может быть записано как -x.
import { BigInt } from '@graphprotocol/graph-ts'

BigInt используется для представления больших целых чисел. Сюда входят значения Ethereum типа от uint32 до uint256 и от int64 до int256. Все, что находится ниже uint32, например int32, uint24 или int8, представлено как i32.

Класс BigInt имеет следующий API:

Конструкция

  • BigInt.fromI32(x: i32): BigInt – создает BigInt из i32.

  • BigInt.fromString(s:string): BigInt– Выполняет разбор BigInt из строки.

  • BigInt.fromUnsignedBytes(x: Bytes): BigInt — Интерпретирует bytes беззнаковое целое число в формате little-endian (младшие байты идут первыми). Если Ваши входные данные в формате big-endian (старшие байты идут первыми), сначала вызовите .reverse().

  • BigInt.fromSignedBytes(x: Bytes): BigInt — Интерпретирует bytes как знаковое целое число в формате little-endian (младшие байты идут первыми). Если Ваши входные данные в формате big-endian (старшие байты идут первыми), сначала вызовите .reverse().

    Преобразования типов

  • x.toHex(): string – преобразует BigInt в строку шестнадцатеричных символов.

  • x.toString(): string – преобразует BigInt в строку десятичных чисел.

  • x.toI32(): i32 – возвращает BigInt в виде i32; завершается с ошибкой, если значение не соответствует i32. Рекомендуется сначала проверить x.is I32().

  • x.to BigDecimal(): BigDecimal - преобразует в десятичное число без дробной части.

Математика

  • x.plus(y: BigInt): BigInt – может быть записано как x + y.
  • x.minus(y: BigInt): BigInt – может быть записано как x - y.
  • x.times(y: BigInt): BigInt – может быть записано как x * y.
  • x.div(y: BigInt): BigInt – может быть записано как x / y.
  • x.mod(y: BigInt): BigInt – может быть записано как x % y.
  • x.equals(y: BigInt): bool – может быть записано как x == y.
  • x.notEqual(y: BigInt): bool – может быть записано как x != y.
  • x.lt(y: BigInt): bool – может быть записано как x < y.
  • x.le(y: BigInt): bool – может быть записано как x <= y.
  • x.gt(y: BigInt): bool – может быть записано как x > y.
  • x.ge(y: BigInt): bool – может быть записано как x >= y.
  • x.neg(): BigInt – может быть записано как -x.
  • x.divDecimal(y: BigDecimal): BigDecimal – делит на десятичное число, что дает десятичный результат.
  • x.isZero(): bool – Удобство для проверки, равно ли число нулю.
  • x.isI32(): bool – Проверяет, соответствует ли число i32.
  • x.abs(): BigInt – Абсолютное значение.
  • x.pow(exp: u8): BigInt – Возведение в степень.
  • bitOr(x: BigInt, y: BigInt): BigInt – может быть записан как x | y.
  • bitAnd(x: BigInt, y: BigInt): BigInt – может быть записан как x & y.
  • leftShift(x: BigInt, bits: u8): BigInt – может быть записан как x << y.
  • rightShift(x: BigInt, bits: u8): BigInt – может быть записан как x >> y.
import { TypedMap } from '@graphprotocol/graph-ts'

TypedMap можно использовать для хранения пар ключ-значение. Смотрите этот пример.

Класс TypedMap имеет следующий API:

  • new TypedMap<K, V>() создает пустую карту с ключами типа K и значениями типа V
  • map.set(key: K, value: V): void – устанавливает значение key в value
  • map.getEntry(key: K): TypedMapEntry<K, V> | null – возвращает пару ключ-значение для key или null, если key не существует на карте
  • map.get(key: K): V | null – возвращает значение для key или null, если key не существует на карте
  • map.isSet(key: K): bool – возвращает true, если key существует на карте, и false, если его нет
import { Bytes } from '@graphprotocol/graph-ts'

Bytes используется для представления массивов байтов произвольной длины. Сюда входят значения Ethereum типа bytes, bytes32 и т. д.

Класс Bytes расширяет [Uint8Array] из AssemblyScript (https://github.com/AssemblyScript/assemblyscript/blob/3b1852bc376ae799d9ebca888e6413afac7b572f/std/assembly/typedarray.ts#L64) и поддерживает все функциональные возможности Uint8Array, а также следующие новые методы:

Конструкция

  • fromHexString(hex: string) : Bytes - Преобразует строку hex, которая должна состоять из четного числа шестнадцатеричных цифр, в ByteArray. Строка hex может опционально начинаться с 0x
  • fromI32(i: i32) : Bytes - Преобразовывает i в массив байтов

Преобразования типов

  • b.toHex() – возвращает шестнадцатеричную строку, представляющую байты в массиве
  • b.toString() – преобразует байты в массиве в строку символов Unicode
  • b.toBase58() – преобразует значение Ethereum Bytes в кодировку base58 (используется для хэшей IPFS)

Операторы

  • b.concat(other: Bytes) : Bytes - - возвращает новые Bytes, состоящие из this, за которым непосредственно следует other
  • b.concatI32(other: i32) : ByteArray - возвращает новые Bytes, состоящие из this, за которым непосредственно следует байтовое представление other
import { Address } from '@graphprotocol/graph-ts'

Address расширяет Bytes для представления значений Ethereum address.

Поверх Bytes API добавляется следующий метод:

  • Address.fromString(s: string): Address – создает Address из шестнадцатеричной строки
  • Address.fromBytes(b: Bytes): Address — создайте Address из b длиной ровно 20 байт. Передача значения с меньшим или большим количеством байт приведет к ошибке
import { store } from '@graphprotocol/graph-ts'

API store позволяет загружать, сохранять и удалять объекты из хранилища the Graph Node и в него.

Объекты, записанные в хранилище карты, сопоставляются один к одному с типами @entity, определенными в схеме субграфов GraphQL. Чтобы сделать работу с этими объектами удобной, команда graph codegen, предоставляемая Graph CLI генерирует классы объектов, которые являются подклассами встроенного типа Entity, с геттерами и сеттерами свойств для полей в схеме, а также методами загрузки и сохранения этих объектов.

Создание объектов

Ссылка на этот раздел

Ниже приведен общий шаблон для создания объектов из событий Ethereum.

// Импорт класса событий Transfer, сгенерированного из ERC20 ABI
import { Transfer as TransferEvent } from '../generated/ERC20/ERC20'
// Импорт типа объекта Transfer, сгенерированного из схемы GraphQL
import { Transfer } from '../generated/schema'
событие
// Обработчик события передачи
экспортирует функцию handleTransfer(event: TransferEvent): void {
// Создание объекта 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()
}

Когда при обработке чейна возникает событие Transfer, оно передается обработчику события handleTransfer, используя сгенерированный тип Transfer (здесь используется псевдоним TransferEvent, чтобы избежать конфликта наименования с типом объекта). Этот тип позволяет получить доступ к таким данным, как материнская транзакция события и ее параметры.

Каждый объект должен иметь уникальный идентификатор, чтобы избежать конфликтов с другими объектами. Параметры событий довольно часто включают уникальный идентификатор, который можно использовать.

Примечание: Использование хэша транзакции в качестве идентификатора предполагает, что никакие другие события в той же транзакции не создают объекты с этим хэшем в качестве идентификатора.

Загрузка объектов из хранилища

Ссылка на этот раздел

Если объект уже существует, его можно загрузить из хранилища следующим образом:

let id = event.transaction.hash // или некоторым образом создается идентификатор
let transfer = Transfer.load(id)
if (transfer == null) {
transfer = new Transfer(id)
}
// Используйте объект Transfer, как и раньше

Поскольку объект может еще не существовать в хранилище, метод load возвращает значение типа Transfer | null. Таким образом, перед использованием значения может потребоваться проверка на наличие null.

Примечание: Загрузка объектов необходима только в том случае, если изменения, внесенные в мэппинг, зависят от предыдущих данных объекта. В следующем разделе описаны два способа обновления существующих объектов.

Поиск объектов, созданных внутри блока

Ссылка на этот раздел

Начиная с graph-node v0.31.0, @graphprotocol/graph-ts v0.30.0 и @graphprotocol/graph-cli v0.49.0 метод loadInBlock доступен для всех типов объектов.

API хранилища облегчает извлечение объектов, которые были созданы или обновлены в текущем блоке. Типичная ситуация: один обработчик создает транзакцию из какого-то события в он-чейне, а следующий обработчик хочет получить доступ к этой транзакции, если она существует.

  • В случае, если транзакция не существует, субграф должен будет обратиться к базе данных просто для того, чтобы узнать, что объект не существует. Если автор субграфа уже знает, что объект должен быть создан в том же блоке, использование loadInBlock позволяет избежать этого обращения к базе данных.
  • Для некоторых субграфов эти пропущенные поиски могут существенно увеличить время индексации.
let id = event.transaction.hash // или некоторым образом создается идентификатор
let transfer = Transfer.loadInBlock(id)
if (transfer == null) {
transfer = new Transfer(id)
}
// Используйте объект Transfer, как и раньше

Примечание: Если в данном блоке не создан объект, loadInBlock вернет null, даже если в хранилище есть объект с данным идентификатором.

Поиск производных объектов

Ссылка на этот раздел

Начиная с graph-node v0.31.0, @graphprotocol/graph-ts v0.31.0 и @graphprotocol/graph-cli v0.51.0 метод loadRelated доступен.

Это позволяет загружать поля производных объектов из обработчика событий. Например, учитывая следующую схему:

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

Следующий код загрузит объект Token, из которого был получен объект Holder:

let holder = Holder.load('test-id')
// Загрузите объекты токена, связанные с данным держателем
let tokens = holder.tokens.load()

Обновление существующих объектов

Ссылка на этот раздел

Существует два способа обновить существующий объект:

  1. Загрузите объект, например, с помощью Transfer.load(id), установите свойства объекта, затем с помощью .save() верните его обратно в хранилище.
  2. Просто создайте объект, например, с помощью new Transfer(id), установите свойства объекта, затем с помощью .save() сохраните его в хранилище. Если объект уже существует, изменения будут объединены с ним.

Изменение свойств в большинстве случаев не вызывает затруднений благодаря сгенерированным установщикам свойств:

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

Также можно сбросить свойства с помощью одной из следующих двух инструкций:

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

Это работает только с необязательными свойствами, то есть свойствами, объявленными без ! в GraphQL. Два примера: owner: Bytes или amount: BigInt.

Обновление свойств массива немного сложнее, поскольку получение массива из объекта создает копию этого массива. Это означает, что после изменения массива необходимо снова точно задать его свойства. В следующем примере предполагается, что entity имеет поле numbers: [BigInt!]!.

// Это не сработает
entity.numbers.push(BigInt.fromI32(1))
entity.save()
// Это сработает
let numbers = entity.numbers
numbers.push(BigInt.fromI32(1))
entity.numbers = numbers
entity.save()

Удаление объектов из хранилища

Ссылка на этот раздел

В настоящее время нет способа удалить объект с помощью сгенерированных типов. Вместо этого для удаления объекта требуется передать имя типа объекта и идентификатор объекта в store.remove:

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

Ethereum API предоставляет доступ к смарт-контрактам, общедоступным переменным состояния, функциям контрактов, событиям, транзакциям, блокам и кодированию/декодированию данных Ethereum.

Поддержка типов Ethereum

Ссылка на этот раздел

Как и в случае с объектами, graph codegen генерирует классы для всех смарт-контрактов и событий, используемых в субграфе. Для этого ABI контракта должны быть частью источника данных в манифесте субграфа. Как правило, файлы ABI хранятся в папке abis/.

С помощью сгенерированных классов преобразования между типами Ethereum и [встроенными типами] (#built-in-types) происходят за кулисами, так что авторам субграфов не нужно беспокоиться о них.

Следующий пример иллюстрирует это. С учётом схемы субграфа, такой как

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

и сигнатура события Transfer(address,address,uint256) на Ethereum, значения from, to и amount типа address, address и uint256 преобразуются в Address и BigInt, что позволяет передавать их в свойства Bytes! и BigInt! объекта 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()

События и данные о блоках/транзакциях

Ссылка на этот раздел

События Ethereum, передаваемые обработчикам событий, такие как событие Transfer в предыдущих примерах, предоставляют доступ не только к параметрам события, но и к их материнской транзакции и блоку, частью которого они являются. Следующие данные могут быть получены из экземпляров event (эти классы являются частью модуля ethereum в 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
}

Доступ к состоянию смарт-контракта

Ссылка на этот раздел

Код, сгенерированный с помощью graph codegen, также включает классы для смарт-контрактов, используемых в субграфе. Они могут быть использованы для доступа к общедоступным переменным состояния и вызова функций контракта в текущем блоке.

Распространенным шаблоном является доступ к контракту, из которого исходит событие. Это достигается с помощью следующего кода:

// Импорт сгенерированного класса контракта и сгенерированного класса события Transfer
import { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'
// Импорт созданного класса объекта
import { Transfer } from '../generated/schema'
export function handleTransfer(event: TransferEvent) {
// Привязка контракта к адресу, сгенерировавшему событие
let contract = ERC20Contract.bind(event.address)
// Доступ к переменным состояния и функциям путем их вызова
пусть erc20Symbol = контракт.символ()
}

Transfer связывается с TransferEvent, чтобы избежать конфликта наименований с типом объекта

Пока ERC20Contract в Ethereum имеет общедоступную функцию только для чтения, называемую symbol, ее можно вызвать с помощью .symbol(). Для общедоступных переменных состояния автоматически создается метод с таким же именем.

Любой другой контракт, который является частью субграфа, может быть импортирован из сгенерированного кода и привязан к действительному адресу.

Обработка возвращенных вызовов

Ссылка на этот раздел

Если методы Вашего контракта, доступные только для чтения, могут вернуться к предыдущему состоянию, то Вам следует решить эту проблему, вызвав сгенерированный метод контракта с префиксом try_.

  • Например, контракт Gravity предоставляет метод gravatarToOwner. Этот код сможет выполнить возврат в этом методе:
let gravity = Gravity.bind(event.address)
let callResult = gravity.try_gravatarToOwner(gravatar)
if (callResult.reverted) {
log.info('getGravatar reverted', [])
} else {
let owner = callResult.value
}

Примечание: Graph Node, подключенный к клиенту Geth или Infura, может обнаружить не все откаты. Если Вы полагаетесь на это, мы рекомендуем использовать Graph Node, подключенный к клиенту Parity.

Кодирование/декодирование ABI

Ссылка на этот раздел

Данные могут быть закодированы и декодированы в соответствии с форматом кодирования ABI Ethereum с использованием функций encode и decode в модуле 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)

Для получения дополнительной информации:

Баланс адреса

Ссылка на этот раздел

Баланс нативного токена адреса можно получить с помощью модуля ethereum. Эта функция доступна начиная с apiVersion: 0.0.9, которая определена subgraph.yaml. getBalance() извлекает баланс указанного адреса на конец блока, в котором инициировано событие.

import { ethereum } from '@graphprotocol/graph-ts'
let address = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
let balance = ethereum.getBalance(address) // возвращает баланс в BigInt

Проверьте, является ли адрес контрактом или EOA

Ссылка на этот раздел

Чтобы проверить, является ли адрес адресом смарт-контракта или внешним адресом (EOA), используйте функцию hasCode() из модуля ethereum, которая вернет boolean. Эта функция доступна начиная с apiVersion: 0.0.9, которая определена subgraph.yaml.

import { ethereum } from '@graphprotocol/graph-ts'
let contractAddr = Address.fromString('0x2E645469f354BB4F5c8a05B3b30A929361cf77eC')
let isContract = ethereum.hasCode(contractAddr).inner // возвращает ложное значение
let eoa = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
let isContract = ethereum.hasCode(eoa).inner // возвращает ложное значение

Регистрация API

Ссылка на этот раздел
import { log } from '@graphprotocol/graph-ts'

API log позволяет субграфам записывать информацию в стандартный вывод Graph Node, а также в Graph Explorer. Сообщения могут быть зарегистрированы с использованием различных уровней ведения лога. Для составления сообщений лога из аргумента предусмотрен синтаксис строки базового формата.

API log включает в себя следующие функции:

  • log.debug(fmt: string, args: Array<string>): void - регистрирует сообщение об отладке.
  • log.info (fmt: string, args: Array<string>): void - регистрирует информационное сообщение.
  • log.warning(fmt: string, args: Array<string>): void - регистрирует предупреждение.
  • log.error(fmt: string, args: Array<string>): void - регистрирует сообщение об ошибке.
  • log.critical(fmt: string, args: Array<string>): void – регистрирует критическое сообщение и завершает работу субграфа.

API log принимает строку формата и массив строковых значений. Затем он заменяет заполнители строковыми значениями из массива. Первый {} заполнитель заменяется первым значением в массиве, второй {} заполнитель заменяется вторым значением и так далее.

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

Регистрация одного или нескольких значений

Ссылка на этот раздел
Регистрация одного значения
Ссылка на этот раздел

В приведенном ниже примере строковое значение "A" передается в массив, чтобы стать ['A'] перед тем как будет записано в лог:

let myValue = 'A'
export function handleSomeEvent(event: SomeEvent): void {
// Отображает: "My value is: A"
log.info('My value is: {}', [myValue])
}
Регистрация одной записи из существующего массива
Ссылка на этот раздел

В приведенном ниже примере регистрируется только первое значение массива аргументов, несмотря на то, что массив содержит три значения.

let myArray = ['A', 'B', 'C']
export function handleSomeEvent(event: SomeEvent): void {
// Отображает : "My value is: A" (Несмотря на то, что в `log.info` передаются три значения)
log.info('My value is: {}', myArray)
}

Регистрация нескольких записей из существующего массива

Ссылка на этот раздел

Для каждой записи в массиве arguments требуется свой собственный заполнитель {} в строке сообщения лога. В приведенном ниже примере в сообщении лога содержатся три заполнителя {}. По этой причине в myArray регистрируются все три значения.

let myArray = ['A', 'B', 'C']
export function handleSomeEvent(event: SomeEvent): void {
// Отображает : "My first value is: A, second value is: B, third value is: C"
log.info('My first value is: {}, second value is: {}, third value is: {}', myArray)
}
Регистрация конкретной записи из существующего массива
Ссылка на этот раздел

Чтобы отобразить определенное значение в массиве, необходимо указать индексированное значение.

export function handleSomeEvent(event: SomeEvent): void {
// Отображает : "My third value is C"
log.info('My third value is: {}', [myArray[2]])
}
Регистрация информации о событии
Ссылка на этот раздел

В приведенном ниже примере регистрируется номер блока, хэш блока и хэш транзакции из события:

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

Смарт-контракты иногда привязывают файлы IPFS к чейну. Это позволяет мэппингам получать хэши IPFS из контракта и считывать соответствующие файлы из IPFS. Данные файла будут возвращены в виде Bytes, что обычно требует дальнейшей обработки, например, с помощью json API, описанного далее на этой странице.

При наличии хеша или пути IPFS чтение файла из IPFS выполняется следующим образом:

// Поместите это в обработчик события в мэппинге
let hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'
let data = ipfs.cat(hash)
// Пути, подобные `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`,
// которые включают файлы в директориях, также поддерживаются
let path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'
let data = ipfs.cat(path)

Примечание: pfs.cat на данный момент не является детерминированным. Если файл не может быть получен по сети IPFS до истечения времени ожидания запроса, он вернет null. В связи с этим всегда стоит проверять результат на наличие null.

С помощью ipfs.map можно также обрабатывать файлы большего размера в потоковом режиме. Функция ожидает, что хэш или путь к файлу IPFS, имя обратного вызова и флаги изменят его поведение:

import { JSONValue, Value } from '@graphprotocol/graph-ts'
export function processItem(value: JSONValue, userData: Value): void {
// Смотрите документацию по JsonValue для получения подробной информации о работе
// со значениями JSON
let obj = value.toObject()
let id = obj.get('id')
let title = obj.get('title')
if (!id || !title) {
return
}
// Обратные вызовы также могут создавать объекты
let newItem = new Item(id)
newItem.title = title.toString()
newitem.parent = userData.toString() // Установите для родителя значение "parentId"
newitem.save()
}
// Поместите это внутри обработчика событий в мэппинге
ipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])
// В качестве альтернативы, используйте `ipfs.mapJSON`
ipfs.mapJSON('Qm...', 'processItem', Value.fromString('parentId'))

Единственным поддерживаемым в настоящее время флагом является json, который должен быть передан в ipfs.map. С флагом json файл IPFS должен состоять из серии значений JSON, по одному значению в строке. Вызов ipfs.map прочитает каждую строку в файле, десериализует ее в JSONValue и совершит обратный вызов для каждой из них. Затем обратный вызов может использовать операции с объектами для хранения данных из JSONValue. Изменения объекта сохраняются только после успешного завершения обработчика, вызвавшего ipfs.map; в то же время они хранятся в памяти, и поэтому размер файла, который может обработать ipfs.map, ограничен.

При успешном завершении ipfs.map возвращает void. Если какое-либо совершение обратного вызова приводит к ошибке, обработчик, вызвавший ipfs.map, прерывается, а субграф помечается как давший сбой.

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

API crypto делает криптографические функции доступными для использования в мэппингах. На данный момент есть только один:

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

Данные JSON могут быть разобраны с помощью json API:

  • json.fromBytes(data: Bytes): JSONValue – выполняет разбор данных JSON из массива Bytes, интерпретируемого как допустимая последовательность UTF-8
  • json.try_fromBytes(data: Bytes): Result<JSONValue, boolean> – безопасная версия json.fromBytes, возвращает вариант ошибки, если выполнение разбора не удалось
  • json.fromString(data: string): JSONValue – выполняет разбор данных JSON из допустимой UTF-8 String
  • json.try_fromString(data: string): Result<JSONValue, boolean> – безопасная версия json.fromString, возвращает вариант ошибки, если выполнение разбора не удалось

Класс JSONValue предоставляет способ извлечения значений из произвольного документа JSON. Поскольку значениями JSON могут быть логические значения, числа, массивы и многое другое, JSONValue поставляется со свойством kind для проверки типа значения:

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

Кроме того, существует способ проверить, является ли значение null:

  • value.isNull(): boolean

Когда тип значения определен, его можно преобразовать во встроенный тип, используя один из следующих методов:

  • value.toBool(): boolean
  • value.toI64(): i64
  • value.toF64(): f64
  • value.toBigInt(): BigInt
  • value.toString(): string
  • value.toArray(): Array<JSONValue> - (а затем преобразовать JSONValue одним из 5 методов, описанных выше)

Справка по преобразованию типов

Ссылка на этот раздел
Источник(и)Место назначенияФункция преобразования
AddressBytesотсутствует
AddressStrings.toHexString()
BigDecimalStrings.toString()
BigIntBigDecimals.toBigDecimal()
BigIntString (hexadecimal)s.toHexString() или s.toHex()
BigIntString (unicode)s.toString()
BigInti32s.toI32()
BooleanBooleanотсутствует
Bytes (signed)BigIntBigInt.fromSignedBytes(s)
Bytes (unsigned)BigIntBigInt.fromUnsignedBytes(s)
BytesString (hexadecimal)s.toHexString() или s.toHex()
BytesString (unicode)s.toString()
BytesString (base58)s.toBase58()
Bytesi32s.toI32()
Bytesu32s.toU32()
BytesJSONjson.fromBytes(s)
int8i32отсутствует
int32i32отсутствует
int32BigIntBigInt.fromI32(s)
uint24i32отсутствует
int64 - int256BigIntотсутствует
uint32 - uint256BigIntотсутствует
JSONbooleans.toBool()
JSONi64s.toU64()
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)

Метаданные источника данных

Ссылка на этот раздел

Вы можете проверить адрес контракта, сеть и контекст источника данных, который вызвал обработчик, через пространство имен DataSource:

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

Объект и DataSourceContext

Ссылка на этот раздел

Базовый класс Entity и дочерний класс DataSourceContext имеют помощников для динамической установки и получения полей:

  • 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 в манифесте

Ссылка на этот раздел

Раздел context в dataSources позволяет Вам определять пары ключ-значение, которые доступны в Ваших мэппингах субграфа. Доступные типы: Bool, String, Int, Int8, BigDecimal, Bytes, List и BigInt.

Ниже приведен пример YAML, иллюстрирующий использование различных типов в разделе 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: Определяет логическое значение (true или false).
  • String: Определяет строковое значение.
  • Int: Определяет 32-разрядное целое число.
  • Int8: Определяет 8-разрядное целое число.
  • BigDecimal: Определяет десятичное число. Необходимо заключить в кавычки.
  • Bytes: Определяет шестнадцатеричную строку.
  • List: Определяет список элементов. Для каждого элемента необходимо указать его тип и данные.
  • BigInt: Определяет большое целочисленное значение. Необходимо заключить в кавычки из-за большого размера.

Затем этот контекст становится доступным в Ваших мэппинговых файлах субграфов, что позволяет сделать субграфы более динамичными и настраиваемыми.

Редактировать страницу

Предыдущий
Advance Subgraph Features
Следующий
Распространенные проблемы с AssemblyScript
Редактировать страницу