グラフ 4 データ ソース テンプレート

Ethereum スマート コントラクトの一般的なパターンは、レジストリ コントラクトまたはファクトリー コントラクトを使用することです。この場合、1 つのコントラクトが、それぞれ独自の状態とイベントを持つ他のコントラクトを任意の数だけ作成、管理、または参照します。これらの下位契約のアドレスは、事前にわかっている場合もあれば、わかっていない場合もあり、これらの契約の多くは、時間の経過とともに作成または追加される可能性があります。そのため、この場合、単一のデータ ソースや固定数のデータ ソースを定義することはできず、より動的なアプローチ、つまりデータ ソース テンプレートが必要になります。

スマートコントラクト

以前の GravatarRegistry コントラクトは手動でデプロイされましたが、今回はこれを少し変更して、ファクトリ コントラクトを通じて GravatarRegistry コントラクトを動的に作成します。

まず工場契約:

// SPDX-License-Identifier: MIT
pragma solidity >0.5.16;

import './GravatarRegistry.sol';
import './IGravatarRegistry.sol';


contract GravatarRegistryFactory {

    mapping(string => address) public getRegistry;
    string[] public allRegistries;

    event RegistryCreated(string gravatarGroup, address indexed registry,uint);


    function allRegistriesLength() external view returns (uint) {
        return allRegistries.length;
    }

    function createGravatarRegistry(string calldata _gravatarGroup) external returns (address registry) {
        require(getRegistry[_gravatarGroup] == address(0), 'GravatarRegistry: ALREADY_EXISTS');
        bytes memory bytecode = type(GravatarRegistry).creationCode;
        bytes32 salt = keccak256(abi.encodePacked(_gravatarGroup));
        assembly {
            registry := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }
        IGravatarRegistry(registry).initialize(_gravatarGroup);
        getRegistry[_gravatarGroup] = registry;
        allRegistries.push(_gravatarGroup);
        emit RegistryCreated(_gravatarGroup, registry, allRegistries.length);
    }
}

インターフェースを追加します。

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

interface IGravatarRegistry {
    event NewGravatar(uint id, address owner, string displayName, string imageUrl);
    event UpdatedGravatar(uint id, address owner, string displayName, string imageUrl);

    function createGravatar(string calldata _displayName, string calldata _imageUrl) external;

    function getGravatar(address owner) external view returns (string memory, string memory);
    function updateGravatarName(string calldata _displayName) external;

    function updateGravatarImage(string calldata _imageUrl) external;

    function setMythicalGravatar() external ;

    function initialize(string calldata _groupName) external ;
}

GravatarRegistry コントラクトは、元のコントラクトに groupName 変数を追加します。

// SPDX-License-Identifier: MIT
pragma solidity >0.4.0;

contract GravatarRegistry {
  event NewGravatar(uint id, address owner, string displayName, string imageUrl);
  event UpdatedGravatar(uint id, address owner, string displayName, string imageUrl);

  struct Gravatar {
    address owner;
    string displayName;
    string imageUrl;
  }

  address public factory;
  string public groupName;


  Gravatar[] public gravatars;

  mapping (uint => address) public gravatarToOwner;
  mapping (address => uint) public ownerToGravatar;

  
  constructor() public {
      factory = msg.sender;
  }

  function createGravatar(string calldata _displayName, string calldata _imageUrl) public {
    require(ownerToGravatar[msg.sender] == 0);
    gravatars.push(Gravatar(msg.sender, _displayName, _imageUrl));
    uint id = gravatars.length - 1;

    gravatarToOwner[id] = msg.sender;
    ownerToGravatar[msg.sender] = id;

    emit NewGravatar(id, msg.sender, _displayName, _imageUrl);
  }

  function getGravatar(address owner) public view returns (string memory, string memory) {
    uint id = ownerToGravatar[owner];
    return (gravatars[id].displayName, gravatars[id].imageUrl);
  }

  function updateGravatarName(string calldata _displayName) public {
    require(ownerToGravatar[msg.sender] != 0);
    require(msg.sender == gravatars[ownerToGravatar[msg.sender]].owner);

    uint id = ownerToGravatar[msg.sender];

    gravatars[id].displayName = _displayName;
    emit UpdatedGravatar(id, msg.sender, _displayName, gravatars[id].imageUrl);
  }

  function updateGravatarImage(string calldata _imageUrl) public {
    require(ownerToGravatar[msg.sender] != 0);
    require(msg.sender == gravatars[ownerToGravatar[msg.sender]].owner);

    uint id = ownerToGravatar[msg.sender];

    gravatars[id].imageUrl =  _imageUrl;
    emit UpdatedGravatar(id, msg.sender, gravatars[id].displayName, _imageUrl);
  }

  // the gravatar at position 0 of gravatars[]
  // is fake
  // it's a mythical gravatar
  // that doesn't really exist
  // dani will invoke this function once when this contract is deployed
  // but then no more
  function setMythicalGravatar() public {
    require(msg.sender == 0xBA8B604410ca76AF86BDA9B00Eb53B65AC4c41AC);
    gravatars.push(Gravatar(address(0x0), " ", " "));
  }

  // called once by the factory at time of deployment
  function initialize(string calldata _groupName) external {
      require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
      groupName = _groupName;
  }
}

ファクトリ コントラクトをデプロイし、後で動的にデプロイするために GravatarRegistry を残しておきます。

サブグラフ.yaml

テンプレート設定を追加します。その設定項目は基本的に dataSource と同様です。アドレスと startBlock はソースではサポートされていません

