Skip to content

Latest commit

 

History

History
982 lines (822 loc) · 31.4 KB

README-es.md

File metadata and controls

982 lines (822 loc) · 31.4 KB

Ink! Explorer Frontend

App Build Coverage Status Codacy Badge Docker Image Version Docker Image Size License

Demo

ink-explorer.blockcoders.io

About the explorer

Ink Explorer es una aplicacion que provee informacion relativa a los contratos que utilizan Ink! en blockchains basadas en Substrate. Se suscribe a la blockchain y a los eventos emitidos por los modulos de Ink! y guarda la informacion en su propia base de datos PostgreSQL. El back-end expone una API que puede interactuar con la base de datos y ejecutar consultas rapidas para obtener informacion especifica en poco tiempo.

La idea de este proyecto es brindar una herramienta que permita a los desarrolladores de Ink! explorar y analizar los contratos que se encuentran en blockchain. Esta herramienta se puede utilizar para analizar los contratos que se encuentran en blockchains basadas en Substrate que utilizan módulos Ink!. También se puede usar para analizar contratos que están en una blockchain local.

Este proyecto ofrece información útil que no está disponible en ningún otro lugar. Ya que el back end se encarga de obtener información relacionada con los saldos, transacciones y más, de los contratos que utilizan módulos Ink!. El explorador utiliza polkadot.js para comunicarse con las redes Substrate/Polkadot. Es seguro decir que este proyecto es imprescindible.

Introduccion

Levantando el servicio localmente

Configuracion del entorno

  • Instalar Node.js
    • El metodo recomentado es utilizando NVM
    • La verision de Node.js recomendada es v16.13
  • Instalar Docker

Instalar todas las dependencias

pnpm i --frozen-lockfile

Configurar las variables de entorno

Nota: El archivo .env tiene la configuracion para GraphQL, la base de datos PostgreSQL, Node y la url del RPC de la blockchain basada en Substrate.

cp .env.sample .env

Configuraciones del servicio

NODE_ENV=development
PORT=8080
LOG_NAME=ink-substrate-explorer-api
LOG_LEVEL=debug
SCHEMA_URL=https://ink-explorer-api.blockcoders.io/graphql

Configuraciones de GraphQL

GRAPHQL_DEBUG=true
GRAPHQL_PLAYGROUND=true
GRAPHQL_SORT_SCHEMA=true
GRAPHQL_INTROSPECTION=true

Configuraciones de la base de datos

DATABASE_HOST=postgres
DATABASE_NAME=ink
DATABASE_USERNAME=root
DATABASE_PASSWORD=password
DATABASE_RETRY_ATTEMPTS=5
DATABASE_RETRY_DELAY=3000

Configuraciones de la blockchain y la sincronizacion de datos

WS_PROVIDER=wss://rococo-contracts-rpc.polkadot.io
# Asignar el valor _true_ para procesar cada bloque desde FIRST_BLOCK_TO_LOAD hasta el ultimo bloque de la cadena. Asignar el valor _false_ para solo comenzar a procesar los bloques desde el ultimo bloque existente en la base de datos.
LOAD_ALL_BLOCKS=false
# Número de bloque a partir del cual el servicio comenzará a procesar bloques. (Puede ser génesis o algún otro bloque. Por ejemplo, el primer bloque admite contratos)
FIRST_BLOCK_TO_LOAD=0
# Número de bloques a procesar simultáneamente. Esto puede acelerar o retrasar el proceso de sincronización.
BLOCK_CONCURRENCY=1000

Levantando el servicio (DEV)

Para ejecutar en modo dev, aún se necesita el backend. Para eso, el archivo docker-compose.yaml ya cuenta con todos los servicios necesarios.

Ejecutar este comando también iniciará un contenedor para la interfaz (no querrás hacer esto en modo DEV). Para evitar esto, comente el servicio 'frontend'. Luego ejecute el comando

docker-compose up -d

