28 分钟
汇编脚本API
注意:如果您在 graph-cli
/graph-ts
版本 0.22.0
之前创建了子图,那么您正在使用较旧版本的 AssemblyScript,我们建议查看迁移指南
。
此页面记录了编写子图映射时可以使用的内置 API。有两种开箱即用的 API:
- Graph TypeScript库 (
graph-ts
) - 通过
graph codegen
生成的子图文件中的代码
您还可以添加其他库作为依赖项,只要它们与AssemblyScript兼容。
由于语言映射是用AssemblyScript编写的,因此从AssemblyScript wiki查看语言和标准库功能非常有用。
API 参考
@graphprotocol/graph-ts 库提供以下 API:
- 用于处理以太坊智能合约、事件、区块、交易和以太坊价值的以太坊 API。
- 用于与图形节点交互,存储和加载实体的 存储 API。
- 用于将消息记录Graph Node输出和Graph Explorer的
log
API。 - 用于从 IPFS 加载文件的ipfs API。
- 用于解析 JSON 数据的json API。
- 使用加密功能的crypto API。
- 用于在不同类型系统(例如 Ethereum、JSON、GraphQL 和 AssemblyScript)之间进行转换的低级原语。
版本
子图清单中的 apiVersion
指定了由 Graph Node 运行的特定子图的映射 API 版本。
版本 | Release 说明 |
---|---|
0.0.9 | 添加新的主机函数eth_get_balance 和 hasCode |
0.0.8 | 在保存实体时添加对模式中是否存在字段的验证。 |
0.0.7 | 添加了 TransactionReceipt 和 Log 类到以太坊类型。已将 receipt 字段添加到Ethereum Event对象。 |
0.0.6 | 向Ethereum Transaction对象添加了 nonce 字段 向 Etherum Block对象添加 baseFeePerGas字段 |
0.0.5 | AssemblyScript 升级到版本 0.19.10(这包括重大更改,参阅迁移指南 )ethereum.transaction.gasUsed 重命名为 ethereum.transaction.gasLimit` |
0.0.4 | 已向 Ethereum SmartContractCall对象添加了 functionSignature 字段。 |
0.0.3 | 已向Ethereum Call 对象添加了 from 字段。ethereum.call.address 被重命名为 ethereum.call.to 。 |
0.0.2 | 已向Ethereum Transaction对象添加了 input 字段。 |
内置类型
关于内置于 AssemblyScript 中的基本类型的文档可以在AssemblyScript wiki中找到。
以下额外的类型由 @graphprotocol/graph-ts
提供。
字节数组
1从'@graphprotocol/graph-ts'导入{ ByteArray }
ByteArray
表示一个 u8
类型的数组。
结构
fromI32(x: i32): ByteArray
- 将x
分解为字节。fromHexString(hex: string): ByteArray
- 输入长度必须是偶数。在十六进制字符串中添加0x
前缀是可选的。
类型转换
toHexString(): string
- 转换为以0x
前缀的十六进制字符串。toString(): string
- 将字节解释为 UTF-8 字符串。toBase58(): string
- 将字节编码为 base58 字符串。toU32(): u32
- 将字节解释为小端u32
。在溢出的情况下会抛出异常。toI32(): i32
- 将字节数组解释为小端i32
。在溢出的情况下会抛出异常。
操作符
equals(y: ByteArray): bool
– 可以写成x == y
。concat(other: ByteArray) : ByteArray
- 返回一个新的ByteArray
,由this
直接跟随other
构成。concatI32(other: i32) : ByteArray
- 返回一个新的ByteArray
,由this
直接跟随other
的字节表示构成。
BigDecimal
1从“@Graphprotocol/graph-ts”导入{ BigDecimal }
BigDecimal 用于表示任意精度的小数。
注意:内部BigDecimal
以IEEE-754 decimal128浮点格式存储,支持34位十进制有效位。这使得BigDecimal
不适合表示跨度超过34位的定点类型,例如Solidity ufixed256x18
或等效类型。
结构
constructor(bigInt: BigInt)
– 从一个BigInt
创建一个BigDecimal
。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
1从'@graphprotocol/graph-ts'导入{ BigInt }
BigInt
用于表示大整数。这包括以太坊中的类型 uint32
到 uint256
和 int64
到 int256
的数值。所有小于 uint32
的数值,比如 int32
、uint24
或 int8
都被表示为 i32
。
BigInt 类具有以下 API:
结构
-
BigInt.fromI32(x: i32): BigInt
– 从 i32 创建一个 BigInt。 -
BigInt.fromString(s: string): BigInt
– 从字符串解析一个 BigInt。 -
BigInt.fromUnsignedBytes(x: Bytes): BigInt
– 将 bytes 解释为无符号的小端整数。如果你的输入是大端的,请先调用.reverse()。 -
BigInt.fromSignedBytes(x: Bytes): BigInt
– 将 bytes 解释为有符号的小端整数。如果你的输入是大端的,请先调用.reverse()。类型转换
-
x.toHex(): string
– 将 BigInt 转换为十六进制字符的字符串。 -
x.toString(): string
– 将 BigInt 转换为十进制数字字符串。 -
x.toI32(): i32
– 将 BigInt 作为 i32 返回;如果值不适合 i32,则失败。最好先检查 x.isI32()。 -
x.toBigDecimal(): 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
.
类型化映射
1从'@graphprotocol/graph-ts'导入{ TypedMap }
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
。map.get(key: K): V | null
– 返回键为key
的值,如果映射中不存在该键,则返回null
。map.isSet(key: K): bool
– 如果映射中存在key
则返回true
,否则返回false
。
字节
1从 '@graphprotocol/graph-ts'导入{ Bytes }
字节用于表示任意长度的字节数组,包括字节类型的以太坊值、 bytes32等。
Bytes
类扩展了 AssemblyScript 的 Uint8Array,支持所有 Uint8Array
的功能,以及以下新方法:
结构
fromHexString(hex: string): Bytes
- 将字符串hex
转换为一个ByteArray
。字符串hex
必须由偶数个十六进制数字组成,并且可选地以0x
开头。fromI32(i: i32): Bytes
- 将i
转换为一个字节数组。
类型转换
b.toHex()
– 返回表示数组中字节的十六进制字符串。b.toString()
– 将数组中的字节转换为 Unicode 字符串。b.toBase58()
– 将以太坊的字节数值转换为 base58 编码(用于 IPFS 哈希)。
操作符
b.concat(other: Bytes): Bytes
- 返回一个新的Bytes
,由this
直接跟随other
组成。b.concatI32(other: i32): ByteArray
- 返回一个新的Bytes
,由this
直接跟随other
的字节表示组成。
地址
1从 '@graphprotocol/graph-ts'导入{ Address }
Address
扩展了 Bytes
,用于表示以太坊的 address
值。
除了 Bytes
API 之外,它还添加了以下方法:
Address.fromString(s: string): Address
– 从十六进制字符串创建一个Address
。Address.fromBytes(b: Bytes): Address
– 从b
创建一个Address
,b
必须恰好为 20 个字节长。传入长度不足或超过 20 字节的值将导致错误。
商店API
1从 '@graphprotocol/graph-ts'导入 { store }
store
API 允许从和到 Graph Node 存储加载、保存和删除实体。
写入存储的实体与子图的 GraphQL 模式中定义的 @entity
类型一一对应。为了方便处理这些实体,由 Graph CLI 提供的 graph codegen
命令会生成实体类,这些类是内置 Entity
类的子类,具有模式中字段的属性 getter 和 setter,以及加载和保存这些实体的方法。
创建实体
以下是从以太坊事件创建实体的常见模式。
1/ Import the Transfer event class generated from the ERC20 ABI2import { Transfer as TransferEvent } from '../generated/ERC20/ERC20'34// Import the Transfer entity type generated from the GraphQL schema5import { Transfer } from '../generated/schema'67// Transfer event handler8export function handleTransfer(event: TransferEvent): void {9 // Create a Transfer entity, using the transaction hash as the entity ID10 let id = event.transaction.hash11 let transfer = new Transfer(id)1213 // Set properties on the entity, using the event parameters14 transfer.from = event.params.from15 transfer.to = event.params.to16 transfer.amount = event.params.amount1718 // Save the entity to the store19 transfer.save()20}
如果在处理链时遇到 Transfer 事件,它会使用生成的 Transfer 类型(别名为 TransferEvent 以避免与实体类型的命名冲突) 传递给 handleTransfer 事件处理器。 此类型允许访问事件的母交易及其参数等数据。
每个实体都必须有一个唯一的ID,以避免与其他实体发生冲突。事件参数中包含可使用的唯一标识符是相当常见的。
注意:使用交易hash作为ID假设同一交易中没有其他事件创建具有此hash作为ID的实体。
从存储中加载实体
如果实体已经存在,则可以使用以下内容从存储中加载它:
1let id = event.transaction.hash // or however the ID is constructed2let transfer = Transfer.load(id)3if (transfer == null) {4 transfer = new Transfer(id)5}67// Use the Transfer entity as before
由于实体可能尚未存在于存储中,因此 load
方法返回一个类型为 Transfer | null
的值。因此,在使用该值之前可能需要检查 null
情况。
注意: 仅当映射中所做的更改依赖于实体的先前数据时,才需要加载实体。 有关更新现有实体的两种方法,请参阅下一节。
查找在区块中创建的实体
截至 graph-node
v0.31.0、@graphprotocol/graph-ts
v0.30.0 和 @graphprotocol/graph-cli
v0.49.0,所有实体类型上都提供了 loadInBlock
方法。
存储API便于检索在当前块中创建或更新的实体。一个典型的情况是,一个处理程序从某个链上事件创建一个交易,如果该交易存在,则稍后的处理程序希望访问该交易。
- 在交易不存在的情况下,子图将不得不访问数据库,以发现实体不存在。如果子图作者已经知道实体必须是在同一块中创建的,那么使用
loadInBlock
可以避免这种数据库往返。 - 对于某些子图,这些遗漏的查找可能会大大增加索引时间。
1let id = event.transaction.hash // or however the ID is constructed2let transfer = Transfer.loadInBlock(id)3if (transfer == null) {4 transfer = new Transfer(id)5}67// Use the Transfer entity as before
注意: 如果在给定的区块中没有创建实体,则 loadInBlock
将返回 null
,即使存储中存在具有给定 ID 的实体也是如此。
查找派生实体
在 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}
以下代码将加载Holder实体来源的Token实体:
1let holder = Holder.load('test-id')2// 加载与给定持有人相关联的 Token 实体。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// This won't work2entity.numbers.push(BigInt.fromI32(1))3entity.save()45// This will work6let numbers = entity.numbers7numbers.push(BigInt.fromI32(1))8entity.numbers = numbers9entity.save()
从存储中删除实体
目前无法通过生成的类型来删除实体。相反,要删除实体,需要将实体类型的名称和实体ID传递给 store.remove
方法:
1import { store } from '@graphprotocol/graph-ts'2...3let id = event.transaction.hash4store.remove('Transfer', id)
以太坊 API
以太坊 API 提供对智能合约、公共状态变量、合约函数、事件、交易、区块和编码/解码以太坊数据的访问。
对以太坊类型的支持
与实体一样,graph codegen
为子图中使用的所有智能合约和事件生成类。为此,合约 ABI 需要作为子图清单中数据源的一部分。通常,ABI 文件存储在一个名为 abis/
的文件夹中。
通过生成的类,以太坊类型和内置类型 之间的转换在幕后进行,这样子图作者就不必担心它们。
以下示例说明了这一点。 给定一个子图模式,如
1type Transfer @entity {2 from: Bytes!3 to: Bytes!4 amount: BigInt!5}
对于以太坊上的 Transfer(address,address,uint256)
事件签名,类型为 address
、address
和 uint256
的 from
、to
和 amount
值被转换为 Address
和 BigInt
,从而允许它们传递给 Transfer
实体的 Bytes!
和 BigInt!
属性:
1let id = event.transaction.hash.toHex()2let transfer = new Transfer(id)3transfer.from = event.params.from4transfer.to = event.params.to5transfer.amount = event.params.amount6transfer.save()
事件和区块/交易数据
传递给事件处理程序的以太坊事件(例如前面示例中的 Transfer
事件)不仅提供对事件参数的访问,还提供对其父事务和它们所在的区块的访问。以下数据可以从 event
实例中获取(这些类是 graph-ts
中的 ethereum
模块的一部分):
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}
访问智能合约状态
由 graph codegen
生成的代码还包括用于子图中使用的智能合约的类。这些类可以用于访问智能合约在当前区块上的公共状态变量和调用函数。
一种常见的模式是访问事件起源的合约。 这是通过以下代码实现的:
1// Import the generated contract class and generated Transfer event class2import { ERC20Contract, Transfer as TransferEvent } from '../generated/ERC20Contract/ERC20Contract'3// Import the generated entity class4import { Transfer } from '../generated/schema'56export function handleTransfer(event: TransferEvent) {7 // Bind the contract to the address that emitted the event8 let contract = ERC20Contract.bind(event.address)910 // Access state variables and functions by calling them11 let erc20Symbol = contract.symbol()12}
这里将 Transfer
别名为 TransferEvent
,以避免与实体类型产生命名冲突。
只要以太坊上的 ERC20Contract
有一个名为 symbol
的公共只读函数,就可以使用 .symbol()
来调用它。对于公共状态变量,将自动生成一个同名的方法。
作为子图一部分的任何其他合约都可以从生成的代码中导入,并且可以绑定到一个有效地址。
处理重复调用
如果你的合约的只读方法可能会恢复,那么你应该通过调用以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}
请注意,连接到 Geth 或 Infura 客户端的 Graph 节点可能无法检测到所有重复使用,如果您依赖于此,我们建议使用连接到 Parity 客户端的 Graph 节点。
编码/解码 ABI
数据可以按照以太坊的 ABI 编码格式使用 ethereum
模块中的 encode
和 decode
函数进行编码和解码。
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 Ethereum ABI 库/CLI来进行编码和解码。
- 更复杂的示例可以在这里找到。
地址余额
可以使用以太坊
模块检索地址的本地代币余额。此功能可从apiVersion:0.0.9
中获得,其定义为subgraph.yaml
。getBalance()
检索指定地址在触发事件的块结束时的余额。
1import { ethereum } from '@graphprotocol/graph-ts'23let address = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')4let balance = ethereum.getBalance(address) // returns balance in BigInt
检查地址是合约还是EOA
要检查地址是智能合约地址还是外部所有地址(EOA),请使用以太坊
模块中的hasCode()
函数,该函数将返回boolean
。此功能可从apiVersion:0.0.9
中获得,该版本定义为subgraph.yaml
。
1import { ethereum } from '@graphprotocol/graph-ts'23let contractAddr = Address.fromString('0x2E645469f354BB4F5c8a05B3b30A929361cf77eC')4let isContract = ethereum.hasCode(contractAddr).inner // returns true56let eoa = Address.fromString('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')7let isContract = ethereum.hasCode(eoa).inner // returns false
日志记录 API
1从 '@graphprotocol/graph-ts'导入 { log }
log
API 允许子图将信息记录到 Graph Node 的标准输出以及 Graph Explorer。可以使用不同的日志级别记录消息。提供了一种基本的格式字符串语法来从参数组成日志消息。
log
API 包括以下函数:
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
– 记录关键消息并终止子图。
log
API 接受一个格式字符串和一个字符串值数组。然后,它用数组中的字符串值替换占位符。第一个 {}
占位符会被数组中的第一个值替换,第二个 {}
占位符会被第二个值替换,依此类推。
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 // Displays : "My value is: A"5 log.info('My value is: {}', [myValue])6}
从现有数组记录单个条目
在下面的示例中,尽管参数数组包含三个值,只有该数组的第一个值被记录了。
1let myArray = ['A', 'B', 'C']23export function handleSomeEvent(event: SomeEvent): void {4 // Displays : "My value is: A" (Even though three values are passed to `log.info`)5 log.info('My value is: {}', myArray)6}
从现有数组记录多个条目
在日志消息字符串中,参数数组中的每个条目都需要自己的占位符 。下面的示例包含了日志消息中的三个占位符 。因此,myArray 中的所有三个值都被记录。
1let myArray = ['A', 'B', 'C']23export function handleSomeEvent(event: SomeEvent): void {4 // Displays : "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 // Displays : "My third value is C"3 log.info('My third value is: {}', [myArray[2]])4}
记录事件信息
下面的例子记录了一个事件的区块号、区块hash和交易hash:
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
1从 '@graphprotocol/graph-ts'导入{ ipfs }
智能合约偶尔会在链上固定 IPFS 文件。这允许映射从合约获取 IPFS 哈希并从 IPFS 读取相应的文件。文件数据将作为 Bytes
返回,通常需要进一步处理,例如使用稍后在本页面中记录的 json
API。
给定一个 IPFS hash或路径,从 IPFS 读取文件的过程如下:
1// Put this inside an event handler in the mapping2let hash = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D'3let data = ipfs.cat(hash)45// Paths like `QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile`6// that include files in directories are also supported7let path = 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D/Makefile'8let data = ipfs.cat(path)
注意: 目前 ipfs.cat
不是确定性的。如果在请求超时之前无法从 IPFS 网络检索到文件,则会返回 null
。因此,始终值得检查结果是否为 null
。
还可以使用 ipfs.map 以流式处理的方式处理较大的文件。该函数需要 IPFS 文件的哈希或路径、回调函数的名称以及修改其行为的标志:
1import { JSONValue, Value } from '@graphprotocol/graph-ts'23export function processItem(value: JSONValue, userData: Value): void {4 // See the JSONValue documentation for details on dealing5 // with JSON values6 let obj = value.toObject()7 let id = obj.get('id')8 let title = obj.get('title')910 if (!id || !title) {11 return12 }1314 // Callbacks can also created entities15 let newItem = new Item(id)16 newItem.title = title.toString()17 newitem.parent = userData.toString() // Set parent to "parentId"18 newitem.save()19}2021// Put this inside an event handler in the mapping22ipfs.map('Qm...', 'processItem', Value.fromString('parentId'), ['json'])2324// Alternatively, use `ipfs.mapJSON`25ipfs.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
的处理程序将被中止,并且子图被标记为失败。
加密API
1从'@graphprotocol/graph-ts'导入{ crypto }
crypto
API 为映射中的使用提供了加密函数。目前,只有一个函数可用:
crypto.keccak256(input: ByteArray): ByteArray
JSON API
1从'@graphprotocol/graph-ts'导入{ json, JSONValueKind }
JSON 数据可以使用 json API 进行解析:
json.fromBytes(data: Bytes): JSONValue
– 从有效的 UTF-8 序列的Bytes
数组中解析 JSON 数据json.try_fromBytes(data: Bytes): Result<JSONValue, boolean>
–json.fromBytes
的安全版本,如果解析失败,则返回错误变体json.fromString(data: string): JSONValue
– 从有效的 UTF-8String
解析 JSON 数据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>
- (然后使用上述 5 种方法之一将JSONValue
转换)
类型转换参考
源类型 | 目标类型 | 转换函数 |
---|---|---|
Address | Bytes | none |
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 | none |
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 | none |
int32 | i32 | none |
int32 | BigInt | BigInt.fromI32(s) |
uint24 | i32 | none |
int64 - int256 | BigInt | none |
uint32 - uint256 | BigInt | none |
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
Entity 和 DataSourceContext
基础的 Entity
类和子类 DataSourceContext
类都有助于动态设置和获取字段的帮助函数:
setString(key: string, value: string): void
setI32(key: string, value: i32): void
setI32(key: string, value: i32): 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
dataSources
内的 context
部分允许您定义在子图映射中可访问的键值对。可用的类型包括 Bool
、String
、Int
、Int8
、BigDecimal
、Bytes
、List
和 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
:指定一个大整数值。由于其较大,必须用引号括起来。
该上下文可以在您的子图映射文件中访问,从而实现更加动态和可配置的子图。