specVersion: 0.0.5
schema:
  file: ./schema.graphql
features:
  - fullTextSearch
dataSources:
  - kind: ethereum/contract
    name: Factory
    network: goerli
    source:
      address: '0x580b57db55a0636B53Cb09BbA4aE8CbB765D72bA'
      abi: Factory
      startBlock: 8403815
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.7
      language: wasm/assemblyscript
      file: ./src/factory.ts
      entities:
        - Registry
      abis:
        - name: Factory
          file: ./abis/Factory.json
      eventHandlers:
        - event: RegistryCreated(string,indexed address,uint256)
          handler: handleNewRegistry
templates:
  - kind: ethereum
    name: GravatarRegistry
    network: goerli
    source:
      abi: GravatarRegistry
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.7
      language: wasm/assemblyscript
      entities:
        - Gravatar
        - Transaction
        - Block
      abis:
        - name: GravatarRegistry
          file: ./abis/GravatarRegistry.json
      eventHandlers:
        - event: NewGravatar(string,uint256,address,string,string)
          handler: handleNewGravatar
        - event: UpdatedGravatar(string,uint256,address,string,string)
          handler: handleUpdatedGravatar
      callHandlers:
        - function: createGravatar(string,string)
          handler: handleCreateGravatar
      blockHandlers:
        - handler: handleBlockWithCallToContract
          filter:
            kind: call
      file: ./src/gravatar-registry.ts

スキーマ.graphql

type _Schema_
  @fulltext(
    name: "gravatarSearch"
    language: en
    algorithm: rank
    include: [{ entity: "Gravatar", fields: [{ name: "displayName" }, { name: "description" }] }]
  )


type Gravatar @entity(immutable: false) {
  id: String!
  owner: Bytes! # address
  groupName: String! # string
  displayName: String! # string
  description: String! # string
  imageUrl: String! # string
  transaction: Transaction
}


type Transaction @entity(immutable: true) {
  id: Bytes!
  block: Block
  gasPrice: BigInt! # bigInt
}

type Block @entity(immutable: true) {
  id: Bytes!
  blockNumber: BigInt!
  blockTimestamp: BigInt!
  transactions: [Transaction!] @derivedFrom(field: "block")
}

type Registry @entity(immutable: false) {
  id: String!
  address: Bytes! # address
  groupName: String! # string
}

ファクトリー.ts

import {
  RegistryCreated as RegistryCreatedEvent,
} from "../generated/Factory/Factory"
import { Registry} from "../generated/schema"
import { GravatarRegistry } from "../generated/templates"
export function handleNewRegistry(event: RegistryCreatedEvent): void {
  let registry = new Registry(event.params.param2.toString());
  registry.address = event.params.registry;
  registry.groupName = event.params.gravatarGroup
  registry.save()
  GravatarRegistry.create(event.params.registry);
}

これは主に工場契約を監視するために使用され、重要なのは、監視対象の契約データ ソースを動的に作成するコードの最後の行です。

gravatar-registry.ts

ここでも、主に groupName フィールドを追加するために、いくつかの変更を行う必要があります。

import {
  CreateGravatarCall,
  NewGravatar as NewGravatarEvent,
  UpdatedGravatar as UpdatedGravatarEvent
} from "../generated/templates/GravatarRegistry/GravatarRegistry"
import { Gravatar,Transaction,Block} from "../generated/schema"
import { ethereum } from '@graphprotocol/graph-ts'

export function handleNewGravatar(event: NewGravatarEvent): void {
  let gravatar = new Gravatar(event.params.id.toString()+event.params.groupName);
  gravatar.owner = event.params.owner
  gravatar.groupName = event.params.groupName
  gravatar.displayName = event.params.displayName
  gravatar.imageUrl = event.params.imageUrl
  gravatar.description = event.params.imageUrl.replaceAll("/"," ");
  gravatar.transaction = event.transaction.hash
  gravatar.save()
}

export function handleUpdatedGravatar(event: UpdatedGravatarEvent): void {
  let id = event.params.id.toString();
  let gravatar = Gravatar.load(id+event.params.groupName)
  if (gravatar == null) {
    gravatar = new Gravatar(id+event.params.groupName)
  }
  gravatar.groupName = event.params.groupName
  gravatar.owner = event.params.owner
  gravatar.displayName = event.params.displayName
  gravatar.description = event.params.imageUrl.replaceAll("/"," ");
  gravatar.imageUrl = event.params.imageUrl
  gravatar.transaction = event.transaction.hash
  gravatar.save()
}

リリースを再コンパイルします:

 graph codegen
 graph build
 yarn deploy

確認

最後に、ファクトリ コントラクトの createGravatarRegistry メソッドを呼び出します。ここでは、test1、test2、test3、test4 がそれぞれ渡され、4 つの GravatarRegistry コントラクトが作成されます。その groupName はそれぞれ test1、test2、test3、test4 です。

実際、ファクトリによって動的に作成されたこの種のスマート コントラクトには、groupName ジャンプを通じてアクセスされるルーティング コントラクトも必要です。ここでは、例の複雑さは簡略化されており、GravatarRegistry コントラクトの関数は、によって返されたアドレスを通じて手動で直接呼び出されます。工場契約。

4 つのコントラクトの createGravatar メソッドをそれぞれ呼び出して、クエリ検証を行います。

サンプルコードは次のとおりです。

https://github.com/ziyiyu/subgraph-example

おすすめ

転載: blog.csdn.net/gambool/article/details/128797639
おすすめ