The Graph 4 Data Source Templates

以太坊智能合约的常见模式是使用注册表或工厂合约,其中一个合约创建、管理或引用任意数量的其他合约,每个合约都有自己的状态和事件。这些下级合约的地址可能事先就知道,也可能不知道,其中许多合约可能会随着时间的推移而创建或添加。这就是为什么在这种情况下,不可能定义单个数据源或固定数量的数据源,而需要一种更动态的方法:数据源模板。

智能合约

之前的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留到后面动态部署。

subgraph.yaml

添加templates配置,其配置项基本上和dataSources差不多,source中不支持address和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

schema.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
}

扫描二维码关注公众号,回复: 15111132 查看本文章

factory.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,创建了四个GravatarRegistry合约。其groupName分别为test1,test2,test3,test4。

实际上这种由工厂动态创建的智能合约,应该还有一个路由合约通过groupName跳转访问,这里简化示例的复杂性直接通过工厂合约返回的地址手动调用GravatarRegistry合约的函数。

分别调用了四个合约的createGravatar方法,查询验证:

示例代码如下:

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

猜你喜欢

转载自blog.csdn.net/gambool/article/details/128797639