Un patrón común para los contratos inteligentes de Ethereum es usar un contrato de registro o de fábrica, donde un contrato crea, administra o hace referencia a cualquier número de otros contratos, cada uno con su propio estado y eventos. Las direcciones de estos contratos subordinados pueden o no conocerse de antemano, y muchos de estos contratos pueden crearse o agregarse con el tiempo. Por eso, en este caso, no es posible definir una sola fuente de datos o un número fijo de fuentes de datos, y se requiere un enfoque más dinámico: plantillas de fuentes de datos.
contrato inteligente
El contrato de GravatarRegistry anterior se implementó manualmente, ahora lo modificamos ligeramente para crear dinámicamente el contrato de GravatarRegistry a través del contrato de fábrica.
Primero el contrato de fábrica:
// 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);
}
}
Agregar interfaz:
// 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 ;
}
El contrato GravatarRegistry agrega una variable groupName al original:
// 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;
}
}
Implemente el contrato de fábrica y deje GravatarRegistry para la implementación dinámica más adelante.
subgraph.yaml
Agregue la configuración de plantillas, sus elementos de configuración son básicamente similares a las fuentes de datos, la dirección y el bloque de inicio no son compatibles con la fuente
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
esquema.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
}
fábrica.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);
}
Esto se utiliza principalmente para monitorear el contrato de fábrica.La clave es la última línea de código, que crea dinámicamente la fuente de datos del contrato que se monitoreará.
gravatar-registry.ts
También hay que hacer algunas modificaciones aquí, principalmente para agregar el campo 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()
}
Vuelva a compilar la versión:
graph codegen
graph build
yarn deploy
verificar
Finalmente llamamos al método createGravatarRegistry del contrato de fábrica. Aquí, test1, test2, test3 y test4 se pasan respectivamente y se crean cuatro contratos GravatarRegistry. Su nombre de grupo es test1, test2, test3, test4 respectivamente.
De hecho, este tipo de contrato inteligente creado dinámicamente por la fábrica también debería tener acceso a un contrato de enrutamiento a través del salto groupName Aquí, la complejidad del ejemplo se simplifica y la función del contrato GravatarRegistry se llama manualmente directamente a través de la dirección devuelta por el contrato de fábrica.
Llamado al método createGravatar de los cuatro contratos respectivamente, consulta de verificación:
El código de ejemplo es el siguiente: