19 минуты
AssemblyScript API
Note: If you created a Subgraph prior to graph-cli
/graph-ts
version 0.22.0
, then you’re using an older version of AssemblyScript. It is recommended to review the Migration Guide
.
Learn what built-in APIs can be used when writing Subgraph mappings. There are two kinds of APIs available out of the box:
- Библиотека The Graph TypeScript (
graph-ts
) - Code generated from Subgraph files by
graph codegen
Вы также можете добавлять другие библиотеки в качестве зависимостей, если они совместимы с 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.
Версии
The apiVersion
in the Subgraph manifest specifies the mapping API version which is run by Graph Node for a given Subgraph.
Версия | Примечания к релизу |
---|---|
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.5 | AssemblyScript upgraded to version 0.19.10 (this includes breaking changes, please see the Migration Guide )ethereum.transaction.gasUsed renamed to ethereum.transaction.gasLimit |
0.0.4 | В объект Ethereum SmartContractCall добавлено поле functionSignature |
0.0.3 | Added from field to the Ethereum Call objectethereum.call.address renamed to ethereum.call.to |
0.0.2 | В объект Ethereum Transaction добавлено поле input |
Встроенные типы
Документацию по базовым типам, встроенным в AssemblyScript, можно найти в AssemblyScript wiki.
Дополнительные типы, предоставляемые @graphprotocol/graph-ts
.
ByteArray
1import { 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-endianu32
. Выбрасывает в случае переполнения.to I32(): i32
- Интерпретирует массив байтов как little-endiani32
. Выбрасывает в случае переполнения.
Операторы
equals(y: ByteArray): bool
– может быть записано какx == y
.concat(other: ByteArray) : ByteArray
- возвращает новыйByteArray
, состоящий изthis
, за которым непосредственно следуетother
concatI32(other: i32) : ByteArray
- возвращает новыйByteArray
, состоящий изthis
, за которым непосредственно следует байтовое представлениеother
BigDecimal
1import { 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
.
BigInt
1import { 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
.
TypedMap
1import { 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
, если его нет
Bytes
1import { 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()
– преобразует байты в массиве в строку символов Unicodeb.toBase58()
– преобразует значение EthereumBytes
в кодировкуbase58
(используется для хэшей IPFS)
Операторы
b.concat(other: Bytes) : Bytes
- - возвращает новыеBytes
, состоящие изthis
, за которым непосредственно следуетother
b.concatI32(other: i32) : ByteArray
- возвращает новыеBytes
, состоящие изthis
, за которым непосредственно следует байтовое представлениеother
Address
1import { 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 байт. Передача значения с меньшим или большим количеством байт приведет к ошибке
Store API
1import { store } from '@graphprotocol/graph-ts'
API store
позволяет загружать, сохранять и удалять объекты из хранилища the Graph Node и в него.
Entities written to the store map one-to-one to the @entity
types defined in the Subgraph’s GraphQL schema. To make working with these entities convenient, the graph codegen
command provided by the Graph CLI generates entity classes, which are subclasses of the built-in Entity
type, with property getters and setters for the fields in the schema as well as methods to load and save these entities.
Создание объектов
Ниже приведен общий шаблон для создания объектов из событий Ethereum.
1// Импорт класса событий Transfer, сгенерированного из ERC20 ABI2import { Transfer as TransferEvent } from '../generated/ERC20/ERC20'34// Импорт типа объекта Transfer, сгенерированного из схемы GraphQL5import { Transfer } from '../generated/schema'6событие7// Обработчик события передачи8экспортирует функцию handleTransfer(event: TransferEvent): void {9 // Создание объекта Transfer, с использованием хеша транзакции в качестве идентификатора объекта10let id = event.transaction.hash11 let transfer = new Transfer(id)1213 // Установка свойства объекта, с использованием параметров события14transfer.from = event.params.from15 transfer.to = event.params.to16 transfer.amount = event.params.amount1718 // Сохранение объекта в хранилище19transfer.save()20}
Когда при обработке чейна возникает событие Transfer
, оно передается обработчику события handleTransfer
, используя сгенерированный тип Transfer
(здесь используется псевдоним TransferEvent
, чтобы избежать конфликта наименования с типом объекта). Этот тип позволяет получить доступ к таким данным, как материнская транзакция события и ее параметры.
Каждый объект должен иметь уникальный идентификатор, чтобы избежать конфликтов с другими объектами. Параметры событий довольно часто включают уникальный идентификатор, который можно использовать.
Примечание: Использование хэша транзакции в качестве идентификатора предполагает, что никакие другие события в той же транзакции не создают объекты с этим хэшем в качестве идентификатора.
Загрузка объектов из хранилища
Если объект уже существует, его можно загрузить из хранилища следующим образом:
1let id = event.transaction.hash // или некоторым образом создается идентификатор2let transfer = Transfer.load(id)3if (transfer == null) {4 transfer = new Transfer(id)5}67// Используйте объект Transfer, как и раньше
Поскольку объект может еще не существовать в хранилище, метод load
возвращает значение типа Transfer | null
. Таким образом, перед использованием значения может потребоваться проверка на наличие null
.
Примечание: Загрузка объектов необходима только в том случае, если изменения, внесенные в мэппинг, зависят от предыдущих данных объекта. В следующем разделе описаны два способа обновления существующих объектов.
Поиск объектов, созданных внутри блока
Начиная с graph-node
v0.31.0, @graphprotocol/graph-ts
v0.30.0 и @graphprotocol/graph-cli
v0.49.0 метод loadInBlock
доступен для всех типов объектов.
The store API facilitates the retrieval of entities that were created or updated in the current block. A typical situation for this is that one handler creates a transaction from some onchain event, and a later handler wants to access this transaction if it exists.
- In the case where the transaction does not exist, the Subgraph will have to go to the database simply to find out that the entity does not exist. If the Subgraph author already knows that the entity must have been created in the same block, using
loadInBlock
avoids this database roundtrip. - For some Subgraphs, these missed lookups can contribute significantly to the indexing time.
1let id = event.transaction.hash // или некоторым образом создается идентификатор2let transfer = Transfer.loadInBlock(id)3if (transfer == null) {4 transfer = new Transfer(id)5}67// Используйте объект Transfer, как и раньше
Примечание: Если в данном блоке не создан объект, loadInBlock
вернет null
, даже если в хранилище есть объект с данным идентификатором.
Поиск производных объектов
Начиная с graph-node
v0.31.0, @graphprotocol/graph-ts
v0.31.0 и @graphprotocol/graph-cli
v0.51.0 метод loadRelated
доступен.
Это позволяет загружать поля производных объектов из обработчика событий. Например, учитывая следующую схему:
1type Token @entity {2 id: ID!3 holder: Holder!4 color: String5}67type Holder @entity {8 id: ID!9 tokens: [Token!]! @derivedFrom(field: "holder")10}
Следующий код загрузит объект Token
, из которого был получен объект Holder
:
1let holder = Holder.load('test-id')2// Загрузите объекты токена, связанные с данным держателем3let tokens = holder.tokens.load()
Обновление существующих объектов
Существует два способа обновить существующий объект:
- Загрузите объект, например, с помощью
Transfer.load(id)
, установите свойства объекта, затем с помощью.save()
верните его обратно в хранилище. - Просто создайте объект, например, с помощью
new Transfer(id)
, установите свойства объекта, затем с помощью.save()
сохраните его в хранилище. Если объект уже существует, изменения будут объединены с ним.
Изменение свойств в большинстве случаев не вызывает затруднений благодаря сгенерированным установщикам свойств:
1let transfer = new Transfer(id)2transfer.from = ...3transfer.to = ...4transfer.amount = ...
Также можно сбросить свойства с помощью одной из следующих двух инструкций:
1transfer.from.unset()2transfer.from = null
Это работает только с необязательными свойствами, то есть свойствами, объявленными без !
в GraphQL. Два примера: owner: Bytes
или amount: BigInt
.
Обновление свойств массива немного сложнее, поскольку получение массива из объекта создает копию этого массива. Это означает, что после изменения массива необходимо снова точно задать его свойства. В следующем примере предполагается, что entity
имеет поле numbers: [BigInt!]!
.
1// Это не сработает2entity.numbers.push(BigInt.fromI32(1))3entity.save()45// Это сработает6let numbers = entity.numbers7numbers.push(BigInt.fromI32(1))8entity.numbers = numbers9entity.save()
Удаление объектов из хранилища
В настоящее время нет способа удалить объект с помощью сгенерированных типов. Вместо этого для удаления объекта требуется передать имя типа объекта и идентификатор объекта в store.remove
:
1import { store } from '@graphprotocol/graph-ts'2...3let id = event.transaction.hash4store.remove('Transfer', id)
Ethereum API
Ethereum API предоставляет доступ к смарт-контрактам, общедоступным переменным состояния, функциям контрактов, событиям, транзакциям, блокам и кодированию/декодированию данных Ethereum.
Поддержка типов Ethereum
As with entities, graph codegen
generates classes for all smart contracts and events used in a Subgraph. For this, the contract ABIs need to be part of the data source in the Subgraph manifest. Typically, the ABI files are stored in an abis/
folder.
With the generated classes, conversions between Ethereum types and the built-in types take place behind the scenes so that Subgraph authors do not have to worry about them.
The following example illustrates this. Given a Subgraph schema like
1type Transfer @entity {2 id: Bytes!3 from: Bytes!4 to: Bytes!5 amount: BigInt!6}
и сигнатура события Transfer(address,address,uint256)
на Ethereum, значения from
, to
и amount
типа address
, address
и uint256
преобразуются в Address
и BigInt
, что позволяет передавать их в свойства Bytes!
и BigInt!
объекта Transfer
:
1let id = event.transaction.hash2let transfer = new Transfer(id)3transfer.from = event.params.from4transfer.to = event.params.to5transfer.amount = event.params.amount6transfer.save()
События и данные о блоках/транзакциях
События Ethereum, передаваемые обработчикам событий, такие как событие Transfer
в предыдущих примерах, предоставляют доступ не только к параметрам события, но и к их материнской транзакции и блоку, частью которого они являются. Следующие данные могут быть получены из экземпляров event
(эти классы являются частью модуля ethereum
в graph-ts
):
1class Event {2 address: Address3 logIndex: BigInt4 transactionLogIndex: BigInt5 logType: string | null6 block: Block7 transaction: Transaction8 parameters: Array<EventParam>9 receipt: TransactionReceipt | null10}1112class Block {13 hash: Bytes14 parentHash: Bytes15 unclesHash: Bytes16 author: Address17 stateRoot: Bytes18 transactionsRoot: Bytes19 receiptsRoot: Bytes20 number: BigInt21 gasUsed: BigInt22 gasLimit: BigInt23 timestamp: BigInt24 difficulty: BigInt25 totalDifficulty: BigInt26 size: BigInt | null27 baseFeePerGas: BigInt | null28}2930class Transaction {31 hash: Bytes32 index: BigInt33 from: Address34 to: Address | null35 value: BigInt36 gasLimit: BigInt37 gasPrice: BigInt38 input: Bytes39 nonce: BigInt40}4142class TransactionReceipt {43 transactionHash: Bytes44 transactionIndex: BigInt45 blockHash: Bytes46 blockNumber: BigInt47 cumulativeGasUsed: BigInt48 gasUsed: BigInt49 contractAddress: Address50 logs: Array<Log>51 status: BigInt52 root: Bytes53 logsBloom: Bytes54}5556class Log {57 address: Address58 topics: Array<Bytes>59 data: Bytes60 blockHash: Bytes61 blockNumber: Bytes62 transactionHash: Bytes63 transactionIndex: BigInt64 logIndex: BigInt65 transactionLogIndex: BigInt66 logType: string67 removed: bool | null68}
Доступ к состоянию смарт-контракта
The code generated by graph codegen
also includes classes for the smart contracts used in the Subgraph. These can be used to access public state variables and call functions of the contract at the current block.
Распространенным шаблоном является доступ к контракту, из которого исходит событие. Это достигается с помощью следующего кода:
1// Импорт сгенерированного класса контракта и сгенерированного класса события Transfer2import { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'3// Импорт созданного класса объекта4import { Transfer } from '../generated/schema'56export function handleTransfer(event: TransferEvent) {7 // Привязка контракта к адресу, сгенерировавшему событие8 let contract = ERC20Contract.bind(event.address)910 // Доступ к переменным состояния и функциям путем их вызова11 пусть erc20Symbol = контракт.символ()12}
Transfer
связывается с TransferEvent
, чтобы избежать конфликта наименований с типом объекта
Пока ERC20Contract
в Ethereum имеет общедоступную функцию только для чтения, называемую symbol
, ее можно вызвать с помощью .symbol()
. Для общедоступных переменных состояния автоматически создается метод с таким же именем.
Any other contract that is part of the Subgraph can be imported from the generated code and can be bound to a valid address.
Обработка возвращенных вызовов
Если методы Вашего контракта, доступные только для чтения, могут вернуться к предыдущему состоянию, то Вам следует решить эту проблему, вызвав сгенерированный метод контракта с префиксом try_
.
- Например, контракт Gravity предоставляет метод
gravatarToOwner
. Этот код сможет выполнить возврат в этом методе:
1let gravity = Gravity.bind(event.address)2let callResult = gravity.try_gravatarToOwner(gravatar)3if (callResult.reverted) {4 log.info('getGravatar reverted', [])5} else {6 let owner = callResult.value7}
Примечание: Graph Node, подключенный к клиенту Geth или Infura, может обнаружить не все откаты. Если Вы полагаетесь на это, мы рекомендуем использовать Graph Node, подключенный к клиенту Parity.
Кодирование/декодирование ABI
Данные могут быть закодированы и декодированы в соответствии с форматом кодирования ABI Ethereum с использованием функций encode
и decode
в модуле ethereum
.
1import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'23let tupleArray: Array<ethereum.Value> = [4 ethereum.Value.fromAddress(Address.fromString('0x0000000000000000000000000000000000000420')),5 ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(62)),6]78let tuple = tupleArray as ethereum.Tuple910let encoded = ethereum.encode(ethereum.Value.fromTuple(tuple))!1112let decoded = ethereum.decode('(address,uint256)', encoded)
Для получения дополнительной информации:
- Спецификация ABI
- Кодирование/декодирование библиотека Rust/CLI
- Более [сложный пример] (https://github.com/graphprotocol/graph-node/blob/08da7cb46ddc8c09f448c5ea4b210c9021ea05ad/tests/integration-tests/host-exports/src/mapping.ts#L86).
Баланс адреса
Баланс нативного токена адреса можно получить с помощью модуля ethereum
. Эта функция доступна начиная с apiVersion: 0.0.9
, которая определена subgraph.yaml
. getBalance()
извлекает баланс указанного адреса на конец блока, в котором инициировано событие.
1import { ethereum } from '@graphprotocol/graph-ts'23let address = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')4let balance = ethereum.getBalance(address) // возвращает баланс в BigInt
Проверьте, является ли адрес контрактом или EOA
Чтобы проверить, является ли адрес адресом смарт-контракта или внешним адресом (EOA), используйте функцию hasCode()
из модуля ethereum
, которая вернет boolean
. Эта функция доступна начиная с apiVersion: 0.0.9
, которая определена subgraph.yaml
.
1import { ethereum } from '@graphprotocol/graph-ts'23let contractAddr = Address.fromString('0x2E645469f354BB4F5c8a05B3b30A929361cf77eC')4let isContract = ethereum.hasCode(contractAddr).inner // возвращает ложное значение56let eoa = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')7let isContract = ethereum.hasCode(eoa).inner // возвращает ложное значение
Регистрация API
1import { log } from '@graphprotocol/graph-ts'
The log
API allows Subgraphs to log information to the Graph Node standard output as well as Graph Explorer. Messages can be logged using different log levels. A basic format string syntax is provided to compose log messages from argument.
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
– logs a critical message and terminates the Subgraph.
API log
принимает строку формата и массив строковых значений. Затем он заменяет заполнители строковыми значениями из массива. Первый {}
заполнитель заменяется первым значением в массиве, второй {}
заполнитель заменяется вторым значением и так далее.
1log.info('Message to be displayed: {}, {}, {}', [value.toString(), anotherValue.toString(), 'already a string'])
Регистрация одного или нескольких значений
Регистрация одного значения
В приведенном ниже примере строковое значение “A” передается в массив, чтобы стать ['A']
перед тем как будет записано в лог:
1let myValue = 'A'23export function handleSomeEvent(event: SomeEvent): void {4 // Отображает: "My value is: A"5 log.info('My value is: {}', [myValue])6}
Регистрация одной записи из существующего массива
В приведенном ниже примере регистрируется только первое значение массива аргументов, несмотря на то, что массив содержит три значения.
1let myArray = ['A', 'B', 'C']23export function handleSomeEvent(event: SomeEvent): void {4 // Отображает : "My value is: A" (Несмотря на то, что в `log.info` передаются три значения)5 log.info('My value is: {}', myArray)6}
Регистрация нескольких записей из существующего массива
Для каждой записи в массиве arguments требуется свой собственный заполнитель {}
в строке сообщения лога. В приведенном ниже примере в сообщении лога содержатся три заполнителя {}
. По этой причине в myArray
регистрируются все три значения.
1let myArray = ['A', 'B', 'C']23export function handleSomeEvent(event: SomeEvent): void {4 // Отображает : "My first value is: A, second value is: B, third value is: C"5 log.info('My first value is: {}, second value is: {}, third value is: {}', myArray)6}
Регистрация конкретной записи из существующего массива
Чтобы отобразить определенное значение в массиве, необходимо указать индексированное значение.
1export function handleSomeEvent(event: SomeEvent): void {2 // Отображает : "My third value is C"3 log.info('My third value is: {}', [myArray[2]])4}
Регистрация информации о событии
В приведенном ниже примере регистрируется номер блока, хэш блока и хэш транзакции из события:
1import { log } from '@graphprotocol/graph-ts'23export function handleSomeEvent(event: SomeEvent): void {4 log.debug('Block number: {}, block hash: {}, transaction hash: {}', [5 event.block.number.toString(), // "47596000"6 event.block.hash.toHexString(), // "0x..."7 event.transaction.hash.toHexString(), // "0x..."8 ])9}
IPFS API
1import { ipfs } from '@graphprotocol/graph-ts'
Smart contracts occasionally anchor IPFS files onchain. This allows mappings to obtain the IPFS hashes from the contract and read the corresponding files from IPFS. The file data will be returned as Bytes
, which usually requires further processing, e.g. with the json
API documented later on this page.
При наличии хеша или пути IPFS чтение файла из IPFS выполняется следующим образом:
1// Поместите это в обработчик события в мэппинге2let hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'3let data = ipfs.cat(hash)45// Пути, подобные `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`,6// которые включают файлы в директориях, также поддерживаются7let path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'8let data = ipfs.cat(path)
Примечание: pfs.cat
на данный момент не является детерминированным. Если файл не может быть получен по сети IPFS до истечения времени ожидания запроса, он вернет null
. В связи с этим всегда стоит проверять результат на наличие null
.
С помощью ipfs.map
можно также обрабатывать файлы большего размера в потоковом режиме. Функция ожидает, что хэш или путь к файлу IPFS, имя обратного вызова и флаги изменят его поведение:
1import { JSONValue, Value } from '@graphprotocol/graph-ts'23export function processItem(value: JSONValue, userData: Value): void {4 // Смотрите документацию по JsonValue для получения подробной информации о работе5 // со значениями JSON6 let obj = value.toObject()7 let id = obj.get('id')8 let title = obj.get('title')910 if (!id || !title) {11 return12 }1314 // Обратные вызовы также могут создавать объекты15 let newItem = new Item(id)16 newItem.title = title.toString()17 newitem.parent = userData.toString() // Установите для родителя значение "parentId"18 newitem.save()19}2021// Поместите это внутри обработчика событий в мэппинге22ipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])2324// В качестве альтернативы, используйте `ipfs.mapJSON`25ipfs.mapJSON('Qm...', 'processItem', Value.fromString('parentId'))
Единственным поддерживаемым в настоящее время флагом является json
, который должен быть передан в ipfs.map
. С флагом json
файл IPFS должен состоять из серии значений JSON, по одному значению в строке. Вызов ipfs.map
прочитает каждую строку в файле, десериализует ее в JSONValue
и совершит обратный вызов для каждой из них. Затем обратный вызов может использовать операции с объектами для хранения данных из JSONValue
. Изменения объекта сохраняются только после успешного завершения обработчика, вызвавшего ipfs.map
; в то же время они хранятся в памяти, и поэтому размер файла, который может обработать ipfs.map
, ограничен.
On success, ipfs.map
returns void
. If any invocation of the callback causes an error, the handler that invoked ipfs.map
is aborted, and the Subgraph is marked as failed.
Crypto API
1import { crypto } from '@graphprotocol/graph-ts'
API crypto
делает криптографические функции доступными для использования в мэппингах. На данный момент есть только один:
crypto.keccak256(input: ByteArray): ByteArray
JSON API
1import { json, JSONValueKind } from '@graphprotocol/graph-ts'
Данные JSON могут быть разобраны с помощью json
API:
json.fromBytes(data: Bytes): JSONValue
– выполняет разбор данных JSON из массиваBytes
, интерпретируемого как допустимая последовательность UTF-8json.try_fromBytes(data: Bytes): Result<JSONValue, boolean>
– безопасная версияjson.fromBytes
, возвращает вариант ошибки, если выполнение разбора не удалосьjson.fromString(data: string): JSONValue
– выполняет разбор данных JSON из допустимой UTF-8String
json.try_fromString(data: string): Result<JSONValue, boolean>
– безопасная версияjson.fromString
, возвращает вариант ошибки, если выполнение разбора не удалось
Класс JSONValue
предоставляет способ извлечения значений из произвольного документа JSON. Поскольку значениями JSON могут быть логические значения, числа, массивы и многое другое, JSONValue
поставляется со свойством kind
для проверки типа значения:
1let value = json.fromBytes(...)2if (value.kind == JSONValueKind.BOOL) {3 ...4}
Кроме того, существует способ проверить, является ли значение 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 методов, описанных выше)
Справка по преобразованию типов
Источник(и) | Место назначения | Функция преобразования |
---|---|---|
Address | Bytes | отсутствует |
Address | String | s.toHexString() |
BigDecimal | String | s.toString() |
BigInt | BigDecimal | s.toBigDecimal() |
BigInt | String (hexadecimal) | s.toHexString() или s.toHex() |
BigInt | String (unicode) | s.toString() |
BigInt | i32 | s.toI32() |
Boolean | Boolean | отсутствует |
Bytes (signed) | BigInt | BigInt.fromSignedBytes(s) |
Bytes (unsigned) | BigInt | BigInt.fromUnsignedBytes(s) |
Bytes | String (hexadecimal) | s.toHexString() или s.toHex() |
Bytes | String (unicode) | s.toString() |
Bytes | String (base58) | s.toBase58() |
Bytes | i32 | s.toI32() |
Bytes | u32 | s.toU32() |
Bytes | JSON | json.fromBytes(s) |
int8 | i32 | отсутствует |
int32 | i32 | отсутствует |
int32 | BigInt | BigInt.fromI32(s) |
uint24 | i32 | отсутствует |
int64 - int256 | BigInt | отсутствует |
uint32 - uint256 | BigInt | отсутствует |
JSON | boolean | s.toBool() |
JSON | i64 | s.toU64() |
JSON | u64 | s.toU64() |
JSON | f64 | s.toF64() |
JSON | BigInt | s.toBigInt() |
JSON | string | s.toString() |
JSON | Array | s.toArray() |
JSON | Object | s.toObject() |
String | Address | Address.fromString(s) |
Bytes | Address | Address.fromBytes(s) |
String | BigInt | BigInt.fromString(s) |
String | BigDecimal | BigDecimal.fromString(s) |
String (hexadecimal) | Bytes | ByteArray.fromHexString(s) |
String (UTF-8) | Bytes | ByteArray.fromUTF8(s) |
Метаданные источника данных
Вы можете проверить адрес контракта, сеть и контекст источника данных, который вызвал обработчик, через пространство имен 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 в манифесте
The context
section within dataSources
allows you to define key-value pairs that are accessible within your Subgraph mappings. The available types are Bool
, String
, Int
, Int8
, BigDecimal
, Bytes
, List
, and BigInt
.
Ниже приведен пример YAML, иллюстрирующий использование различных типов в разделе context
:
1dataSources:2 - kind: ethereum/contract3 name: ContractName4 network: mainnet5 context:6 bool_example:7 type: Bool8 data: true9 string_example:10 type: String11 data: 'hello'12 int_example:13 type: Int14 data: 4215 int8_example:16 type: Int817 data: 12718 big_decimal_example:19 type: BigDecimal20 data: '10.99'21 bytes_example:22 type: Bytes23 data: '0x68656c6c6f'24 list_example:25 type: List26 data:27 - type: Int28 data: 129 - type: Int30 data: 231 - type: Int32 data: 333 big_int_example:34 type: BigInt35 data: '1000000000000000000000000'
Bool
: Определяет логическое значение (true
илиfalse
).String
: Определяет строковое значение.Int
: Определяет 32-разрядное целое число.Int8
: Определяет 8-разрядное целое число.BigDecimal
: Определяет десятичное число. Необходимо заключить в кавычки.Bytes
: Определяет шестнадцатеричную строку.List
: Определяет список элементов. Для каждого элемента необходимо указать его тип и данные.BigInt
: Определяет большое целочисленное значение. Необходимо заключить в кавычки из-за большого размера.
This context is then accessible in your Subgraph mapping files, enabling more dynamic and configurable Subgraphs.