Una vez que el servicio se está ejecutando, se puede acceder a pgAdmin siguiendo el enlace que se muestra en la terminal (en este caso localhost:80)

pgAdmin

Las credenciales para acceder a pgAdmin son (establecidas en el archivo docker-compose):

Registre un nuevo servidor en pgAdmin y establezca las credenciales para la base de datos PostgreSQL:

Click derecho en 'Servers' y seleccione "Register" -> "Server"

pgAdmin

Elija un nombre para el servidor (por ejemplo, "Docker")

pgAdmin

Establezca las credenciales para la base de datos PostgreSQL (esto se puede encontrar en el archivo docker-compose):

pgAdmin

Instanciar un nodo local de Substrate (opcional)

Para ejecutar un nodo de Substrate localmente, agregue al archivo docker-compose.yaml el siguiente servicio:

  substrate:
    image: blockcoders/substrate-contracts-node:latest
    restart: on-failure
    ports:
      - 9944:9944
    command: '--dev --ws-external'
    networks:
      ink-explorer-network:
        aliases:
          - "substrate"

Esto iniciará un nuevo contenedor con un nodo local de Substrate.

Otra forma de ejecutar un nodo local es con [esta guía de paritytech] (https://github.com/paritytech/substrate-contracts-node).

Nota: cambie la variable WS_PROVIDER en el archivo .env para que sea ws://127.0.0.1:9944

Levantando el servicio

To run the frontend service in DEV mode run:

  • pnpm start:dev

The service will reload if you make edits.

Note: A postgresDB up and running and a valid connection to a substrate node are required.

Starting the project (PROD)

Para iniciar los servicios de back-end y front-end, ejecute:

  • docker-compose up -d

Ejecutar la imagen de Docker del servicio front-end

Descarga la imagen de DockerHub

docker pull blockcoders/ink-substrate-explorer-frontend:latest

Run

# Crear la red de docker
docker network create ink-explorer-network

# Correr servicio
docker run -it -p 3000:3000 --network ink-explorer-network blockcoders/ink-substrate-explorer-frontend:latest

Verifique que la imagen comenzó a ejecutarse

docker ps

El resultado debería verse así:

CONTAINER ID   IMAGE                                    COMMAND                  CREATED          STATUS          PORTS                                       NAMES
f31a7d0fd6c8   blockcoders/ink-substrate-explorer-frontend   "docker-entrypoint.s…"   15 seconds ago   Up 14 seconds   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   funny_lumiere

Testing

Ejecución de las pruebas unitarias.

pnpm test

Ejecución de la cobertura de pruebas.

pnpm test:cov

Probando las consultas de GraphQL.

{"level":30,"time":1664298430389,"pid":1388770,"hostname":"username","name":"ink-substrate-explorer-api","msg":"App listening on http://0.0.0.0:8080"}

Una vez que el servicio back-end se está ejecutando, se puede acceder a GraphQL Playground en http://localhost:8080/graphql

backend

Definicion de la API

Una vez que el servicio esta levantado y corriendo correctamente se provee una API que puede utilizarse enviado consultas de GraphQL.

Consultas

Status: Recupera el estado de la aplicación

query {
  status
}

Respuesta:

{
  "data": {
    "status": "running"
  }
}

Version: Recupera la version de la aplicación

query {
  status
}

Respuesta:

{
  "data": {
    "version": "v1.0.6"
  }
}

getBlock: Recupera el bloque por hash

query {
  getBlock(hash: "0x0f615cf7edf8a1e8591893a594fe0ef67d5d56c4d9b1a89d8d120c5f821127fe") {
    hash
    number
    parentHash
    timestamp
    encodedLength
    transactions {
      hash
    }
  }
}

Respuesta:

{
  "data": {
    "getBlock": {
      "hash": "0x0f615cf7edf8a1e8591893a594fe0ef67d5d56c4d9b1a89d8d120c5f821127fe",
      "number": 7,
      "parentHash": "0xd8ecc752f280a3786c5cdd4d441d71488414fd6132ace481dd6ddb23fd8000b0",
      "timestamp": 1666888006111,
      "encodedLength": 312,
      "transactions": [
        {
          "hash": "0xdb561ee4432e07a959292acf9895ce379e2474a52160b93fd62496806fdf26cd"
        },
        {
          "hash": "0x33831da6b804e82cd7613e0d780823c7455c773546ea5e76c945ed10a6f6554b"
        }
      ]
    }
  }
}

getBlocks: Recupera bloques. Use 'skip' y 'take' para paginar. Use 'orderByNumber: false' para ordenar por tiempo y 'orderAsc: true' para ver primero los bloques más antiguos.

query {
  getBlocks(skip: 0, take: 1, orderByNumber: false, orderAsc: false) {
    hash
    number
    parentHash
    timestamp
    encodedLength
    transactions {
      hash
    }
  }
}

Respuesta:

{
  "data": {
    "getBlocks": [
      {
        "hash": "0x0f615cf7edf8a1e8591893a594fe0ef67d5d56c4d9b1a89d8d120c5f821127fe",
        "number": 7,
        "parentHash": "0xd8ecc752f280a3786c5cdd4d441d71488414fd6132ace481dd6ddb23fd8000b0",
        "timestamp": 1666888006111,
        "encodedLength": 312,
        "transactions": [
          {
            "hash": "0xdb561ee4432e07a959292acf9895ce379e2474a52160b93fd62496806fdf26cd"
          },
          {
            "hash": "0x33831da6b804e82cd7613e0d780823c7455c773546ea5e76c945ed10a6f6554b"
          }
        ]
      }
    ]
  }
}

getTransaction: Recupera una sola transacción por hash

query {
  getTransaction(hash: "0x33831da6b804e82cd7613e0d780823c7455c773546ea5e76c945ed10a6f6554b") {
    args
    blockHash
    callIndex
    decimals
    encodedLength
    era
    events {
      method
    }
    hash
    method
    nonce
    section
    signature
    signer
    ss58
    timestamp
    tip
    tokens
    type
    version
  }
}

Respuesta:

{
  "data": {
    "getTransaction": {
      "args": "{\"dest\":{\"id\":\"5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8\"},\"value\":0,\"gas_limit\":75000000000,\"storage_deposit_limit\":null,\"data\":\"0x84a15da18eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48005039278c0400000000000000000000\"}",
      "blockHash": "0x0f615cf7edf8a1e8591893a594fe0ef67d5d56c4d9b1a89d8d120c5f821127fe",
      "callIndex": "7,0",
      "decimals": "12",
      "encodedLength": 201,
      "era": "{\"mortalEra\":\"0x0b00\"}",
      "events": [
        {
          "method": "ContractEmitted"
        }
      ],
      "hash": "0x33831da6b804e82cd7613e0d780823c7455c773546ea5e76c945ed10a6f6554b",
      "method": "call",
      "nonce": 3,
      "section": "contracts",
      "signature": "0x78582786706e947a6d77ac5b49ba140b4c88ebc644421136bbfa8b66577e1e3efdbc1d981948546fff41600be9a716e4c38a8531a867853f26ba11ee21128f82",
      "signer": "5GVmSPghWsjACADGYi78dmhuZEgfgDwfixR7BM3aMEoNuTBc",
      "ss58": "42",
      "timestamp": 1666888006111,
      "tip": 0,
      "tokens": "Unit",
      "type": 4,
      "version": 132
    }
  }
}

getTransactionsByContract: Recupera una lista de transacciones de un contrato.

query {
  getTransactionsByContract(
    address: "5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8"
    skip: 0
    take: 1
    orderAsc: false
  ) {
    args
    blockHash
    callIndex
    decimals
    encodedLength
    era
    events {
      method
    }
    hash
    method
    nonce
    section
    signature
    signer
    ss58
    timestamp
    tip
    tokens
    type
    version
  }
}

Respuesta:

{
  "data": {
    "getTransactionsByContract": [
      {
        "args": "{\"dest\":{\"id\":\"5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8\"},\"value\":0,\"gas_limit\":75000000000,\"storage_deposit_limit\":null,\"data\":\"0x84a15da18eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48005039278c0400000000000000000000\"}",
        "blockHash": "0x0f615cf7edf8a1e8591893a594fe0ef67d5d56c4d9b1a89d8d120c5f821127fe",
        "callIndex": "7,0",
        "decimals": "12",
        "encodedLength": 201,
        "era": "{\"mortalEra\":\"0x0b00\"}",
        "events": [
          {
            "method": "ContractEmitted"
          }
        ],
        "hash": "0x33831da6b804e82cd7613e0d780823c7455c773546ea5e76c945ed10a6f6554b",
        "method": "call",
        "nonce": 3,
        "section": "contracts",
        "signature": "0x78582786706e947a6d77ac5b49ba140b4c88ebc644421136bbfa8b66577e1e3efdbc1d981948546fff41600be9a716e4c38a8531a867853f26ba11ee21128f82",
        "signer": "5GVmSPghWsjACADGYi78dmhuZEgfgDwfixR7BM3aMEoNuTBc",
        "ss58": "42",
        "timestamp": 1666888006111,
        "tip": 0,
        "tokens": "Unit",
        "type": 4,
        "version": 132
      }
    ]
  }
}

getTransactions: Recupera transacciones por hash de bloque (use 'skip' y 'take' para paginar. use 'orderAsc' para ver primero las más antiguas o las más nuevas)

query {
  getTransactions(skip: 0, take: 1, orderAsc: false) {
    args
    blockHash
    callIndex
    decimals
    encodedLength
    era
    events {
      method
    }
    hash
    method
    nonce
    section
    signature
    signer
    ss58
    timestamp
    tip
    tokens
    type
    version
  }
}

Respuesta:

{
  "data": {
    "getTransactions": [
      {
        "args": "{\"now\":1666888006111}",
        "blockHash": "0x0f615cf7edf8a1e8591893a594fe0ef67d5d56c4d9b1a89d8d120c5f821127fe",
        "callIndex": "2,0",
        "decimals": "12",
        "encodedLength": 11,
        "era": "{\"immortalEra\":\"0x00\"}",
        "events": [],
        "hash": "0xdb561ee4432e07a959292acf9895ce379e2474a52160b93fd62496806fdf26cd",
        "method": "set",
        "nonce": 0,
        "section": "timestamp",
        "signature": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
        "signer": "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM",
        "ss58": "42",
        "timestamp": 1666888006111,
        "tip": 0,
        "tokens": "Unit",
        "type": 4,
        "version": 4
      }
    ]
  }
}

getEvent: Recupera un evento por su id

query {
  getEvent(id: "81735cc9-76d3-5984-83af-5872bc9eaeb7") {
    id
    index
    method
    section
    timestamp
    topics
    transactionHash
    data
    decodedData
    formattedData
  }
}

Respuesta:

{
  "data": {
    "getEvent": {
      "id": "81735cc9-76d3-5984-83af-5872bc9eaeb7",
      "index": "0x0703",
      "method": "ContractEmitted",
      "section": "contracts",
      "timestamp": 1666888006111,
      "topics": "[0x0045726332303a3a5472616e7366657200000000000000000000000000000000, 0x08be862c40d599dc6f4f28076712bb324c0cd2197c30f07459887b41fadff2c8, 0x2b00c7d40fe6d84d660f3e6bed90f218e022a0909f7e1a7ea35ada8b6e003564]",
      "transactionHash": "0x33831da6b804e82cd7613e0d780823c7455c773546ea5e76c945ed10a6f6554b",
      "data": "[\"5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8\",\"0x0001c40e2006bbebf9022c317f9337ad376e56d392917e5ac1397fe09b07c765c050018eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48005039278c0400000000000000000000\"]",
      "decodedData": "{\"args\":[\"5GVmSPghWsjACADGYi78dmhuZEgfgDwfixR7BM3aMEoNuTBc\",\"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\"0x00000000000000000000048c27395000\"],\"event\":{\"args\":[{\"name\":\"from\",\"type\":{\"sub\":{\"docs\":[],\"info\":10,\"type\":\"AccountId\",\"namespace\":\"ink_env::types::AccountId\",\"lookupIndex\":2,\"lookupNameRoot\":\"InkEnvAccountId\"},\"docs\":[],\"info\":9,\"type\":\"Option<AccountId>\",\"namespace\":\"Option\",\"lookupIndex\":11}},{\"name\":\"to\",\"type\":{\"sub\":{\"docs\":[],\"info\":10,\"type\":\"AccountId\",\"namespace\":\"ink_env::types::AccountId\",\"lookupIndex\":2,\"lookupNameRoot\":\"InkEnvAccountId\"},\"docs\":[],\"info\":9,\"type\":\"Option<AccountId>\",\"namespace\":\"Option\",\"lookupIndex\":11}},{\"name\":\"value\",\"type\":{\"info\":10,\"type\":\"Balance\"}}],\"docs\":[\" Event emitted when a token transfer occurs.\"],\"index\":0,\"identifier\":\"Transfer\"}}",
      "formattedData": "{\"from\":\"5GVmSPghWsjACADGYi78dmhuZEgfgDwfixR7BM3aMEoNuTBc\",\"to\":\"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\"value\":5}"
    }
  }
}

getEvents: Recupera eventos por dirección de contrato o hash de transacción (use 'skip' y 'take' para paginar, 'orderAsc' para ver primero los más antiguos o los más nuevos)

query {
  getEvents(contract: "5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8", skip: 0, take: 1, orderAsc: false) {
    id
    index
    method
    section
    timestamp
    topics
    transactionHash
    data
    decodedData
    formattedData
  }
}

Respuesta:

{
  "data": {
    "getEvents": [
      {
        "id": "81735cc9-76d3-5984-83af-5872bc9eaeb7",
        "index": "0x0703",
        "method": "ContractEmitted",
        "section": "contracts",
        "timestamp": 1666888006111,
        "topics": "[0x0045726332303a3a5472616e7366657200000000000000000000000000000000, 0x08be862c40d599dc6f4f28076712bb324c0cd2197c30f07459887b41fadff2c8, 0x2b00c7d40fe6d84d660f3e6bed90f218e022a0909f7e1a7ea35ada8b6e003564]",
        "transactionHash": "0x33831da6b804e82cd7613e0d780823c7455c773546ea5e76c945ed10a6f6554b",
        "data": "[\"5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8\",\"0x0001c40e2006bbebf9022c317f9337ad376e56d392917e5ac1397fe09b07c765c050018eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48005039278c0400000000000000000000\"]",
        "decodedData": "{\"args\":[\"5GVmSPghWsjACADGYi78dmhuZEgfgDwfixR7BM3aMEoNuTBc\",\"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\"0x00000000000000000000048c27395000\"],\"event\":{\"args\":[{\"name\":\"from\",\"type\":{\"sub\":{\"docs\":[],\"info\":10,\"type\":\"AccountId\",\"namespace\":\"ink_env::types::AccountId\",\"lookupIndex\":2,\"lookupNameRoot\":\"InkEnvAccountId\"},\"docs\":[],\"info\":9,\"type\":\"Option<AccountId>\",\"namespace\":\"Option\",\"lookupIndex\":11}},{\"name\":\"to\",\"type\":{\"sub\":{\"docs\":[],\"info\":10,\"type\":\"AccountId\",\"namespace\":\"ink_env::types::AccountId\",\"lookupIndex\":2,\"lookupNameRoot\":\"InkEnvAccountId\"},\"docs\":[],\"info\":9,\"type\":\"Option<AccountId>\",\"namespace\":\"Option\",\"lookupIndex\":11}},{\"name\":\"value\",\"type\":{\"info\":10,\"type\":\"Balance\"}}],\"docs\":[\" Event emitted when a token transfer occurs.\"],\"index\":0,\"identifier\":\"Transfer\"}}",
        "formattedData": "{\"from\":\"5GVmSPghWsjACADGYi78dmhuZEgfgDwfixR7BM3aMEoNuTBc\",\"to\":\"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\"value\":5}"
      }
    ]
  }
}

getContract: Recupera un contrato por address

query {
  getContract(address: "5G24svh2w4QXNhsHU5XBxf8N3Sw2MPu7sAemofv1bCuyxAzc") {
    address
    metadata
    hasMetadata
  }
}

Respuesta:

{
  "data": {
    "getContract": {
      "address": "5G24svh2w4QXNhsHU5XBxf8N3Sw2MPu7sAemofv1bCuyxAzc",
      "metadata": "{\n  \"source\": {\n    \"hash\": ...   }\n}\n",
      "hasMetadata": true
    }
  }
}

getContracts: Recupera una lista de contratos

query {
  getContracts(skip: 0, take: 10) {
    address
    metadata
    hasMetadata
    events {
      method
    }
  }
}

Respuesta:

{
  "data": {
    "getContracts": [
      {
        "address": "5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8",
        "hasMetadata": true,
        "metadata": "{ ... }",
        "events": [
          {
            "method": "ContractEmitted"
          },
        ]
      }
    ]
  }
}

getContractQueries: Recupera un contrato. Si este contrato ha cargado metadatos, también recuperará las consultas y los métodos de transacción que se pueden ejecutar.

query {
  getContractQueries(address: "5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8") {
    address
    hasMetadata
    queries {
      args
      docs
      method
      name
    }
  }
}

Respuesta:

{
  "data": {
    "getContractQueries": {
      "address": "5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8",
      "hasMetadata": true,
      "queries": [
        {
          "args": [],
          "docs": [
            " Returns the total token supply."
          ],
          "method": "totalSupply",
          "name": "Total supply"
        },
        {
          "args": [
            "{\"name\":\"to\",\"type\":{\"info\":10,\"type\":\"AccountId\"}}",
            "{\"name\":\"value\",\"type\":{\"info\":10,\"type\":\"Balance\"}}"
          ],
          "docs": [
            " Transfers `value` amount of tokens from the caller's account to account `to`.",
            "",
            " On success a `Transfer` event is emitted.",
            "",
            " # Errors",
            "",
            " Returns `InsufficientBalance` error if there are not enough tokens on",
            " the caller's account balance."
          ],
          "method": "transfer",
          "name": "Transfer"
        }
      ]
    }
  }
}

Mutaciones

decodeEvent: Decodifica los datos del evento para un evento específico. Requiere que los metadatos del contrato ya se hayan subido usando la mutación uploadMetadata

mutation {
  decodeEvent(
    contractAddress: "5ELpkDtq7werhT5ybZZMbVBcQTPNomvJP7j5kJQifv7GzVik"
    id: "972e782c-2517-5648-9bf1-4c693d2fed90"
  )
}

Respuesta:

{
  "data": {
    "decodeEvent": "{\"identifier\":\"Transfer\",\"decodedData\":{\"args\":[\"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y\",\"0x000000000000000000002d79883d2000\"],\"event\":{\"args\":[{\"name\":\"from\",\"type\":{\"info\":9,\"lookupIndex\":11,\"type\":\"Option<AccountId>\",\"docs\":[],\"namespace\":\"Option\",\"sub\":{\"info\":10,\"lookupIndex\":2,\"type\":\"AccountId\",\"docs\":[],\"namespace\":\"ink_env::types::AccountId\",\"lookupNameRoot\":\"InkEnvAccountId\"}}},{\"name\":\"to\",\"type\":{\"info\":9,\"lookupIndex\":11,\"type\":\"Option<AccountId>\",\"docs\":[],\"namespace\":\"Option\",\"sub\":{\"info\":10,\"lookupIndex\":2,\"type\":\"AccountId\",\"docs\":[],\"namespace\":\"ink_env::types::AccountId\",\"lookupNameRoot\":\"InkEnvAccountId\"}}},{\"name\":\"value\",\"type\":{\"info\":10,\"type\":\"Balance\"}}],\"docs\":[\" Event emitted when a token transfer occurs.\"],\"identifier\":\"Transfer\",\"index\":0}},\"formattedData\":{\"from\":\"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\"to\":\"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y\",\"value\":50}}"
  }
}

decodeEvents: Decodifica los datos de eventos para un contrato específico (use 'skip' y 'take' para seleccionar los eventos y 'orderAsc' para ordenar por tiempo). Requiere que los metadatos del contrato ya se hayan subido usando la mutación uploadMetadata

mutation {
  decodeEvents(contract: "5DfG5TyaffuJ78rHP71cvkYEtktRkpeMiJNJyxd8Q5924GR8", skip: 0, take: 1, orderAsc: false)
}

Respuesta:

{
  "data": {
    "decodeEvents": "[{\"identifier\":\"Transfer\",\"decodedData\":{\"args\":[\"5GVmSPghWsjACADGYi78dmhuZEgfgDwfixR7BM3aMEoNuTBc\",\"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\"0x00000000000000000000048c27395000\"],\"event\":{\"args\":[{\"name\":\"from\",\"type\":{\"info\":9,\"lookupIndex\":11,\"type\":\"Option<AccountId>\",\"docs\":[],\"namespace\":\"Option\",\"sub\":{\"info\":10,\"lookupIndex\":2,\"type\":\"AccountId\",\"docs\":[],\"namespace\":\"ink_env::types::AccountId\",\"lookupNameRoot\":\"InkEnvAccountId\"}}},{\"name\":\"to\",\"type\":{\"info\":9,\"lookupIndex\":11,\"type\":\"Option<AccountId>\",\"docs\":[],\"namespace\":\"Option\",\"sub\":{\"info\":10,\"lookupIndex\":2,\"type\":\"AccountId\",\"docs\":[],\"namespace\":\"ink_env::types::AccountId\",\"lookupNameRoot\":\"InkEnvAccountId\"}}},{\"name\":\"value\",\"type\":{\"info\":10,\"type\":\"Balance\"}}],\"docs\":[\" Event emitted when a token transfer occurs.\"],\"identifier\":\"Transfer\",\"index\":0}},\"formattedData\":{\"from\":\"5GVmSPghWsjACADGYi78dmhuZEgfgDwfixR7BM3aMEoNuTBc\",\"to\":\"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\"value\":5}}]"
  }
}

uploadMetadata: Para decodificar eventos es necesario cargar el ABI del contrato. Pasar un ABI en base64 a esta mutación lo guardará en la base de datos. Después de eso, ejecute una consulta decodeEvents para ver los datos decodificados en los eventos.

mutation Upload {
  uploadMetadata(
    contractAddress: "5G24svh2w4QXNhsHU5XBxf8N3Sw2MPu7sAemofv1bCuyxAzc"
    metadata: "ewogICJzb3VyY2UiOiB7CiAgICAiaGFzaCI6I...(base64)"
  )
}

Respuesta:

{
  "data": {
    "uploadMetadata": true
  }
}

Acerca de la decodificación de eventos

Para ver los datos decodificados de los eventos, hay un requisito: los metadatos del contrato deben cargarse al menos una vez.

Ejemplo de metadatos de un contrato ERC20:

{
  "source": {
    "hash": "0x3aa1c8ba5f59034a42a93c00ee039a9464d6fa63d70b6889a2596f4528b28a19",
    "language": "ink! 3.3.0",
    "compiler": "rustc 1.64.0-nightly"
  },
  "contract": {
    "name": "erc20",
    "version": "0.1.0",
    "authors": [
      "[your_name] <[your_email]>"
    ]
  },
  "V3": {
    "spec": {
      "constructors": [
        {
          "args": [
            {
              "label": "initial_supply",
              "type": {
                "displayName": [
                  "Balance"
                ],
                "type": 0
              }
            }
          ],
          "docs": [
            "Creates a new ERC-20 contract with the specified initial supply."
          ],
          "label": "new",
          "payable": false,
          "selector": "0x9bae9d5e"
        }
      ],
      "docs": [],
      "events": [
        {
          "args": [
            {
              "docs": [],
              "indexed": true,
              "label": "from",
              "type": {
                "displayName": [
                  "Option"
                ],
                "type": 11
              }
            },
            {
              "docs": [],
              "indexed": true,
              "label": "to",
              "type": {
                "displayName": [
                  "Option"
                ],
                "type": 11
              }
            },
            {
              "docs": [],
              "indexed": false,
              "label": "value",
              "type": {
                "displayName": [
                  "Balance"
                ],
                "type": 0
              }
            }
          ],
          "docs": [
            " Event emitted when a token transfer occurs."
          ],
          "label": "Transfer"
        },
        {
          "args": [
            {
              "docs": [],
              "indexed": true,
              "label": "owner",
              "type": {
                "displayName": [
                  "AccountId"
                ],
                "type": 2
              }
            },
            {
              "docs": [],
              "indexed": true,
              "label": "spender",
              "type": {
                "displayName": [
                  "AccountId"
                ],
                "type": 2
              }
            },
            {
              "docs": [],
              "indexed": false,
              "label": "value",
              "type": {
                "displayName": [
                  "Balance"
                ],
                "type": 0
              }
            }
          ],
          "docs": [
            " Event emitted when an approval occurs that `spender` is allowed to withdraw",
            " up to the amount of `value` tokens from `owner`."
          ],
          "label": "Approval"
        }
      ],
      ...

Una vez cargado, los eventos se pueden decodificar mediante las mutaciones decodeEvent o decodeEvents que se encuentran en la sección Mutaciones.

Nota: Los metadatos deben cargarse como un texto (string) en base64.

Para obtener más información sobre la carga de metadatos, vaya a la sección Mutaciones y busque uploadMetadata.

Suscripciones

La primera vez que se inicia el nodo, es posible que deba comenzar desde el bloque 0 y cargar todos los bloques (LOAD_ALL_BLOCKS env var debe establecerse en verdadero). Si desea comenzar desde un bloque específico, puede usar FIRST_BLOCK_TO_LOAD env var para comenzar desde otro bloque.

En caso de un tiempo de inactividad del nodo, las suscripciones se reconectarán automáticamente recuperando todos los bloques nuevos desde el último bloque que se procesó.

Nota: Cargar todos los bloques puede llevar mucho tiempo dependiendo de la cantidad de bloques que deban cargarse. Se recomienda utilizar un nodo con una conexión rápida a Internet. El nodo podrá procesar todos los bloques en unas pocas horas.

Algunos puntos de referencia

Utilizando BLOCK_CONCURRENCY = 100

  • 100 bloques en ~ 6 segundos
  • 1000 bloques en ~ 30.5 segundos
  • 10000 bloques en ~ 4:24 minutos
  • 100000 bloques en ~ 39.57 minutos

Utilizando BLOCK_CONCURRENCY = 1000

  • 100 bloques en ~ 0.5 segundos
  • 1000 bloques en ~ 5 segundos
  • 10000 bloques en ~ 3 minutos
  • 100000 bloques en ~ 24 minutos

Registro de cambios

Consulte Changelog para más información.

Contribuye

¡Las contribuciones son bienvenidas! Consulte Contributing.

Colaboradores

Licencia

Con licencia de Apache 2.0 - consulte el archivo LICENSE para obtener más información.