From 45fa9b49e4c5d4213e326418ed8870827b0a2911 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Wed, 5 Jul 2023 14:55:59 +0700 Subject: [PATCH 01/54] docs: cw721 --- docs/services/cw721/cw721-media.md | 53 ++++++++++++++++++++++++++++++ docs/services/cw721/cw721.md | 38 +++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 docs/services/cw721/cw721-media.md create mode 100644 docs/services/cw721/cw721.md diff --git a/docs/services/cw721/cw721-media.md b/docs/services/cw721/cw721-media.md new file mode 100644 index 000000000..22e00e852 --- /dev/null +++ b/docs/services/cw721/cw721-media.md @@ -0,0 +1,53 @@ +## Cw721 Media Service + +```mermaid + sequenceDiagram + autonumber + participant A as Cw721MediaService + participant B as DB + participant C as Network + participant D as S3 + + loop Interval + A->>B: Get top 10 token which media_info = null (unprocessed) + activate B + B-->>A: token instances + deactivate B + + A->>C: get token info (token_uri, extension) + activate C + C-->>A: token info + deactivate C + + alt token_uri!=null + A->>C: call ipfs link from token_uri to get metadata + else token_uri==null + A->>A: metadata = extension + end + + A->>A: get image url from metadata + alt image_url!=null + A->>D: get image then upload image to S3 + activate D + D-->>A: S3 link + deactivate D + else image_url==null + A->>A: empty image offchain + else error + A->>A: empty image offchain + end + + alt animation_url!=null + A->>D: get animation then upload image to S3 + activate D + D-->>A: S3 link + deactivate D + else animation_url==null + A->>A: empty animation offchain + else error + A->>A: empty animation offchain + end + + A-->>B: update media_info (animation offchain, image offchain) + end +``` diff --git a/docs/services/cw721/cw721.md b/docs/services/cw721/cw721.md new file mode 100644 index 000000000..20798a40c --- /dev/null +++ b/docs/services/cw721/cw721.md @@ -0,0 +1,38 @@ +## Cw721 Service + +```mermaid + sequenceDiagram + autonumber + participant A as Cw721Service + participant B as DB + + loop Interval + A->>B: Get BlockCheckpoint for Cw721Service + activate B + B-->>A: Return BlockCheckpoint + deactivate B + alt not found BlockCheckpoint + A->>A: Set checkpoint = startBlock config + end + A->>A: endBlock = startBlock + config.BlocksPerCall + + A->>B: Get cw721 smart contract event from startBlock to endBlock + activate B + B-->>A: return list cw721 events + deactivate B + A->>A: handle each cw721 event type + + alt instantiate + A->>B: Upsert new cw721 contract instance + else mint + A->>B: Insert new cw721 token instance + else transfer + A->>B: Update new owner + else burn + A->>B: Update burn status + end + + A->>B: insert new cw721 event instances + A->>B: Update checkpoint = endBlock + end +``` From b27aa4c5f9700a5e8171ceb30209f917b24afd4d Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 7 Jul 2023 14:25:31 +0700 Subject: [PATCH 02/54] docs: cw20 --- docs/services/cw20/cw20.md | 33 +++++++++++++++++++ docs/services/cw20/cw20_update_by_contract.md | 23 +++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 docs/services/cw20/cw20.md create mode 100644 docs/services/cw20/cw20_update_by_contract.md diff --git a/docs/services/cw20/cw20.md b/docs/services/cw20/cw20.md new file mode 100644 index 000000000..05d447c4a --- /dev/null +++ b/docs/services/cw20/cw20.md @@ -0,0 +1,33 @@ +## Cw20 Service + +```mermaid + sequenceDiagram + autonumber + participant A as Cw20Service + participant B as DB + participant C as Network + participant D as Cw20UpdateByContractService + loop Interval + A->>B: Get BlockCheckpoint for Cw20Service + activate B + B-->>A: Return BlockCheckpoint + deactivate B + alt not found BlockCheckpoint + A->>A: Set checkpoint = startBlock config + end + A->>A: endBlock = startBlock + config.BlocksPerCall + A->>B: Get cw20 smart contract event from startBlock to endBlock + activate B + B-->>A: return list cw20 events + deactivate B + A->>A: handle each cw20 instantiate event + A->>C: Get all current holders and their balance + activate C + C->>A: Return results + deactivate C + A->>B: Insert into cw20_contract and cw20_holder + A->>B: insert new cw20 event instances + A->>B: Update checkpoint = endBlock + A->>D: Call action update cw20 by contract {contract, startBlock, endBlock} + end +``` diff --git a/docs/services/cw20/cw20_update_by_contract.md b/docs/services/cw20/cw20_update_by_contract.md new file mode 100644 index 000000000..d4b665ee4 --- /dev/null +++ b/docs/services/cw20/cw20_update_by_contract.md @@ -0,0 +1,23 @@ +## Cw20 Update By Contract Service + +```mermaid + sequenceDiagram + autonumber + participant A as Cw20UpdateByContract + participant B as DB + loop Interval + A->>B: Get cw20 smart contract event from startBlock to endBlock for specified contract + activate B + B-->>A: return list cw20 events + deactivate B + A->>A: handle each cw20 event type + alt mint + A->>B: Insert new cw721_holder instance or add balance if he already has balance + else transfer + A->>B: Add balance to recipient and sub balance to sender + else burn + A->>B: Sub balance to owner + end + A->>B: Update total_supply + end +``` From 5e50079af69f8d0aaab0ed734184b55bf9f8d182 Mon Sep 17 00:00:00 2001 From: Phan Anh Tuan Date: Mon, 31 Jul 2023 14:16:24 +0700 Subject: [PATCH 03/54] fix: update validator as UNSPECIFIED when not found onchain --- src/models/validator.ts | 3 +++ .../crawl_validator.service.ts | 22 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/models/validator.ts b/src/models/validator.ts index 71c58a81b..4e9ea8758 100644 --- a/src/models/validator.ts +++ b/src/models/validator.ts @@ -73,6 +73,9 @@ export class Validator extends BaseModel { return { BONDED: 'BOND_STATUS_BONDED', UNBONDED: 'BOND_STATUS_UNBONDED', + UNSPECIFIED: 'BOND_STATUS_UNSPECIFIED', + UNBONDING: 'BOND_STATUS_UNBONDING', + UNRECOGNIZED: 'UNRECOGNIZED', }; } diff --git a/src/services/crawl-validator/crawl_validator.service.ts b/src/services/crawl-validator/crawl_validator.service.ts index d724269ab..af0aa6ab6 100644 --- a/src/services/crawl-validator/crawl_validator.service.ts +++ b/src/services/crawl-validator/crawl_validator.service.ts @@ -111,7 +111,7 @@ export default class CrawlValidatorService extends BullableService { } const validatorInDB: Validator[] = await knex('validator').select('*'); - + const offchainMapped: Map = new Map(); await Promise.all( validators.map(async (validator) => { const foundValidator = validatorInDB.find( @@ -123,6 +123,9 @@ export default class CrawlValidatorService extends BullableService { if (!foundValidator) { validatorEntity = Validator.createNewValidator(validator); } else { + // mark this offchain validator is mapped with onchain + offchainMapped.set(validator.operator_address, true); + validatorEntity = foundValidator; validatorEntity.jailed = validator.jailed; validatorEntity.status = validator.status; @@ -146,6 +149,23 @@ export default class CrawlValidatorService extends BullableService { updateValidators = await this.loadCustomInfo(updateValidators); + // loop all validator not found onchain, update status is UNSPECIFIED + validatorInDB + .filter((val: any) => !offchainMapped.get(val.operator_address)) + .forEach(async (validatorNotOnchain: any) => { + this.logger.debug( + 'Account not found onchain: ', + validatorNotOnchain.operator_address + ); + validatorNotOnchain.status = Validator.STATUS.UNSPECIFIED; + + validatorNotOnchain.jailed_until = + validatorNotOnchain.jailed_until.toISOString(); + validatorNotOnchain.unbonding_time = + validatorNotOnchain.unbonding_time.toISOString(); + updateValidators.push(validatorNotOnchain); + }); + await Validator.query() .insert(updateValidators) .onConflict('operator_address') From 147fb9bd058277412ce5adaf4b4c277759f0317c Mon Sep 17 00:00:00 2001 From: Phan Anh Tuan Date: Tue, 1 Aug 2023 14:30:04 +0700 Subject: [PATCH 04/54] fix: remove token and delegator_shares when validator is UNRECOGNIZED --- src/services/crawl-validator/crawl_validator.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/crawl-validator/crawl_validator.service.ts b/src/services/crawl-validator/crawl_validator.service.ts index af0aa6ab6..6e0cbe132 100644 --- a/src/services/crawl-validator/crawl_validator.service.ts +++ b/src/services/crawl-validator/crawl_validator.service.ts @@ -157,7 +157,9 @@ export default class CrawlValidatorService extends BullableService { 'Account not found onchain: ', validatorNotOnchain.operator_address ); - validatorNotOnchain.status = Validator.STATUS.UNSPECIFIED; + validatorNotOnchain.status = Validator.STATUS.UNRECOGNIZED; + validatorNotOnchain.tokens = 0; + validatorNotOnchain.delegator_shares = 0; validatorNotOnchain.jailed_until = validatorNotOnchain.jailed_until.toISOString(); From dde64260d599eff3d746e8e3b8cf71a71693de8c Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Wed, 2 Aug 2023 14:40:11 +0700 Subject: [PATCH 05/54] feat: crawl ibc tao --- ci/config.json.ci | 5 + config.json | 5 + migrations/20230802071931_ibc_tao_model.ts | 36 ++ src/common/constant.ts | 5 + src/models/event.ts | 10 +- src/models/event_attribute.ts | 18 + src/models/ibc_channel.ts | 64 +++ src/models/ibc_client.ts | 40 ++ src/models/ibc_connection.ts | 51 +++ src/models/index.ts | 3 + src/services/ibc/crawl_ibc_tao.service.ts | 259 +++++++++++ test/unit/services/ibc/crawl_ibc_tao.spec.ts | 428 +++++++++++++++++++ 12 files changed, 923 insertions(+), 1 deletion(-) create mode 100644 migrations/20230802071931_ibc_tao_model.ts create mode 100644 src/models/ibc_channel.ts create mode 100644 src/models/ibc_client.ts create mode 100644 src/models/ibc_connection.ts create mode 100644 src/services/ibc/crawl_ibc_tao.service.ts create mode 100644 test/unit/services/ibc/crawl_ibc_tao.spec.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index 245de74f2..2a191016e 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -161,5 +161,10 @@ }, "jobRedecodeTx": { "limitRecordGet": 100 + }, + "crawlIbcTao": { + "key": "crawlIbcTao", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100 } } diff --git a/config.json b/config.json index cd07b5711..87061ab13 100644 --- a/config.json +++ b/config.json @@ -161,5 +161,10 @@ }, "jobRedecodeTx": { "limitRecordGet": 100 + }, + "crawlIbcTao": { + "key": "crawlIbcTao", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100 } } diff --git a/migrations/20230802071931_ibc_tao_model.ts b/migrations/20230802071931_ibc_tao_model.ts new file mode 100644 index 000000000..a1b2a03b4 --- /dev/null +++ b/migrations/20230802071931_ibc_tao_model.ts @@ -0,0 +1,36 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('ibc_client', (table) => { + table.increments(); + table.string('client_id').notNullable().unique(); + table.string('counterparty_chain_id').notNullable(); + table.jsonb('client_state').notNullable(); + table.jsonb('consensus_state').notNullable(); + table.string('client_type').notNullable(); + }); + await knex.schema.createTable('ibc_connection', (table) => { + table.increments(); + table.integer('ibc_client_id').index(); + table.string('connection_id').notNullable().unique(); + table.string('counterparty_client_id').notNullable(); + table.string('counterparty_connection_id').notNullable(); + table.foreign('ibc_client_id').references('ibc_client.id'); + }); + await knex.schema.createTable('ibc_channel', (table) => { + table.increments(); + table.integer('ibc_connection_id').index(); + table.string('channel_id').notNullable().unique(); + table.string('port_id').notNullable().index(); + table.string('counterparty_port_id').notNullable(); + table.string('counterparty_channel_id').notNullable(); + table.string('state').notNullable(); + table.foreign('ibc_connection_id').references('ibc_connection.id'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists('ibc_client'); + await knex.schema.dropTableIfExists('ibc_connection'); + await knex.schema.dropTableIfExists('ibc_channel'); +} diff --git a/src/common/constant.ts b/src/common/constant.ts index fcbed0f5f..bcedb6638 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -66,6 +66,7 @@ export const BULL_JOB_NAME = { REINDEX_CW721_HISTORY: 'reindex:cw721-history', HANDLE_MIGRATE_CONTRACT: 'handle:migrate-contract', JOB_REDECODE_TX: 'job:redecode-tx', + CRAWL_IBC_TAO: 'crawl:ibc-tao', }; export const SERVICE = { @@ -223,6 +224,10 @@ export const SERVICE = { path: 'v1.ReDecodeTx', }, }, + CrawlIBCTaoService: { + key: 'CrawlIBCTaoService', + name: 'v1.CrawlIBCTaoService', + }, }, }; diff --git a/src/models/event.ts b/src/models/event.ts index e5cc1a9d1..efc835cd0 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -6,7 +6,7 @@ import { EventAttribute } from './event_attribute'; import { TransactionMessage } from './transaction_message'; export class Event extends BaseModel { - [relation: string]: any; + [relation: string]: any | any[]; id!: string; @@ -90,5 +90,13 @@ export class Event extends BaseModel { USE_FEEGRANT: 'use_feegrant', SET_FEEGRANT: 'set_feegrant', MIGRATE: 'migrate', + CREATE_CLIENT: 'create_client', + CONNECTION_OPEN_ACK: 'connection_open_ack', + CONNECTION_OPEN_CONFIRM: 'connection_open_confirm', + CHANNEL_OPEN_ACK: 'channel_open_ack', + CHANNEL_OPEN_CONFIRM: 'channel_open_confirm', + CHANNEL_CLOSE_INIT: 'channel_close_init', + CHANNEL_CLOSE_CONFIRM: 'channel_close_confirm', + CHANNEL_CLOSE: 'channel_close', }; } diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 49200ac98..555bcd3ee 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -2,6 +2,7 @@ import { Model } from 'objection'; import BaseModel from './base'; // eslint-disable-next-line import/no-cycle import { Event } from './event'; +import { Transaction } from './transaction'; export class EventAttribute extends BaseModel { event_id!: string; @@ -52,6 +53,14 @@ export class EventAttribute extends BaseModel { to: 'event.id', }, }, + transaction: { + relation: Model.BelongsToOneRelation, + modelClass: Transaction, + join: { + from: 'event_attribute.tx_id', + to: 'transaction.id', + }, + }, }; } @@ -90,5 +99,14 @@ export class EventAttribute extends BaseModel { GRANTER: 'granter', GRANTEE: 'grantee', FROM: 'from', + CLIENT_ID: 'client_id', + CLIENT_TYPE: 'client_type', + CONNECTION_ID: 'connection_id', + COUNTERPARTY_CLIENT_ID: 'counterparty_client_id', + COUNTERPARTY_CONNECTION_ID: 'counterparty_connection_id', + CHANNEL_ID: 'channel_id', + PORT_ID: 'port_id', + COUNTERPARTY_PORT_ID: 'counterparty_port_id', + COUNTERPARTY_CHANNEL_ID: 'counterparty_channel_id', }; } diff --git a/src/models/ibc_channel.ts b/src/models/ibc_channel.ts new file mode 100644 index 000000000..b1720ccaa --- /dev/null +++ b/src/models/ibc_channel.ts @@ -0,0 +1,64 @@ +/* eslint-disable import/no-cycle */ +import { Model } from 'objection'; +import BaseModel from './base'; +import { IbcConnection } from './ibc_connection'; + +export class IbcChannel extends BaseModel { + id!: number; + + ibc_connection_id!: number; + + channel_id!: string; + + port_id!: string; + + counterparty_port_id!: string; + + counterparty_channel_id!: string; + + state!: string; + + static get tableName() { + return 'ibc_channel'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'ibc_connection_id', + 'channel_id', + 'port_id', + 'counterparty_port_id', + 'counterparty_channel_id', + 'state', + ], + properties: { + ibc_connection_id: { type: 'number' }, + channel_id: { type: 'string' }, + port_id: { type: 'string' }, + counterparty_port_id: { type: 'string' }, + counterparty_channel_id: { type: 'string' }, + state: { type: 'string' }, + }, + }; + } + + static get relationMappings() { + return { + ibc_connection: { + relation: Model.BelongsToOneRelation, + modelClass: IbcConnection, + join: { + from: 'ibc_channel.ibc_connection_id', + to: 'ibc_connection.id', + }, + }, + }; + } + + static STATUS = { + OPEN: 'OPEN', + CLOSE: 'CLOSE', + }; +} diff --git a/src/models/ibc_client.ts b/src/models/ibc_client.ts new file mode 100644 index 000000000..4575e446d --- /dev/null +++ b/src/models/ibc_client.ts @@ -0,0 +1,40 @@ +/* eslint-disable import/no-cycle */ +import BaseModel from './base'; + +export class IbcClient extends BaseModel { + id!: number; + + client_id!: string; + + counterparty_chain_id!: string; + + client_state!: any; + + consensus_state!: any; + + client_type!: string; + + static get tableName() { + return 'ibc_client'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'client_id', + 'counterparty_chain_id', + 'client_state', + 'consensus_state', + 'client_type', + ], + properties: { + client_id: { type: 'string' }, + counterparty_chain_id: { type: 'string' }, + client_state: { type: 'object' }, + consensus_state: { type: 'object' }, + client_type: { type: 'string' }, + }, + }; + } +} diff --git a/src/models/ibc_connection.ts b/src/models/ibc_connection.ts new file mode 100644 index 000000000..df9ed2e24 --- /dev/null +++ b/src/models/ibc_connection.ts @@ -0,0 +1,51 @@ +/* eslint-disable import/no-cycle */ +import { Model } from 'objection'; +import BaseModel from './base'; +import { IbcClient } from './ibc_client'; + +export class IbcConnection extends BaseModel { + id!: number; + + ibc_client_id!: number; + + connection_id!: string; + + counterparty_client_id!: string; + + counterparty_connection_id!: string; + + static get tableName() { + return 'ibc_connection'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'ibc_client_id', + 'connection_id', + 'counterparty_client_id', + 'counterparty_connection_id', + ], + properties: { + ibc_client_id: { type: 'number' }, + connection_id: { type: 'string' }, + counterparty_client_id: { type: 'string' }, + counterparty_connection_id: { type: 'string' }, + }, + }; + } + + static get relationMappings() { + return { + ibc_client: { + relation: Model.BelongsToOneRelation, + modelClass: IbcClient, + join: { + from: 'ibc_connection.ibc_client_id', + to: 'ibc_client.id', + }, + }, + }; + } +} diff --git a/src/models/index.ts b/src/models/index.ts index 3ab4c3c6e..1c28230d7 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -23,3 +23,6 @@ export * from './code_id_verification'; export * from './feegrant'; export * from './feegrant_history'; export * from './cw20_total_holder_stats'; +export * from './ibc_client'; +export * from './ibc_connection'; +export * from './ibc_channel'; diff --git a/src/services/ibc/crawl_ibc_tao.service.ts b/src/services/ibc/crawl_ibc_tao.service.ts new file mode 100644 index 000000000..4f047f58c --- /dev/null +++ b/src/services/ibc/crawl_ibc_tao.service.ts @@ -0,0 +1,259 @@ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import { Knex } from 'knex'; +import _ from 'lodash'; +import knex from '../../common/utils/db_connection'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import { + BlockCheckpoint, + Event, + EventAttribute, + IbcChannel, + IbcClient, + IbcConnection, +} from '../../models'; +import { getAttributeFrom } from '../../common/utils/smart_contract'; + +@Service({ + name: SERVICE.V1.CrawlIBCTaoService.key, + version: 1, +}) +export default class CrawlIbcTaoService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_IBC_TAO, + jobName: BULL_JOB_NAME.CRAWL_IBC_TAO, + }) + public async crawlIbcTao(): Promise { + const [startHeight, endHeight, updateBlockCheckpoint] = + await BlockCheckpoint.getCheckpoint( + BULL_JOB_NAME.CRAWL_IBC_TAO, + [BULL_JOB_NAME.HANDLE_TRANSACTION], + config.crawlIbcTao.key + ); + this.logger.info( + `Handle IBC/TAO, startHeight: ${startHeight}, endHeight: ${endHeight}` + ); + if (startHeight > endHeight) return; + const events = await Event.query() + .withGraphFetched('attributes') + .joinRelated('message') + .select('event.id', 'event.type', 'message.content') + .whereIn('event.type', [ + Event.EVENT_TYPE.CREATE_CLIENT, + Event.EVENT_TYPE.CONNECTION_OPEN_ACK, + Event.EVENT_TYPE.CONNECTION_OPEN_CONFIRM, + Event.EVENT_TYPE.CHANNEL_OPEN_ACK, + Event.EVENT_TYPE.CHANNEL_OPEN_CONFIRM, + Event.EVENT_TYPE.CHANNEL_CLOSE_INIT, + Event.EVENT_TYPE.CHANNEL_CLOSE_CONFIRM, + Event.EVENT_TYPE.CHANNEL_CLOSE, + ]) + .andWhere('event.block_height', '>', startHeight) + .andWhere('event.block_height', '<=', endHeight) + .orderBy('event.id'); + await knex.transaction(async (trx) => { + await this.handleNewIbcClient( + events.filter((event) => event.type === Event.EVENT_TYPE.CREATE_CLIENT), + trx + ); + await this.handleNewIbcConnection( + events.filter( + (event) => + event.type === Event.EVENT_TYPE.CONNECTION_OPEN_CONFIRM || + event.type === Event.EVENT_TYPE.CONNECTION_OPEN_ACK + ), + trx + ); + await this.handleNewIbcChannel( + events.filter( + (event) => + event.type === Event.EVENT_TYPE.CHANNEL_OPEN_ACK || + event.type === Event.EVENT_TYPE.CHANNEL_OPEN_CONFIRM + ), + trx + ); + await this.handleCloseIbcChannel( + events.filter( + (event) => + event.type === Event.EVENT_TYPE.CHANNEL_CLOSE || + event.type === Event.EVENT_TYPE.CHANNEL_CLOSE_CONFIRM || + event.type === Event.EVENT_TYPE.CHANNEL_CLOSE_INIT + ), + trx + ); + updateBlockCheckpoint.height = endHeight; + await BlockCheckpoint.query() + .transacting(trx) + .insert(updateBlockCheckpoint) + .onConflict('job_name') + .merge(); + }); + } + + async handleNewIbcClient(events: Event[], trx: Knex.Transaction) { + if (events.length > 0) { + const newClients: IbcClient[] = events.map((event) => + IbcClient.fromJson({ + client_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CLIENT_ID + ), + counterparty_chain_id: event.content.client_state.chain_id, + client_state: event.content.client_state, + consensus_state: event.content.consensus_state, + client_type: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CLIENT_TYPE + ), + }) + ); + this.logger.info('New IBC Clients:'); + this.logger.info(newClients); + await IbcClient.query().insert(newClients).transacting(trx); + } + } + + async handleNewIbcConnection(events: Event[], trx: Knex.Transaction) { + if (events.length > 0) { + const ibcClientsByClientId = _.keyBy( + await IbcClient.query() + .whereIn( + 'client_id', + events.map((event) => + getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CLIENT_ID + ) + ) + ) + .transacting(trx), + 'client_id' + ); + const newConnections: IbcConnection[] = events.map((event) => + IbcConnection.fromJson({ + ibc_client_id: + ibcClientsByClientId[ + getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CLIENT_ID + ) + ].id, + connection_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CONNECTION_ID + ), + counterparty_client_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.COUNTERPARTY_CLIENT_ID + ), + counterparty_connection_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.COUNTERPARTY_CONNECTION_ID + ), + }) + ); + this.logger.info('New IBC Connections:'); + this.logger.info(newConnections); + await IbcConnection.query().insert(newConnections).transacting(trx); + } + } + + async handleNewIbcChannel(events: Event[], trx: Knex.Transaction) { + if (events.length > 0) { + const ibcConnectionsByConnetionId = _.keyBy( + await IbcConnection.query() + .whereIn( + 'connection_id', + events.map((event) => + getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CONNECTION_ID + ) + ) + ) + .transacting(trx), + 'connection_id' + ); + const newChannels: IbcChannel[] = events.map((event) => + IbcChannel.fromJson({ + ibc_connection_id: + ibcConnectionsByConnetionId[ + getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CONNECTION_ID + ) + ].id, + channel_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CHANNEL_ID + ), + port_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.PORT_ID + ), + counterparty_port_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.COUNTERPARTY_PORT_ID + ), + counterparty_channel_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.COUNTERPARTY_CHANNEL_ID + ), + state: IbcChannel.STATUS.OPEN, + }) + ); + this.logger.info('New IBC Channels:'); + this.logger.info(newChannels); + await IbcChannel.query().insert(newChannels).transacting(trx); + } + } + + async handleCloseIbcChannel(events: Event[], trx: Knex.Transaction) { + if (events.length > 0) { + const queries = events.map((event) => { + const channelId = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CHANNEL_ID + ); + if (!channelId) { + throw new Error( + `Event close channel not found channelId: ${event.id}` + ); + } + return IbcChannel.query() + .patch({ + state: IbcChannel.STATUS.CLOSE, + }) + .where({ + channel_id: channelId, + }) + .transacting(trx); + }); + await Promise.all(queries); + } + } + + async _start(): Promise { + await this.createJob( + BULL_JOB_NAME.CRAWL_IBC_TAO, + BULL_JOB_NAME.CRAWL_IBC_TAO, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + every: config.crawlIbcTao.millisecondRepeatJob, + }, + } + ); + return super._start(); + } +} diff --git a/test/unit/services/ibc/crawl_ibc_tao.spec.ts b/test/unit/services/ibc/crawl_ibc_tao.spec.ts new file mode 100644 index 000000000..699a6d02c --- /dev/null +++ b/test/unit/services/ibc/crawl_ibc_tao.spec.ts @@ -0,0 +1,428 @@ +import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { ServiceBroker } from 'moleculer'; +import knex from '../../../../src/common/utils/db_connection'; +import { + Block, + Event, + IbcChannel, + IbcClient, + IbcConnection, + Transaction, +} from '../../../../src/models'; +import CrawlIbcTaoService from '../../../../src/services/ibc/crawl_ibc_tao.service'; + +@Describe('Test crawl ibc service') +export default class CrawlIbcTest { + broker = new ServiceBroker({ logger: false }); + + crawlIbcTaoSerivce = this.broker.createService( + CrawlIbcTaoService + ) as CrawlIbcTaoService; + + block: Block = Block.fromJson({ + height: 1300000, + hash: '4801997745BDD354C8F11CE4A4137237194099E664CD8F83A5FBA9041C43FE9F', + time: '2023-01-12T01:53:57.216Z', + proposer_address: 'auraomd;cvpio3j4eg', + data: {}, + }); + + transaction: Transaction = Transaction.fromJson({ + height: this.block.height, + hash: '4A8B0DE950F563553A81360D4782F6EC451F6BEF7AC50E2459D1997FA168997D', + codespace: '', + code: 0, + gas_used: '123035', + gas_wanted: '141106', + gas_limit: '141106', + fee: 353, + timestamp: '2023-01-12T01:53:57.000Z', + index: 0, + data: { + tx_response: { + logs: [], + }, + }, + }); + + @BeforeAll() + async initSuite() { + this.crawlIbcTaoSerivce.getQueueManager().stopAll(); + await knex.raw('TRUNCATE TABLE block, ibc_client RESTART IDENTITY CASCADE'); + await Block.query().insert(this.block); + } + + @AfterAll() + async tearDown() { + await this.broker.stop(); + } + + @Test('Test handleNewIbcClient') + async testHandleNewIbcClient() { + await knex.transaction(async (trx) => { + const events = Event.fromJson({ + type: Event.EVENT_TYPE.CREATE_CLIENT, + attributes: [ + { + value: '07-tendermint-21', + key: 'client_id', + event_id: '1', + }, + { + value: '07-tendermint', + key: 'client_type', + event_id: '1', + }, + { + value: '3-5086858', + key: 'consensus_height', + event_id: '1', + }, + ], + content: { + '@type': '/ibc.core.client.v1.MsgCreateClient', + signer: 'aura1gypt2w7xg5t9yr76hx6zemwd4xv72jckk03r6t', + client_state: { + '@type': '/ibc.lightclients.tendermint.v1.ClientState', + chain_id: 'axelar-testnet-lisbon-3', + proof_specs: [ + { + leaf_spec: { + hash: 'SHA256', + length: 'VAR_PROTO', + prefix: 'AA==', + prehash_key: 'NO_HASH', + prehash_value: 'SHA256', + }, + max_depth: 0, + min_depth: 0, + inner_spec: { + hash: 'SHA256', + child_size: 33, + child_order: [0, 1], + empty_child: '', + max_prefix_length: 12, + min_prefix_length: 4, + }, + }, + { + leaf_spec: { + hash: 'SHA256', + length: 'VAR_PROTO', + prefix: 'AA==', + prehash_key: 'NO_HASH', + prehash_value: 'SHA256', + }, + max_depth: 0, + min_depth: 0, + inner_spec: { + hash: 'SHA256', + child_size: 32, + child_order: [0, 1], + empty_child: '', + max_prefix_length: 1, + min_prefix_length: 1, + }, + }, + ], + trust_level: { + numerator: '1', + denominator: '3', + }, + upgrade_path: ['upgrade', 'upgradedIBCState'], + frozen_height: { + revision_height: '0', + revision_number: '0', + }, + latest_height: { + revision_height: '5086858', + revision_number: '3', + }, + max_clock_drift: '40000000000', + trusting_period: '403200000000000', + unbonding_period: '604800000000000', + allow_update_after_expiry: true, + allow_update_after_misbehaviour: true, + }, + consensus_state: { + root: { + hash: '/iST1C+vhywD9qgGNqSQvs0NIPlQMqIDwgIJKMIylUI=', + }, + '@type': '/ibc.lightclients.tendermint.v1.ConsensusState', + timestamp: '2022-12-01T07:22:43.523Z', + next_validators_hash: + 'RY9Nf/qtdDMVQK7LMjoVgrS1CkZaEVj02CDlkdgzutM=', + }, + }, + }); + await this.crawlIbcTaoSerivce.handleNewIbcClient([events], trx); + const newClient = await IbcClient.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newClient.client_id).toEqual(events.attributes[0].value); + expect(newClient.client_type).toEqual(events.attributes[1].value); + expect(newClient.client_state).toEqual(events.content.client_state); + expect(newClient.consensus_state).toEqual(events.content.consensus_state); + expect(newClient.counterparty_chain_id).toEqual( + events.content.client_state.chain_id + ); + // await trx.rollback(); + }); + } + + @Test('Test handleNewIbcConnection') + async testHandleNewIbcConnection() { + await knex.transaction(async (trx) => { + const events = Event.fromJson({ + type: Event.EVENT_TYPE.CONNECTION_OPEN_ACK, + attributes: [ + { + value: 'connection-72', + key: 'connection_id', + event_id: '1', + }, + { + value: '07-tendermint-21', + key: 'client_id', + event_id: '1', + }, + { + value: '07-tendermint-45', + key: 'counterparty_client_id', + event_id: '1', + }, + { + value: 'connection-27', + key: 'counterparty_connection_id', + event_id: '1', + }, + ], + content: { + '@type': '/ibc.core.connection.v1.MsgConnectionOpenAck', + signer: 'aura1gypt2w7xg5t9yr76hx6zemwd4xv72jckk03r6t', + version: { + features: ['ORDER_ORDERED', 'ORDER_UNORDERED'], + identifier: '1', + }, + proof_try: + 'CsQICsEIChljb25uZWN0aW9ucy9jb25uZWN0aW9uLTI3EmMKEDA3LXRlbmRlcm1pbnQtNDUSIwoBMRINT1JERVJfT1JERVJFRBIPT1JERVJfVU5PUkRFUkVEGAIiKAoQMDctdGVuZGVybWludC04MBINY29ubmVjdGlvbi03MhoFCgNpYmMaDggBGAEgASoGAAKa8pYEIi4IARIHAgSa8pYEIBohIB/PBZ4uljQRWTUoJWTvlXAf+Sf3geF7LsWKI7+WpaUwIiwIARIoBAia8pYEIEIyNKRwbo7UewNW1Bg6gWp+7QriRoCvyv6ZbKjZHs9wICIsCAESKAYMmvKWBCCeMewQOVwuaeBrU7LaB5kuNW/4JAlZjbrMXHi+1d1mlSAiLAgBEigIGprylgQg3u9esjU846jXfzCL7RGmbCTrgcWgML7vMgDjjXy8LkUgIi4IARIHCiaa8pYEIBohIDGrQ5aJZsN6Ut9GQ3wNVnStZ8Dr7mrIPRFRG/Zv3Z5CIiwIARIoDDia8pYEIHbOF4x7UN1YqA+xoo7KL7w0/rXENvBouFTaVqaJPA7gICIsCAESKA5gmvKWBCA9QpxrAfbqqlM63etC3YtsbTzTJAWBnIGK430UOkRK2CAiLQgBEikQ0AGi8pYEICUsHnwDn1/px2K6DyMm9lISpOrK4TNSLLpbJKbmXp6OICItCAESKRKuA6LylgQgXH14FUNckJ7q6yDzHxVWwkawlgmkdaa9650oE2k2OjsgIi0IARIpFKYFovKWBCAkaa0OlC4pKo4m2xRQeVbx0tieLhfak6Xsu5QVp9cXCCAiLQgBEikWxAqi8pYEIIOT6BYqI5lA+xTBTIRO+dPHH+MnF/7FXoFVz1/cEdVoICItCAESKRj0D6LylgQgxiwkb7P1fNqTdZUSduYl+OfDeXURD69QiFtfjNelu5UgIi0IARIpGqodovKWBCAuUcxazpKF/ptiPz3WEHJuhQNHrYxXUohbInjviGhzCCAiLQgBEikc2F6i8pYEIGlpNHXMBW35F/0aor1DBEL5XBEKxhQigsCTP1pmNykLICIuCAESKh7YlAGi8pYEINa1ZObRgViKtwVE+LVx59w3/oF2pnB0sjlIYyPhwFu2ICIuCAESKiDY7AKi8pYEIPFQOkaEFsZuc5VCL3XLMvndPVA1pCpQVUqq1MAtCRxOICIuCAESKiLq7wWi8pYEIG+vkeRtqUosbXbXctdffMe0oJ0VwyLp+b6J21UuXLBOICIuCAESKibe5Rai8pYEIMdjCIRMYnQf4Nph+jj/sgQbJOO/p/fXqVBXy3hKUr7TICIwCAESCSr+mCai8pYEIBohIKJhx7HKM0nG82d3MAl9cguF5EaDS8VfAQ6E2jwPqc88Ii4IARIqLNiOQaLylgQgZGldPsQJXlzRlduhZIdTRH4SHfo5Xbh/ouka5hktIfcgCv4BCvsBCgNpYmMSIAD8TsE9shScVyP/CHb+hr1siAWPBMHaduu4dWdgj1rBGgkIARgBIAEqAQAiJwgBEgEBGiD5Vt5/BBmvQQnMGXTh8TcjCr0rjLn1J3de7MoaiBcPIiIlCAESIQEvzAtKPUXmsWa73ufs4PS4NzfxMUJ/f7tJz7zRzeuU8CInCAESAQEaIAeJGXGcGsRVvSn5H69yS0J+ru1UFKAa8tkcM2iQ3uOEIiUIARIhAWVN4MYZgTIpdDlaD9rFq8YDvzV9R+cVZEWff23oGt7iIicIARIBARogbDU0NT+iBtS39EPHJu5GTrbal9trUT5GhRdwLYLr+WQ=', + client_state: { + '@type': '/ibc.lightclients.tendermint.v1.ClientState', + chain_id: 'euphoria-2', + proof_specs: [ + { + leaf_spec: { + hash: 'SHA256', + length: 'VAR_PROTO', + prefix: 'AA==', + prehash_key: 'NO_HASH', + prehash_value: 'SHA256', + }, + max_depth: 0, + min_depth: 0, + inner_spec: { + hash: 'SHA256', + child_size: 33, + child_order: [0, 1], + empty_child: '', + max_prefix_length: 12, + min_prefix_length: 4, + }, + }, + { + leaf_spec: { + hash: 'SHA256', + length: 'VAR_PROTO', + prefix: 'AA==', + prehash_key: 'NO_HASH', + prehash_value: 'SHA256', + }, + max_depth: 0, + min_depth: 0, + inner_spec: { + hash: 'SHA256', + child_size: 32, + child_order: [0, 1], + empty_child: '', + max_prefix_length: 1, + min_prefix_length: 1, + }, + }, + ], + trust_level: { + numerator: '1', + denominator: '3', + }, + upgrade_path: ['upgrade', 'upgradedIBCState'], + frozen_height: { + revision_height: '0', + revision_number: '0', + }, + latest_height: { + revision_height: '5744576', + revision_number: '2', + }, + max_clock_drift: '20000000000', + trusting_period: '115200000000000', + unbonding_period: '172800000000000', + allow_update_after_expiry: true, + allow_update_after_misbehaviour: true, + }, + proof_client: + 'CqMJCqAJCiRjbGllbnRzLzA3LXRlbmRlcm1pbnQtNDUvY2xpZW50U3RhdGUSsgEKKy9pYmMubGlnaHRjbGllbnRzLnRlbmRlcm1pbnQudjEuQ2xpZW50U3RhdGUSggEKCmV1cGhvcmlhLTISBAgBEAMaBAiAhAciBAiAxgoqAggUMgA6BwgCEMDP3gJCGQoJCAEYASABKgEAEgwKAgABECEYBCAMMAFCGQoJCAEYASABKgEAEgwKAgABECAYASABMAFKB3VwZ3JhZGVKEHVwZ3JhZGVkSUJDU3RhdGVQAVgBGg4IARgBIAEqBgACovKWBCIuCAESBwIEovKWBCAaISBAlbewzvl2PjgUtyznqYRFOiNhxJp+KAq2YmJaxmAAsiIsCAESKAQGovKWBCDjWwUrygy8PnWb8yoN4Xwomockb9eYporo/dYJRHBDziAiLAgBEigGDqLylgQgjCxek5E7g4E/jZ/z9ayx2Rz8Wlwd5SourZeBm+l7ORkgIiwIARIoCByi8pYEIBzZYxppGpUUOli49o63SLSJv24Pv0bfhHrQL4/b7xR9ICIuCAESBwo0ovKWBCAaISCDdwKGH9QJ5iwCx763LCpf7/QE2iVdkRd52M3o9zDyOSIuCAESBwxGovKWBCAaISDhzb/9iqmZ90/la2ecG8xfrP1g49sNRKF7eHRoZ41hQyIsCAESKA5wovKWBCBbVdBjKBpebgwZFVLIitdMRdO0N0BkfjDLFp9EJMqrNCAiLwgBEggQ0AGi8pYEIBohIJfTNDSJ0SRz/JI1ivIeLCdm3ejk0yl2A7EwNe9AfAYZIi0IARIpEq4DovKWBCBcfXgVQ1yQnurrIPMfFVbCRrCWCaR1pr3rnSgTaTY6OyAiLQgBEikUpgWi8pYEICRprQ6ULikqjibbFFB5VvHS2J4uF9qTpey7lBWn1xcIICItCAESKRbECqLylgQgg5PoFiojmUD7FMFMhE7508cf4ycX/sVegVXPX9wR1WggIi0IARIpGPQPovKWBCDGLCRvs/V82pN1lRJ25iX458N5dREPr1CIW1+M16W7lSAiLQgBEikaqh2i8pYEIC5RzFrOkoX+m2I/PdYQcm6FA0etjFdSiFsieO+IaHMIICItCAESKRzYXqLylgQgaWk0dcwFbfkX/RqivUMEQvlcEQrGFCKCwJM/WmY3KQsgIi4IARIqHtiUAaLylgQg1rVk5tGBWIq3BUT4tXHn3Df+gXamcHSyOUhjI+HAW7YgIi4IARIqINjsAqLylgQg8VA6RoQWxm5zlUIvdcsy+d09UDWkKlBVSqrUwC0JHE4gIi4IARIqIurvBaLylgQgb6+R5G2pSixtdtdy1198x7SgnRXDIun5vonbVS5csE4gIi4IARIqJt7lFqLylgQgx2MIhExidB/g2mH6OP+yBBsk47+n99epUFfLeEpSvtMgIjAIARIJKv6YJqLylgQgGiEgomHHscozScbzZ3cwCX1yC4XkRoNLxV8BDoTaPA+pzzwiLggBEios2I5BovKWBCBkaV0+xAleXNGV26Fkh1NEfhId+jlduH+i6RrmGS0h9yAK/gEK+wEKA2liYxIgAPxOwT2yFJxXI/8Idv6GvWyIBY8Ewdp267h1Z2CPWsEaCQgBGAEgASoBACInCAESAQEaIPlW3n8EGa9BCcwZdOHxNyMKvSuMufUnd17syhqIFw8iIiUIARIhAS/MC0o9ReaxZrve5+zg9Lg3N/ExQn9/u0nPvNHN65TwIicIARIBARogB4kZcZwaxFW9Kfkfr3JLQn6u7VQUoBry2RwzaJDe44QiJQgBEiEBZU3gxhmBMil0OVoP2sWrxgO/NX1H5xVkRZ9/bega3uIiJwgBEgEBGiBsNTQ1P6IG1Lf0Q8cm7kZOttqX22tRPkaFF3Atguv5ZA==', + proof_height: { + revision_height: '4381842', + revision_number: '0', + }, + connection_id: 'connection-72', + proof_consensus: + 'CtUICtIICjJjbGllbnRzLzA3LXRlbmRlcm1pbnQtNDUvY29uc2Vuc3VzU3RhdGVzLzItNTc0NDU3NhKGAQouL2liYy5saWdodGNsaWVudHMudGVuZGVybWludC52MS5Db25zZW5zdXNTdGF0ZRJUCgwIn4D+pQYQ9PKBuAMSIgogtPl77yi9LvzW1gkeO+teMVcM1E6qcv7Xp3okSVvtGAcaIMe9/GKdHdD4dPFEauon+h3QEdH1oZ6jP4DEH2ZWn3ROGg4IARgBIAEqBgACovKWBCIsCAESKAQGovKWBCAHjrwjqZJaxGGLEXaH55ia064+0SLWxo3XLQqnH3PnDSAiLAgBEigGDqLylgQgWMaksHVViO8rzGDk6rbntj3jGE+rHofgIGu7zk8ojukgIi4IARIHCBii8pYEIBohIJgUvjsUDyjaulIls+YyOVYO793IQ59F4DtEoCEKLZTfIiwIARIoCjSi8pYEIEzSRrihNkes+Q5IuNa5gpyhkva9yby1DMC1p8VrYlUUICIuCAESBwxGovKWBCAaISDhzb/9iqmZ90/la2ecG8xfrP1g49sNRKF7eHRoZ41hQyIsCAESKA5wovKWBCBbVdBjKBpebgwZFVLIitdMRdO0N0BkfjDLFp9EJMqrNCAiLwgBEggQ0AGi8pYEIBohIJfTNDSJ0SRz/JI1ivIeLCdm3ejk0yl2A7EwNe9AfAYZIi0IARIpEq4DovKWBCBcfXgVQ1yQnurrIPMfFVbCRrCWCaR1pr3rnSgTaTY6OyAiLQgBEikUpgWi8pYEICRprQ6ULikqjibbFFB5VvHS2J4uF9qTpey7lBWn1xcIICItCAESKRbECqLylgQgg5PoFiojmUD7FMFMhE7508cf4ycX/sVegVXPX9wR1WggIi0IARIpGPQPovKWBCDGLCRvs/V82pN1lRJ25iX458N5dREPr1CIW1+M16W7lSAiLQgBEikaqh2i8pYEIC5RzFrOkoX+m2I/PdYQcm6FA0etjFdSiFsieO+IaHMIICItCAESKRzYXqLylgQgaWk0dcwFbfkX/RqivUMEQvlcEQrGFCKCwJM/WmY3KQsgIi4IARIqHtiUAaLylgQg1rVk5tGBWIq3BUT4tXHn3Df+gXamcHSyOUhjI+HAW7YgIi4IARIqINjsAqLylgQg8VA6RoQWxm5zlUIvdcsy+d09UDWkKlBVSqrUwC0JHE4gIi4IARIqIurvBaLylgQgb6+R5G2pSixtdtdy1198x7SgnRXDIun5vonbVS5csE4gIi4IARIqJt7lFqLylgQgx2MIhExidB/g2mH6OP+yBBsk47+n99epUFfLeEpSvtMgIjAIARIJKv6YJqLylgQgGiEgomHHscozScbzZ3cwCX1yC4XkRoNLxV8BDoTaPA+pzzwiLggBEios2I5BovKWBCBkaV0+xAleXNGV26Fkh1NEfhId+jlduH+i6RrmGS0h9yAK/gEK+wEKA2liYxIgAPxOwT2yFJxXI/8Idv6GvWyIBY8Ewdp267h1Z2CPWsEaCQgBGAEgASoBACInCAESAQEaIPlW3n8EGa9BCcwZdOHxNyMKvSuMufUnd17syhqIFw8iIiUIARIhAS/MC0o9ReaxZrve5+zg9Lg3N/ExQn9/u0nPvNHN65TwIicIARIBARogB4kZcZwaxFW9Kfkfr3JLQn6u7VQUoBry2RwzaJDe44QiJQgBEiEBZU3gxhmBMil0OVoP2sWrxgO/NX1H5xVkRZ9/bega3uIiJwgBEgEBGiBsNTQ1P6IG1Lf0Q8cm7kZOttqX22tRPkaFF3Atguv5ZA==', + consensus_height: { + revision_height: '5744576', + revision_number: '2', + }, + counterparty_connection_id: 'connection-27', + }, + }); + await this.crawlIbcTaoSerivce.handleNewIbcConnection([events], trx); + const newConnection = await IbcConnection.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newConnection.connection_id).toEqual(events.attributes[0].value); + expect(newConnection.counterparty_client_id).toEqual( + events.attributes[2].value + ); + expect(newConnection.counterparty_connection_id).toEqual( + events.attributes[3].value + ); + }); + } + + @Test('Test handleNewIbcChannel') + async testHandleNewIbcChannel() { + await knex.transaction(async (trx) => { + const events = Event.fromJson({ + type: Event.EVENT_TYPE.CHANNEL_OPEN_ACK, + attributes: [ + { + value: + 'wasm.aura1s42mq5xz5et3fhs0cxvf8ds6vmy5u6d27u23ydq28r2hgmednw5s3u7kjf', + key: 'port_id', + event_id: '1', + }, + { + value: 'channel-79', + key: 'channel_id', + event_id: '1', + }, + { + value: + 'wasm.nois1xwde9rzqk5u36fke0r9ddmtwvh43n4fv53c5vc462wz8xlnqjhls6d90xc', + key: 'counterparty_port_id', + event_id: '1', + }, + { + value: 'channel-44', + key: 'counterparty_channel_id', + event_id: '1', + }, + { + value: 'connection-72', + key: 'connection_id', + event_id: '1', + }, + ], + content: { + '@type': '/ibc.core.channel.v1.MsgChannelOpenAck', + signer: 'aura1teguu4gyk002q74rdw8xk6wxz663d3e0da44vv', + port_id: + 'wasm.aura1s42mq5xz5et3fhs0cxvf8ds6vmy5u6d27u23ydq28r2hgmednw5s3u7kjf', + proof_try: + 'CqsJCqgJCmpjaGFubmVsRW5kcy9wb3J0cy93YXNtLm5vaXMxeHdkZTlyenFrNXUzNmZrZTByOWRkbXR3dmg0M240ZnY1M2M1dmM0NjJ3ejh4bG5xamhsczZkOTB4Yy9jaGFubmVscy9jaGFubmVsLTQ0EnAIAhABGlIKRHdhc20uYXVyYTFzNDJtcTV4ejVldDNmaHMwY3h2ZjhkczZ2bXk1dTZkMjd1MjN5ZHEyOHIyaGdtZWRudzVzM3U3a2pmEgpjaGFubmVsLTc5Ig1jb25uZWN0aW9uLTIwKgdub2lzLXY3Gg4IARgBIAEqBgACqNbwAyIsCAESKAIEqNbwAyApVTv4ZHdG03r0dv2FLRgljarFXhPCzLxe5U794Dxg7iAiLAgBEigEBqjW8AMgbTQCakVAjVARrysKAAPM1e1tiTHofZa81TN6VdrZryMgIiwIARIoBgqo1vADIH+PzOA8CN5w3mj/yRWJffIKzoh1l9E7sKZ5XNBpk36XICIuCAESBwgQqNbwAyAaISDJYlIFbAyC1KK1ZZBsZZFdBLoWBTAPcvdp1IOv5E9HLSIuCAESBwowqNbwAyAaISCEaKaAA8yFOHGODccYN+9HuImlZxYWqoTxIN/J8kBiayIsCAESKAxUqNbwAyD3SAJRP1mO4LVWbHvRyD2webWn9TkNbkJZy7jIPzhrMiAiLwgBEggO1AGo1vADIBohICKuXv3N+wd3GBXhJZ27wd4v6vIL8XZUP8OoZE/aOUysIi0IARIpEO4CqNbwAyBgaG7cgNsElUcgrf9Mp2Gmzo0/AsmRHPw5FTwX9Kr0MSAiLQgBEikSqASo1vADIFQDyXDfKzS3OL1ZkYfELpPLhFtz8T5WII7GUvojwrwsICItCAESKRScBqjW8AMgfo0r4iAG6Q26x4Y6zuXHZJc5TgywPhYCp408vX7zDBQgIi0IARIpFtIMqNbwAyDqu2q/0ILlZYXdrphZJmwZtcf8/aHxsqR3dX84m9TTeiAiLQgBEikYgBqo1vADIIPtczfmGBBRreb3CDJB7Wm0ebRH+HR88bBCcXaUupjcICIvCAESCBrAJ6jW8AMgGiEgcpa1SruIq6LeCV4xG7rX2arPRTP/iEbXQ8D5WnWUzwAiLwgBEggc1mGo1vADIBohIDXxpaT6Ugv6U6oOS5S0EDChcoYZW0+my6CUTsXNzvvGIi4IARIqHoqRAajW8AMgBRWQCNEouEeVb1Jgy9Tt+qq891z0wHVNBzVj20RLaGggIi4IARIqItjHAqjW8AMgUvOkgI+dIkIugdSRweG9eleBh/pswvTETxdw6FY4uTwgIjAIARIJJPDCDajW8AMgGiEgyhYnMC8oHxxof8aeyKInF9dJIbxw7e5YkEFV7dhRRWMiLggBEiom8rYSqNbwAyD0JrFmOhwOvx0p9bY869JVWAT2YOnKOMAZ2aBBy0VO/CAiLggBEioosvQZqNbwAyCz9+zU1Wu3buyW7azM+yVxZPfqMxKFHWdwLoOIS5JhYyAiMAgBEgkq9oM/qNbwAyAaISAPNNwvlr7Cghq7eoYfeWqJEKPOMwjXnB66fWb3O1uckQr+AQr7AQoDaWJjEiDfSyEEYUNHoexjzxEoS8ca//0GRWkbX/BhVhlfyDT5zBoJCAEYASABKgEAIicIARIBARog+VbefwQZr0EJzBl04fE3Iwq9K4y59Sd3XuzKGogXDyIiJQgBEiEBL8wLSj1F5rFmu97n7OD0uDc38TFCf3+7Sc+80c3rlPAiJwgBEgEBGiDFznslp7K5oVZJG04oLeQXNU3ws7wFeEH4qB+XS4Q9zyIlCAESIQEtpn507xvLNnr4cRXICIMm+qvT86qeswagi/7S7aB1riInCAESAQEaILASAaS4z/TvmnniGlkXMUOeTYKHWs4RWU6MlCMFBTA3', + channel_id: 'channel-79', + proof_height: { + revision_height: '4068757', + revision_number: '0', + }, + counterparty_version: 'nois-v7', + counterparty_channel_id: 'channel-44', + }, + }); + await this.crawlIbcTaoSerivce.handleNewIbcChannel([events], trx); + const newChannel = await IbcChannel.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newChannel.channel_id).toEqual(events.attributes[1].value); + expect(newChannel.counterparty_channel_id).toEqual( + events.attributes[3].value + ); + expect(newChannel.counterparty_port_id).toEqual( + events.attributes[2].value + ); + expect(newChannel.port_id).toEqual(events.attributes[0].value); + expect(newChannel.state).toEqual(IbcChannel.STATUS.OPEN); + }); + } + + @Test('Test handleCloseIbcChannel') + async testHandleCloseIbcChannel() { + await knex.transaction(async (trx) => { + const events = Event.fromJson({ + type: Event.EVENT_TYPE.CHANNEL_CLOSE_CONFIRM, + attributes: [ + { + value: + 'wasm.aura1s42mq5xz5et3fhs0cxvf8ds6vmy5u6d27u23ydq28r2hgmednw5s3u7kjf', + key: 'port_id', + event_id: '1', + }, + { + value: 'channel-79', + key: 'channel_id', + event_id: '1', + }, + { + value: + 'wasm.nois1xwde9rzqk5u36fke0r9ddmtwvh43n4fv53c5vc462wz8xlnqjhls6d90xc', + key: 'counterparty_port_id', + event_id: '1', + }, + { + value: 'channel-44', + key: 'counterparty_channel_id', + event_id: '1', + }, + { + value: 'connection-72', + key: 'connection_id', + event_id: '1', + }, + ], + content: { + '@type': '/ibc.core.channel.v1.MsgChannelOpenAck', + signer: 'aura1teguu4gyk002q74rdw8xk6wxz663d3e0da44vv', + port_id: + 'wasm.aura1s42mq5xz5et3fhs0cxvf8ds6vmy5u6d27u23ydq28r2hgmednw5s3u7kjf', + proof_try: + 'CqsJCqgJCmpjaGFubmVsRW5kcy9wb3J0cy93YXNtLm5vaXMxeHdkZTlyenFrNXUzNmZrZTByOWRkbXR3dmg0M240ZnY1M2M1dmM0NjJ3ejh4bG5xamhsczZkOTB4Yy9jaGFubmVscy9jaGFubmVsLTQ0EnAIAhABGlIKRHdhc20uYXVyYTFzNDJtcTV4ejVldDNmaHMwY3h2ZjhkczZ2bXk1dTZkMjd1MjN5ZHEyOHIyaGdtZWRudzVzM3U3a2pmEgpjaGFubmVsLTc5Ig1jb25uZWN0aW9uLTIwKgdub2lzLXY3Gg4IARgBIAEqBgACqNbwAyIsCAESKAIEqNbwAyApVTv4ZHdG03r0dv2FLRgljarFXhPCzLxe5U794Dxg7iAiLAgBEigEBqjW8AMgbTQCakVAjVARrysKAAPM1e1tiTHofZa81TN6VdrZryMgIiwIARIoBgqo1vADIH+PzOA8CN5w3mj/yRWJffIKzoh1l9E7sKZ5XNBpk36XICIuCAESBwgQqNbwAyAaISDJYlIFbAyC1KK1ZZBsZZFdBLoWBTAPcvdp1IOv5E9HLSIuCAESBwowqNbwAyAaISCEaKaAA8yFOHGODccYN+9HuImlZxYWqoTxIN/J8kBiayIsCAESKAxUqNbwAyD3SAJRP1mO4LVWbHvRyD2webWn9TkNbkJZy7jIPzhrMiAiLwgBEggO1AGo1vADIBohICKuXv3N+wd3GBXhJZ27wd4v6vIL8XZUP8OoZE/aOUysIi0IARIpEO4CqNbwAyBgaG7cgNsElUcgrf9Mp2Gmzo0/AsmRHPw5FTwX9Kr0MSAiLQgBEikSqASo1vADIFQDyXDfKzS3OL1ZkYfELpPLhFtz8T5WII7GUvojwrwsICItCAESKRScBqjW8AMgfo0r4iAG6Q26x4Y6zuXHZJc5TgywPhYCp408vX7zDBQgIi0IARIpFtIMqNbwAyDqu2q/0ILlZYXdrphZJmwZtcf8/aHxsqR3dX84m9TTeiAiLQgBEikYgBqo1vADIIPtczfmGBBRreb3CDJB7Wm0ebRH+HR88bBCcXaUupjcICIvCAESCBrAJ6jW8AMgGiEgcpa1SruIq6LeCV4xG7rX2arPRTP/iEbXQ8D5WnWUzwAiLwgBEggc1mGo1vADIBohIDXxpaT6Ugv6U6oOS5S0EDChcoYZW0+my6CUTsXNzvvGIi4IARIqHoqRAajW8AMgBRWQCNEouEeVb1Jgy9Tt+qq891z0wHVNBzVj20RLaGggIi4IARIqItjHAqjW8AMgUvOkgI+dIkIugdSRweG9eleBh/pswvTETxdw6FY4uTwgIjAIARIJJPDCDajW8AMgGiEgyhYnMC8oHxxof8aeyKInF9dJIbxw7e5YkEFV7dhRRWMiLggBEiom8rYSqNbwAyD0JrFmOhwOvx0p9bY869JVWAT2YOnKOMAZ2aBBy0VO/CAiLggBEioosvQZqNbwAyCz9+zU1Wu3buyW7azM+yVxZPfqMxKFHWdwLoOIS5JhYyAiMAgBEgkq9oM/qNbwAyAaISAPNNwvlr7Cghq7eoYfeWqJEKPOMwjXnB66fWb3O1uckQr+AQr7AQoDaWJjEiDfSyEEYUNHoexjzxEoS8ca//0GRWkbX/BhVhlfyDT5zBoJCAEYASABKgEAIicIARIBARog+VbefwQZr0EJzBl04fE3Iwq9K4y59Sd3XuzKGogXDyIiJQgBEiEBL8wLSj1F5rFmu97n7OD0uDc38TFCf3+7Sc+80c3rlPAiJwgBEgEBGiDFznslp7K5oVZJG04oLeQXNU3ws7wFeEH4qB+XS4Q9zyIlCAESIQEtpn507xvLNnr4cRXICIMm+qvT86qeswagi/7S7aB1riInCAESAQEaILASAaS4z/TvmnniGlkXMUOeTYKHWs4RWU6MlCMFBTA3', + channel_id: 'channel-79', + proof_height: { + revision_height: '4068757', + revision_number: '0', + }, + counterparty_version: 'nois-v7', + counterparty_channel_id: 'channel-44', + }, + }); + await this.crawlIbcTaoSerivce.handleCloseIbcChannel([events], trx); + const newChannel = await IbcChannel.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newChannel.state).toEqual(IbcChannel.STATUS.CLOSE); + }); + } +} From 6fd8005890c3f92d55d9714bc50d05a2325e4388 Mon Sep 17 00:00:00 2001 From: Phan Anh Tuan Date: Thu, 3 Aug 2023 16:55:08 +0700 Subject: [PATCH 06/54] feat: add test for unrecognized validator --- .../crawl_validator.service.ts | 8 ++-- .../crawl-validator/crawl_validator.spec.ts | 47 +++++++++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/services/crawl-validator/crawl_validator.service.ts b/src/services/crawl-validator/crawl_validator.service.ts index 6e0cbe132..1a8412ef8 100644 --- a/src/services/crawl-validator/crawl_validator.service.ts +++ b/src/services/crawl-validator/crawl_validator.service.ts @@ -70,7 +70,6 @@ export default class CrawlValidatorService extends BullableService { .select('value') .limit(1) .offset(0); - await knex.transaction(async (trx) => { if (resultTx.length > 0) { await this.updateValidators(trx); @@ -110,7 +109,9 @@ export default class CrawlValidatorService extends BullableService { } } - const validatorInDB: Validator[] = await knex('validator').select('*'); + const validatorInDB: Validator[] = await knex('validator') + .select('*') + .whereNot('status', Validator.STATUS.UNRECOGNIZED); const offchainMapped: Map = new Map(); await Promise.all( validators.map(async (validator) => { @@ -149,7 +150,7 @@ export default class CrawlValidatorService extends BullableService { updateValidators = await this.loadCustomInfo(updateValidators); - // loop all validator not found onchain, update status is UNSPECIFIED + // loop all validator not found onchain, update status is UNRECOGNIZED validatorInDB .filter((val: any) => !offchainMapped.get(val.operator_address)) .forEach(async (validatorNotOnchain: any) => { @@ -160,6 +161,7 @@ export default class CrawlValidatorService extends BullableService { validatorNotOnchain.status = Validator.STATUS.UNRECOGNIZED; validatorNotOnchain.tokens = 0; validatorNotOnchain.delegator_shares = 0; + validatorNotOnchain.percent_voting_power = 0; validatorNotOnchain.jailed_until = validatorNotOnchain.jailed_until.toISOString(); diff --git a/test/unit/services/crawl-validator/crawl_validator.spec.ts b/test/unit/services/crawl-validator/crawl_validator.spec.ts index fccb4be8f..ebc87dd91 100644 --- a/test/unit/services/crawl-validator/crawl_validator.spec.ts +++ b/test/unit/services/crawl-validator/crawl_validator.spec.ts @@ -1,4 +1,4 @@ -import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { AfterEach, BeforeEach, Describe, Test } from '@jest-decorated/core'; import { ServiceBroker } from 'moleculer'; import { BULL_JOB_NAME } from '../../../../src/common'; import { @@ -75,7 +75,7 @@ export default class CrawlValidatorTest { crawlSigningInfoService?: CrawlSigningInfoService; - @BeforeAll() + @BeforeEach() async initSuite() { await this.broker.start(); this.crawlSigningInfoService = this.broker.createService( @@ -97,7 +97,7 @@ export default class CrawlValidatorTest { await BlockCheckpoint.query().insert(this.blockCheckpoint); } - @AfterAll() + @AfterEach() async tearDown() { await Promise.all([ Validator.query().delete(true), @@ -128,4 +128,45 @@ export default class CrawlValidatorTest { )?.consensus_address ).toEqual('auravalcons1rvq6km74pua3pt9g7u5svm4r6mrw8z08walfep'); } + + @Test('Set validator not found onchain is UNRECOGNIZED') + public async testCrawlValidatorNotFoundOnchain() { + await Validator.query().insert( + Validator.fromJson({ + operator_address: 'xxx', + account_address: 'xxx', + consensus_address: 'xxx', + consensus_hex_address: 'xxx', + consensus_pubkey: {}, + jailed: false, + status: Validator.STATUS.UNBONDED, + tokens: 100, + delegator_shares: 100, + description: {}, + unbonding_height: 0, + unbonding_time: '1970-01-01 00:00:00+00', + commission: {}, + min_self_delegation: 0, + uptime: 0, + self_delegation_balance: 0, + percent_voting_power: 100, + start_height: 0, + index_offset: 0, + jailed_until: '1970-01-01 00:00:00+00', + tombstoned: false, + missed_blocks_counter: 0, + delegators_count: 0, + delegators_last_height: 0, + image_url: 'xxx', + }) + ); + + await this.crawlValidatorService?.handleCrawlAllValidator({}); + + const validator = await Validator.query().findOne({ + operator_address: 'xxx', + }); + expect(validator?.status).toEqual(Validator.STATUS.UNRECOGNIZED); + expect(validator?.tokens).toEqual('0'); + } } From 6db399019b325d57bdec31af57f55655065c30af Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 4 Aug 2023 14:56:36 +0700 Subject: [PATCH 07/54] feat: crawl genesis ibc tao --- src/common/constant.ts | 1 + .../crawl-genesis/crawl_genesis.service.ts | 102 ++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/src/common/constant.ts b/src/common/constant.ts index bcedb6638..6ff1f2597 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -67,6 +67,7 @@ export const BULL_JOB_NAME = { HANDLE_MIGRATE_CONTRACT: 'handle:migrate-contract', JOB_REDECODE_TX: 'job:redecode-tx', CRAWL_IBC_TAO: 'crawl:ibc-tao', + CRAWL_GENESIS_IBC_TAO: 'crawl:genesis-ibc-tao', }; export const SERVICE = { diff --git a/src/services/crawl-genesis/crawl_genesis.service.ts b/src/services/crawl-genesis/crawl_genesis.service.ts index 03824afea..d8124d41b 100644 --- a/src/services/crawl-genesis/crawl_genesis.service.ts +++ b/src/services/crawl-genesis/crawl_genesis.service.ts @@ -22,6 +22,9 @@ import { SmartContract, Validator, Feegrant, + IbcClient, + IbcConnection, + IbcChannel, } from '../../models'; import BullableService, { QueueHandler } from '../../base/bullable.service'; import { @@ -669,6 +672,105 @@ export default class CrawlGenesisService extends BullableService { await this.terminateProcess(); } + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO, + jobName: BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async crawlGenesisIbcTao(_payload: object): Promise { + this.logger.info('Crawl genesis Ibc Tao'); + + const genesisProcess = await this.checkGenesisJobProcess( + BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO + ); + if (genesisProcess !== 0) return; + await knex + .transaction(async (trx) => { + // crawl genesis ibc client + const genClients: any[] = await this.readStreamGenesis( + 'app_state.ibc.client_genesis.clients' + ); + const genClientsConsensus: any[] = await this.readStreamGenesis( + 'app_state.ibc.client_genesis.clients_consensus' + ); + const ibcClients: IbcClient[] = genClients.map((genClient: any) => { + const consensusStates = genClientsConsensus.find( + (clientConsensus) => + clientConsensus.client_id === genClient.client_id + ); + return IbcClient.fromJson({ + client_id: genClient.client_id, + counterparty_chain_id: genClient.client_state.chain_id, + client_state: genClient.client_state, + consensus_state: + consensusStates.consensus_states[ + consensusStates.consensus_states.length - 1 + ], + client_type: genClient.client_id.substring( + 0, + genClient.client_id.lastIndexOf('-') + ), + }); + }); + const newClients = await IbcClient.query() + .insert(ibcClients) + .transacting(trx); + this.logger.info('Done client'); + // crawl genesis ibc connections + const genConnections: any[] = await this.readStreamGenesis( + 'app_state.ibc.connection_genesis.connections' + ); + const ibcConnections: IbcConnection[] = genConnections.map( + (genConnection: any) => IbcConnection.fromJson({ + ibc_client_id: newClients.find( + (client) => client.client_id === genConnection.client_id + )?.id, + connection_id: genConnection.id, + counterparty_client_id: genConnection.counterparty.client_id, + counterparty_connection_id: + genConnection.counterparty.connection_id, + }) + ); + const newConnections = await IbcConnection.query() + .insert(ibcConnections) + .transacting(trx); + this.logger.info('Done connections'); + // crawl genesis ibc channels + const genChannels: any[] = await this.readStreamGenesis( + 'app_state.ibc.channel_genesis.channels' + ); + const IbcChannels: IbcChannel[] = genChannels.map((genChannel: any) => IbcChannel.fromJson({ + ibc_connection_id: newConnections.find( + (connection) => + connection.connection_id === genChannel.connection_hops[0] + )?.id, + channel_id: genChannel.channel_id, + port_id: genChannel.port_id, + counterparty_port_id: genChannel.counterparty.port_id, + counterparty_channel_id: genChannel.counterparty.channel_id, + state: genChannel.state, + })); + await IbcChannel.query().insert(IbcChannels).transacting(trx); + this.logger.info('Done channel'); + await BlockCheckpoint.query() + .insert( + BlockCheckpoint.fromJson({ + job_name: BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO, + height: 1, + }) + ) + .onConflict('job_name') + .merge() + .returning('id') + .transacting(trx); + }) + .catch((error) => { + this.logger.error(error); + throw error; + }); + await this.terminateProcess(); + } + private async handleIbcDenom(accounts: Account[]): Promise { if (accounts.length === 0) return []; From d8eb803a649473c64c91f882815094e2c96d0bf7 Mon Sep 17 00:00:00 2001 From: Phan Anh Tuan Date: Mon, 7 Aug 2023 09:10:58 +0700 Subject: [PATCH 08/54] feat: add migration to add index time to block, tx --- .../20230807020810_add_time_index_block_tx.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 migrations/20230807020810_add_time_index_block_tx.ts diff --git a/migrations/20230807020810_add_time_index_block_tx.ts b/migrations/20230807020810_add_time_index_block_tx.ts new file mode 100644 index 000000000..b197a6741 --- /dev/null +++ b/migrations/20230807020810_add_time_index_block_tx.ts @@ -0,0 +1,19 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable('block', (table) => { + table.index('time'); + }); + await knex.schema.alterTable('transaction', (table) => { + table.index('timestamp'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable('block', (table) => { + table.dropIndex('time'); + }); + await knex.schema.alterTable('transaction', (table) => { + table.dropIndex('timestamp'); + }); +} From 928e7b97aefe549f9fdd503e941fa8ad879e6455 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Mon, 7 Aug 2023 10:30:10 +0700 Subject: [PATCH 09/54] fix: code --- src/services/crawl-genesis/crawl_genesis.service.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/services/crawl-genesis/crawl_genesis.service.ts b/src/services/crawl-genesis/crawl_genesis.service.ts index d8124d41b..b64de776c 100644 --- a/src/services/crawl-genesis/crawl_genesis.service.ts +++ b/src/services/crawl-genesis/crawl_genesis.service.ts @@ -55,6 +55,7 @@ export default class CrawlGenesisService extends BullableService { BULL_JOB_NAME.CRAWL_GENESIS_CODE, BULL_JOB_NAME.CRAWL_GENESIS_CONTRACT, BULL_JOB_NAME.CRAWL_GENESIS_FEEGRANT, + BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO, ]; public constructor(public broker: ServiceBroker) { @@ -721,7 +722,8 @@ export default class CrawlGenesisService extends BullableService { 'app_state.ibc.connection_genesis.connections' ); const ibcConnections: IbcConnection[] = genConnections.map( - (genConnection: any) => IbcConnection.fromJson({ + (genConnection: any) => + IbcConnection.fromJson({ ibc_client_id: newClients.find( (client) => client.client_id === genConnection.client_id )?.id, @@ -739,7 +741,8 @@ export default class CrawlGenesisService extends BullableService { const genChannels: any[] = await this.readStreamGenesis( 'app_state.ibc.channel_genesis.channels' ); - const IbcChannels: IbcChannel[] = genChannels.map((genChannel: any) => IbcChannel.fromJson({ + const IbcChannels: IbcChannel[] = genChannels.map((genChannel: any) => + IbcChannel.fromJson({ ibc_connection_id: newConnections.find( (connection) => connection.connection_id === genChannel.connection_hops[0] @@ -749,7 +752,8 @@ export default class CrawlGenesisService extends BullableService { counterparty_port_id: genChannel.counterparty.port_id, counterparty_channel_id: genChannel.counterparty.channel_id, state: genChannel.state, - })); + }) + ); await IbcChannel.query().insert(IbcChannels).transacting(trx); this.logger.info('Done channel'); await BlockCheckpoint.query() From 454a38ac30ef1b07ea579077245963e1a8a32dd6 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Tue, 8 Aug 2023 16:54:53 +0700 Subject: [PATCH 10/54] fix: code --- .../crawl-genesis/crawl_genesis.service.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/services/crawl-genesis/crawl_genesis.service.ts b/src/services/crawl-genesis/crawl_genesis.service.ts index b64de776c..9a3465984 100644 --- a/src/services/crawl-genesis/crawl_genesis.service.ts +++ b/src/services/crawl-genesis/crawl_genesis.service.ts @@ -713,9 +713,12 @@ export default class CrawlGenesisService extends BullableService { ), }); }); - const newClients = await IbcClient.query() - .insert(ibcClients) - .transacting(trx); + let newClients: IbcClient[] = []; + if (ibcClients.length > 0) { + newClients = await IbcClient.query() + .insert(ibcClients) + .transacting(trx); + } this.logger.info('Done client'); // crawl genesis ibc connections const genConnections: any[] = await this.readStreamGenesis( @@ -733,9 +736,12 @@ export default class CrawlGenesisService extends BullableService { genConnection.counterparty.connection_id, }) ); - const newConnections = await IbcConnection.query() - .insert(ibcConnections) - .transacting(trx); + let newConnections: IbcConnection[] = []; + if (ibcConnections.length > 0) { + newConnections = await IbcConnection.query() + .insert(ibcConnections) + .transacting(trx); + } this.logger.info('Done connections'); // crawl genesis ibc channels const genChannels: any[] = await this.readStreamGenesis( @@ -754,7 +760,9 @@ export default class CrawlGenesisService extends BullableService { state: genChannel.state, }) ); - await IbcChannel.query().insert(IbcChannels).transacting(trx); + if (IbcChannels.length > 0) { + await IbcChannel.query().insert(IbcChannels).transacting(trx); + } this.logger.info('Done channel'); await BlockCheckpoint.query() .insert( From 2f4cc129bad5f6879f9752f6029d6ca8baba5814 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 11 Aug 2023 09:08:26 +0700 Subject: [PATCH 11/54] feat: ibc app --- config.json | 5 +++ .../20230810094108_create_ibc_tx_model.ts | 20 +++++++++++ src/common/constant.ts | 5 +++ src/services/ibc/crawl_ibc_app.service.ts | 34 +++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 migrations/20230810094108_create_ibc_tx_model.ts create mode 100644 src/services/ibc/crawl_ibc_app.service.ts diff --git a/config.json b/config.json index 87061ab13..03683bf05 100644 --- a/config.json +++ b/config.json @@ -166,5 +166,10 @@ "key": "crawlIbcTao", "millisecondRepeatJob": 2000, "blocksPerCall": 100 + }, + "crawlIbcApp": { + "key": "crawlIbcApp", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100 } } diff --git a/migrations/20230810094108_create_ibc_tx_model.ts b/migrations/20230810094108_create_ibc_tx_model.ts new file mode 100644 index 000000000..0bc1f0d20 --- /dev/null +++ b/migrations/20230810094108_create_ibc_tx_model.ts @@ -0,0 +1,20 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('ibc_message', (table) => { + table.increments(); + table.integer('transaction_message_id').index(); + table.string('src_channel_id').notNullable(); + table.string('src_port_id').notNullable(); + table.string('dst_channel_id').notNullable(); + table.string('dst_port_id').notNullable(); + table.string('type').notNullable(); + table.integer('sequence').notNullable(); + table.string('sequence_key').notNullable(); + table.foreign('ibc_client_id').references('ibc_client.id'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists('ibc_message'); +} diff --git a/src/common/constant.ts b/src/common/constant.ts index 6ff1f2597..849ea7950 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -68,6 +68,7 @@ export const BULL_JOB_NAME = { JOB_REDECODE_TX: 'job:redecode-tx', CRAWL_IBC_TAO: 'crawl:ibc-tao', CRAWL_GENESIS_IBC_TAO: 'crawl:genesis-ibc-tao', + CRAWL_IBC_APP: 'crawl:ibc-app', }; export const SERVICE = { @@ -229,6 +230,10 @@ export const SERVICE = { key: 'CrawlIBCTaoService', name: 'v1.CrawlIBCTaoService', }, + CrawlIBCAppService: { + key: 'CrawlIBCAppService', + name: 'v1.CrawlIBCAppService', + }, }, }; diff --git a/src/services/ibc/crawl_ibc_app.service.ts b/src/services/ibc/crawl_ibc_app.service.ts new file mode 100644 index 000000000..36f0df7eb --- /dev/null +++ b/src/services/ibc/crawl_ibc_app.service.ts @@ -0,0 +1,34 @@ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import { BlockCheckpoint } from '../../models'; + +@Service({ + name: SERVICE.V1.CrawlIBCAppService.key, + version: 1, +}) +export default class CrawlIbcAppService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_IBC_APP, + jobName: BULL_JOB_NAME.CRAWL_IBC_APP, + }) + public async crawlIbcApp(): Promise { + const [startHeight, endHeight, updateBlockCheckpoint] = + await BlockCheckpoint.getCheckpoint( + BULL_JOB_NAME.CRAWL_IBC_APP, + [BULL_JOB_NAME.CRAWL_IBC_TAO], + config.crawlIbcTao.key + ); + this.logger.info( + `Handle IBC/APP, startHeight: ${startHeight}, endHeight: ${endHeight}` + ); + if (startHeight > endHeight) return; + this.logger.info(updateBlockCheckpoint); + } +} From fc2478d65544844cbb2ede0a2d039f3f55db20b9 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 11 Aug 2023 16:20:43 +0700 Subject: [PATCH 12/54] feat: ibc app --- ci/config.json.ci | 5 + .../20230810094108_create_ibc_tx_model.ts | 19 ++-- src/models/event.ts | 2 + src/models/event_attribute.ts | 6 ++ src/models/ibc_message.ts | 96 +++++++++++++++++++ src/models/index.ts | 1 + src/services/ibc/crawl_ibc_app.service.ts | 95 +++++++++++++++++- 7 files changed, 214 insertions(+), 10 deletions(-) create mode 100644 src/models/ibc_message.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index 2a191016e..165f35505 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -166,5 +166,10 @@ "key": "crawlIbcTao", "millisecondRepeatJob": 2000, "blocksPerCall": 100 + }, + "crawlIbcApp": { + "key": "crawlIbcApp", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100 } } diff --git a/migrations/20230810094108_create_ibc_tx_model.ts b/migrations/20230810094108_create_ibc_tx_model.ts index 0bc1f0d20..3a376120f 100644 --- a/migrations/20230810094108_create_ibc_tx_model.ts +++ b/migrations/20230810094108_create_ibc_tx_model.ts @@ -4,14 +4,17 @@ export async function up(knex: Knex): Promise { await knex.schema.createTable('ibc_message', (table) => { table.increments(); table.integer('transaction_message_id').index(); - table.string('src_channel_id').notNullable(); - table.string('src_port_id').notNullable(); - table.string('dst_channel_id').notNullable(); - table.string('dst_port_id').notNullable(); - table.string('type').notNullable(); - table.integer('sequence').notNullable(); - table.string('sequence_key').notNullable(); - table.foreign('ibc_client_id').references('ibc_client.id'); + table.string('src_channel_id').notNullable().index(); + table.string('src_port_id').notNullable().index(); + table.string('dst_channel_id').notNullable().index(); + table.string('dst_port_id').notNullable().index(); + table.string('type').notNullable().index(); + table.integer('sequence').notNullable().index(); + table.string('sequence_key').notNullable().index(); + table.jsonb('data'); + table + .foreign('transaction_message_id') + .references('transaction_message.id'); }); } diff --git a/src/models/event.ts b/src/models/event.ts index efc835cd0..983c6f0f7 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -20,6 +20,8 @@ export class Event extends BaseModel { source!: string; + attributes!: EventAttribute[]; + static get tableName() { return 'event'; } diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 555bcd3ee..27722fd80 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -108,5 +108,11 @@ export class EventAttribute extends BaseModel { PORT_ID: 'port_id', COUNTERPARTY_PORT_ID: 'counterparty_port_id', COUNTERPARTY_CHANNEL_ID: 'counterparty_channel_id', + DATA_HEX: 'packet_data_hex', + SEQUENCE: 'packet_sequence', + SRC_PORT: 'packet_src_port', + SRC_CHANNEL: 'packet_src_channel', + DST_PORT: 'packet_dst_port', + DST_CHANNEL: 'packet_dst_channel', }; } diff --git a/src/models/ibc_message.ts b/src/models/ibc_message.ts new file mode 100644 index 000000000..54845c24d --- /dev/null +++ b/src/models/ibc_message.ts @@ -0,0 +1,96 @@ +/* eslint-disable import/no-cycle */ +import { Model } from 'objection'; +import BaseModel from './base'; +import { IbcChannel } from './ibc_channel'; +import { TransactionMessage } from './transaction_message'; + +export class IbcMessage extends BaseModel { + [relation: string]: any | any[]; + + id!: number; + + transaction_message_id!: number; + + src_channel_id!: string; + + src_port_id!: string; + + dst_channel_id!: string; + + dst_port_id!: string; + + type!: string; + + sequence!: number; + + sequence_key!: string; + + data!: any; + + static get tableName() { + return 'ibc_message'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'transaction_message_id', + 'src_channel_id', + 'src_port_id', + 'dst_channel_id', + 'dst_port_id', + 'type', + 'sequence', + 'sequence_key', + ], + properties: { + transaction_message_id: { type: 'number' }, + src_channel_id: { type: 'string' }, + src_port_id: { type: 'string' }, + dst_channel_id: { type: 'string' }, + dst_port_id: { type: 'string' }, + type: { type: 'string' }, + sequence: { type: 'number' }, + sequence_key: { type: 'string' }, + status: { type: 'boolean' }, + }, + }; + } + + static get relationMappings() { + return { + message: { + relation: Model.BelongsToOneRelation, + modelClass: TransactionMessage, + join: { + from: 'ibc_message.transaction_message_id', + to: 'transaction_message.id', + }, + }, + src_channel: { + relation: Model.BelongsToOneRelation, + modelClass: IbcChannel, + join: { + from: 'ibc_message.src_channel_id', + to: 'ibc_channel.id', + }, + }, + dst_channel: { + relation: Model.BelongsToOneRelation, + modelClass: IbcChannel, + join: { + from: 'ibc_message.dst_channel_id', + to: 'ibc_channel.id', + }, + }, + }; + } + + static EVENT_TYPE = { + SEND_PACKET: 'send_packet', + RECV_PACKET: 'recv_packet', + ACKNOWLEDGE_PACKET: 'acknowledge_packet', + TIMEOUT_PACKET: 'timeout_packet', + }; +} diff --git a/src/models/index.ts b/src/models/index.ts index 1c28230d7..3056261c0 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -26,3 +26,4 @@ export * from './cw20_total_holder_stats'; export * from './ibc_client'; export * from './ibc_connection'; export * from './ibc_channel'; +export * from './ibc_message'; diff --git a/src/services/ibc/crawl_ibc_app.service.ts b/src/services/ibc/crawl_ibc_app.service.ts index 36f0df7eb..5da8eaa40 100644 --- a/src/services/ibc/crawl_ibc_app.service.ts +++ b/src/services/ibc/crawl_ibc_app.service.ts @@ -1,9 +1,18 @@ +import { fromHex, fromUtf8 } from '@cosmjs/encoding'; import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { Knex } from 'knex'; import { ServiceBroker } from 'moleculer'; +import knex from '../../common/utils/db_connection'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import { BULL_JOB_NAME, SERVICE } from '../../common'; -import { BlockCheckpoint } from '../../models'; +import { getAttributeFrom } from '../../common/utils/smart_contract'; +import { + BlockCheckpoint, + Event, + EventAttribute, + IbcMessage, +} from '../../models'; @Service({ name: SERVICE.V1.CrawlIBCAppService.key, @@ -29,6 +38,88 @@ export default class CrawlIbcAppService extends BullableService { `Handle IBC/APP, startHeight: ${startHeight}, endHeight: ${endHeight}` ); if (startHeight > endHeight) return; - this.logger.info(updateBlockCheckpoint); + const events = await Event.query() + .withGraphFetched('attributes') + .joinRelated('message') + .select('event.id', 'event.type', 'message.id as message_id') + .whereIn('event.type', [ + IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET, + IbcMessage.EVENT_TYPE.RECV_PACKET, + IbcMessage.EVENT_TYPE.SEND_PACKET, + IbcMessage.EVENT_TYPE.TIMEOUT_PACKET, + ]) + .andWhere('event.block_height', '>', startHeight) + .andWhere('event.block_height', '<=', endHeight) + .orderBy('event.id'); + await knex.transaction(async (trx) => { + await this.handleIbcMessage(events, trx); + updateBlockCheckpoint.height = endHeight; + await BlockCheckpoint.query() + .transacting(trx) + .insert(updateBlockCheckpoint) + .onConflict('job_name') + .merge(); + }); + } + + async handleIbcMessage(events: Event[], trx: Knex.Transaction) { + const ibcMessage = events.map((event) => { + const srcChannel = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.SRC_CHANNEL + ); + const srcPort = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.SRC_PORT + ); + const dstChannel = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.DST_CHANNEL + ); + const dstPort = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.DST_PORT + ); + const sequence = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.SEQUENCE + ); + const dataHex = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.DATA_HEX + ); + return IbcMessage.fromJson({ + transaction_message_id: event.message_id, + src_channel_id: srcChannel, + src_port_id: srcPort, + dst_channel_id: dstChannel, + dst_port_id: dstPort, + type: event.type, + sequence, + sequence_key: `${srcChannel}.${srcPort}.${dstChannel}.${dstPort}.${sequence}`, + data: fromUtf8(fromHex(dataHex)), + }); + }); + if (ibcMessage.length > 0) { + await IbcMessage.query().insert(ibcMessage).transacting(trx); + } + } + + async _start(): Promise { + await this.createJob( + BULL_JOB_NAME.CRAWL_IBC_APP, + BULL_JOB_NAME.CRAWL_IBC_APP, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + every: config.crawlIbcApp.millisecondRepeatJob, + }, + } + ); + return super._start(); } } From 32d103a670e350dd6977ff48bea263c2c2eb976b Mon Sep 17 00:00:00 2001 From: Tuan Phan Anh <38557844+fibonacci998@users.noreply.github.com> Date: Tue, 15 Aug 2023 15:29:21 +0700 Subject: [PATCH 13/54] fix: allow whitelist query can have depth more than config (#312) --- src/services/api-gateways/graphiql.service.ts | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/services/api-gateways/graphiql.service.ts b/src/services/api-gateways/graphiql.service.ts index 2030bf82a..4cad62184 100644 --- a/src/services/api-gateways/graphiql.service.ts +++ b/src/services/api-gateways/graphiql.service.ts @@ -54,24 +54,6 @@ export default class GraphiQLService extends BaseService { message: '', data: null, }; - let openBrackets = 0; - let isWhere = false; - for (let i = 0; i < query.length; i += 1) { - if (query.charAt(i) === '(') isWhere = true; - else if (query.charAt(i) === ')') isWhere = false; - - if (query.charAt(i) === '{' && !isWhere) openBrackets += 1; - else if (query.charAt(i) === '}' && !isWhere) openBrackets -= 1; - - if (openBrackets > config.graphiqlApi.depthLimit + 2) { - result = { - code: ErrorCode.WRONG, - message: ErrorMessage.VALIDATION_ERROR, - errors: `The query depth must not be greater than ${config.graphiqlApi.depthLimit}`, - }; - return result; - } - } let graphqlObj; try { @@ -109,6 +91,25 @@ export default class GraphiQLService extends BaseService { if ( !queryWhitelist.includes(query.replaceAll(' ', '').replaceAll('\n', '')) ) { + let openBrackets = 0; + let isWhere = false; + for (let i = 0; i < query.length; i += 1) { + if (query.charAt(i) === '(') isWhere = true; + else if (query.charAt(i) === ')') isWhere = false; + + if (query.charAt(i) === '{' && !isWhere) openBrackets += 1; + else if (query.charAt(i) === '}' && !isWhere) openBrackets -= 1; + + if (openBrackets > config.graphiqlApi.depthLimit + 2) { + result = { + code: ErrorCode.WRONG, + message: ErrorMessage.VALIDATION_ERROR, + errors: `The query depth must not be greater than ${config.graphiqlApi.depthLimit}`, + }; + return result; + } + } + const selections = ( graphqlObj.definitions[0] as OperationDefinitionNode ).selectionSet.selections as FieldNode[]; From 685bb4b658db4033d29173b19898fd7c592e33a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20Th=C3=A0nh=20Phong?= <49814372+phamphong9981@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:28:56 +0700 Subject: [PATCH 14/54] feat: reindex cw20 service ( main ) (#277) * feat: reindex cw20 service * test: test cw20 reindexing * fix: idiot * refactor: code * feat: api admin --- ci/config.json.ci | 5 +- config.json | 5 +- src/common/constant.ts | 10 + src/models/cw20_activity.ts | 2 + src/models/cw20_contract.ts | 2 + src/models/cw20_holder.ts | 2 + src/models/cw20_total_holder_stats.ts | 2 + .../api-gateways/api_gateway.service.ts | 6 + .../api-gateways/cw20_admin.service.ts | 45 +++ src/services/cw20/cw20.service.ts | 98 +++-- src/services/cw20/cw20_reindexing.service.ts | 181 +++++++++ .../cw20/cw20_update_by_contract.service.ts | 2 +- test/unit/services/cw20/cw20.spec.ts | 357 +++++++++++++++--- .../services/cw20/cw20_reindexing.spec.ts | 161 ++++++++ 14 files changed, 807 insertions(+), 71 deletions(-) create mode 100644 src/services/api-gateways/cw20_admin.service.ts create mode 100644 src/services/cw20/cw20_reindexing.service.ts create mode 100644 test/unit/services/cw20/cw20_reindexing.spec.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index 245de74f2..31bcf5173 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -108,7 +108,10 @@ "cw20": { "blocksPerCall": 100, "millisecondRepeatJob": 2000, - "key": "cw20" + "key": "cw20", + "reindexHistory": { + "limitRecordGet": 500 + } }, "dashboardStatistics": { "millisecondCrawl": 10000, diff --git a/config.json b/config.json index cd07b5711..e2b66e521 100644 --- a/config.json +++ b/config.json @@ -103,7 +103,10 @@ "cw20": { "blocksPerCall": 100, "millisecondRepeatJob": 2000, - "key": "cw20" + "key": "cw20", + "reindexHistory": { + "limitRecordGet": 500 + } }, "crawlContractEvent": { "key": "crawlContractEvent", diff --git a/src/common/constant.ts b/src/common/constant.ts index fcbed0f5f..624cb63ec 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -66,6 +66,8 @@ export const BULL_JOB_NAME = { REINDEX_CW721_HISTORY: 'reindex:cw721-history', HANDLE_MIGRATE_CONTRACT: 'handle:migrate-contract', JOB_REDECODE_TX: 'job:redecode-tx', + REINDEX_CW20_CONTRACT: 'reindex:cw20-contract', + REINDEX_CW20_HISTORY: 'reindex:cw20-history', }; export const SERVICE = { @@ -223,6 +225,14 @@ export const SERVICE = { path: 'v1.ReDecodeTx', }, }, + Cw20ReindexingService: { + key: 'Cw20ReindexingService', + name: 'v1.Cw20ReindexingService', + Reindexing: { + key: 'reindexing', + path: 'v1.Cw20ReindexingService.reindexing', + }, + }, }, }; diff --git a/src/models/cw20_activity.ts b/src/models/cw20_activity.ts index ac0646b53..19d8a6eb0 100644 --- a/src/models/cw20_activity.ts +++ b/src/models/cw20_activity.ts @@ -5,6 +5,8 @@ import { Cw20Contract } from './cw20_contract'; import { SmartContract } from './smart_contract'; export class Cw20Event extends BaseModel { + static softDelete = false; + [relation: string]: any; id!: number; diff --git a/src/models/cw20_contract.ts b/src/models/cw20_contract.ts index 7fd748023..b337cf097 100644 --- a/src/models/cw20_contract.ts +++ b/src/models/cw20_contract.ts @@ -26,6 +26,8 @@ export interface IContractInfo { name?: string; } export class Cw20Contract extends BaseModel { + static softDelete = false; + [relation: string]: any; id!: number; diff --git a/src/models/cw20_holder.ts b/src/models/cw20_holder.ts index 4b6b01ef4..ab4b00469 100644 --- a/src/models/cw20_holder.ts +++ b/src/models/cw20_holder.ts @@ -5,6 +5,8 @@ import { Cw20Contract } from './cw20_contract'; import { SmartContract } from './smart_contract'; export class CW20Holder extends BaseModel { + static softDelete = false; + [relation: string]: any; id?: number; diff --git a/src/models/cw20_total_holder_stats.ts b/src/models/cw20_total_holder_stats.ts index 7d5d8a846..b5bc50b2d 100644 --- a/src/models/cw20_total_holder_stats.ts +++ b/src/models/cw20_total_holder_stats.ts @@ -3,6 +3,8 @@ import BaseModel from './base'; import { Cw20Contract } from './cw20_contract'; export class CW20TotalHolderStats extends BaseModel { + static softDelete = false; + [relation: string]: any; date!: Date; diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index 9e02973e1..0503fd3a3 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -23,6 +23,12 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; mappingPolicy: 'restrict', // allow action called with exact method whitelist: ['v2.dashboard-statistics.*', 'v2.graphql.*'], }, + { + path: '/admin', + autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services + mappingPolicy: 'restrict', // allow action called with exact method + whitelist: ['v1.cw20-admin.*'], + }, ], // empty cors object will have moleculer to generate handler for preflight request and CORS header which allow all origin cors: {}, diff --git a/src/services/api-gateways/cw20_admin.service.ts b/src/services/api-gateways/cw20_admin.service.ts new file mode 100644 index 000000000..f94fccd0e --- /dev/null +++ b/src/services/api-gateways/cw20_admin.service.ts @@ -0,0 +1,45 @@ +import { Post, Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { Context, ServiceBroker } from 'moleculer'; +import networks from '../../../network.json' assert { type: 'json' }; +import BaseService from '../../base/base.service'; + +@Service({ + name: 'cw20-admin', + version: 1, +}) +export default class Cw20AdminService extends BaseService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Post('/cw20-reindexing', { + name: 'cw20Reindexing', + params: { + chainid: { + type: 'string', + optional: false, + enum: networks.map((network) => network.chainId), + }, + contractAddress: { + type: 'string', + optional: false, + }, + }, + }) + async cw20ReindexingByChainId( + ctx: Context< + { chainid: string; contractAddress: string }, + Record + > + ) { + const selectedChain = networks.find( + (network) => network.chainId === ctx.params.chainid + ); + return this.broker.call( + `v1.Cw20ReindexingService.reindexing@${selectedChain?.moleculerNamespace}`, + { + contractAddress: ctx.params.contractAddress, + } + ); + } +} diff --git a/src/services/cw20/cw20.service.ts b/src/services/cw20/cw20.service.ts index 49cc86251..db63a1e93 100644 --- a/src/services/cw20/cw20.service.ts +++ b/src/services/cw20/cw20.service.ts @@ -1,4 +1,5 @@ import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { Queue } from 'bullmq'; import { Knex } from 'knex'; import _ from 'lodash'; import { ServiceBroker } from 'moleculer'; @@ -34,6 +35,13 @@ export const CW20_ACTION = { BURN_FROM: 'burn_from', SEND_FROM: 'send_from', }; +export interface ICw20ReindexingHistoryParams { + smartContractId: number; + startBlock: number; + endBlock: number; + prevId: number; + contractAddress: string; +} @Service({ name: SERVICE.V1.Cw20.key, version: 1, @@ -205,41 +213,38 @@ export default class Cw20Service extends BullableService { } } - async getCw20ContractEvents(startBlock: number, endBlock: number) { + async getCw20ContractEvents( + startBlock: number, + endBlock: number, + smartContractId?: number, + page?: { prevId: number; limit: number } + ) { return SmartContractEvent.query() - .alias('smart_contract_event') - .withGraphJoined( - '[message(selectMessage), tx(selectTransaction), attributes(selectAttribute), smart_contract(selectSmartContract).code(selectCode)]' - ) - .modifiers({ - selectCode(builder) { - builder.select('type'); - }, - selectTransaction(builder) { - builder.select('hash', 'height'); - }, - selectMessage(builder) { - builder.select('sender', 'content'); - }, - selectAttribute(builder) { - builder.select('key', 'value'); - }, - selectSmartContract(builder) { - builder.select('address', 'id'); - }, - }) + .withGraphFetched('attributes(selectAttribute)') + .joinRelated('[message, tx, smart_contract.code]') .where('smart_contract:code.type', 'CW20') .where('tx.height', '>', startBlock) .andWhere('tx.height', '<=', endBlock) + .modify((builder) => { + if (smartContractId) { + builder.andWhere('smart_contract.id', smartContractId); + } + if (page) { + builder + .andWhere('smart_contract_event.id', '>', page.prevId) + .orderBy('smart_contract_event.id', 'asc') + .limit(page.limit); + } + }) .select( 'message.sender as sender', 'smart_contract.address as contract_address', 'smart_contract_event.action', - 'smart_contract_event.event_id as event_id', - 'smart_contract_event.index', + 'smart_contract_event.event_id', 'smart_contract.id as smart_contract_id', - 'tx.height as height', - 'smart_contract_event.id as smart_contract_event_id' + 'smart_contract_event.id as smart_contract_event_id', + 'tx.hash', + 'tx.height' ) .orderBy('smart_contract_event.id', 'asc'); } @@ -304,4 +309,45 @@ export default class Cw20Service extends BullableService { } return super._start(); } + + @QueueHandler({ + queueName: BULL_JOB_NAME.REINDEX_CW20_HISTORY, + jobName: BULL_JOB_NAME.REINDEX_CW20_HISTORY, + }) + public async reindexHistory(_payload: ICw20ReindexingHistoryParams) { + const { smartContractId, startBlock, endBlock, prevId, contractAddress } = + _payload; + // insert data from event_attribute_backup to event_attribute + const { limitRecordGet } = config.cw20.reindexHistory; + const events = await this.getCw20ContractEvents( + startBlock, + endBlock, + smartContractId, + { limit: limitRecordGet, prevId } + ); + if (events.length > 0) { + await knex.transaction(async (trx) => { + await this.handleCw20Histories(events, trx); + }); + await this.createJob( + BULL_JOB_NAME.REINDEX_CW20_HISTORY, + BULL_JOB_NAME.REINDEX_CW20_HISTORY, + { + smartContractId, + startBlock, + endBlock, + prevId: events[events.length - 1].smart_contract_event_id, + contractAddress, + } satisfies ICw20ReindexingHistoryParams, + { + removeOnComplete: true, + } + ); + } else { + const queue: Queue = this.getQueueManager().getQueue( + BULL_JOB_NAME.REINDEX_CW20_CONTRACT + ); + (await queue.getJob(contractAddress))?.remove(); + } + } } diff --git a/src/services/cw20/cw20_reindexing.service.ts b/src/services/cw20/cw20_reindexing.service.ts new file mode 100644 index 000000000..b2caf4cb8 --- /dev/null +++ b/src/services/cw20/cw20_reindexing.service.ts @@ -0,0 +1,181 @@ +import { + Action, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import _ from 'lodash'; +import { Context, ServiceBroker } from 'moleculer'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import { BULL_JOB_NAME, IContextUpdateCw20, SERVICE } from '../../common'; +import { + CW20Holder, + CW20TotalHolderStats, + Cw20Contract, + Cw20Event, + IHolderEvent, + SmartContract, +} from '../../models'; +import { ICw20ReindexingHistoryParams } from './cw20.service'; +import knex from '../../common/utils/db_connection'; + +export interface IAddressParam { + contractAddress: string; +} +interface ICw20ReindexingParams { + contractAddress: string; + smartContractId: number; +} +@Service({ + name: SERVICE.V1.Cw20ReindexingService.key, + version: 1, +}) +export default class Cw20ReindexingContract extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Action({ + name: SERVICE.V1.Cw20ReindexingService.Reindexing.key, + params: { + contractAddress: 'string', + }, + }) + public async reindexing(ctx: Context) { + const { contractAddress } = ctx.params; + const smartContract = await SmartContract.query() + .withGraphJoined('code') + .where('address', contractAddress) + .first() + .throwIfNotFound(); + + // check whether contract is Cw20 type -> throw error to user + if (smartContract.code.type === 'CW20') { + await this.createJob( + BULL_JOB_NAME.REINDEX_CW20_CONTRACT, + BULL_JOB_NAME.REINDEX_CW20_CONTRACT, + { + contractAddress, + smartContractId: smartContract.id, + } satisfies ICw20ReindexingParams, + { + jobId: contractAddress, + } + ); + } else { + throw new Error( + `Smart contract ${ctx.params.contractAddress} is not CW20 type` + ); + } + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.REINDEX_CW20_CONTRACT, + jobName: BULL_JOB_NAME.REINDEX_CW20_CONTRACT, + }) + async jobHandler(_payload: ICw20ReindexingParams): Promise { + const { smartContractId, contractAddress } = _payload; + const cw20Contract = await Cw20Contract.query() + .withGraphJoined('smart_contract') + .where('smart_contract.address', contractAddress) + .select(['cw20_contract.id']) + .first(); + // query + const contractInfo = ( + await Cw20Contract.getContractsInfo([contractAddress]) + )[0]; + let track = true; + let initBalances: IHolderEvent[] = []; + // get init address holder, init amount + try { + initBalances = await Cw20Contract.getInstantiateBalances(contractAddress); + } catch (error) { + track = false; + } + const minUpdatedHeightOwner = + _.min(initBalances.map((holder) => holder.event_height)) || 0; + const maxUpdatedHeightOwner = + _.max(initBalances.map((holder) => holder.event_height)) || 0; + let id = -1; + let lastUpdatedHeight = -1; + await knex.transaction(async (trx) => { + if (cw20Contract) { + await Cw20Event.query() + .delete() + .where('cw20_contract_id', cw20Contract.id) + .transacting(trx); + await CW20TotalHolderStats.query() + .delete() + .where('cw20_contract_id', cw20Contract.id) + .transacting(trx); + await CW20Holder.query() + .delete() + .where('cw20_contract_id', cw20Contract.id) + .transacting(trx); + await Cw20Contract.query().deleteById(cw20Contract.id).transacting(trx); + } + const newCw20Contract = await Cw20Contract.query() + .insertGraph({ + ...Cw20Contract.fromJson({ + smart_contract_id: smartContractId, + symbol: contractInfo?.symbol, + minter: contractInfo?.minter, + marketing_info: contractInfo?.marketing_info, + name: contractInfo?.name, + total_supply: initBalances.reduce( + (acc: string, curr: { address: string; amount: string }) => + (BigInt(acc) + BigInt(curr.amount)).toString(), + '0' + ), + track, + decimal: contractInfo?.decimal, + last_updated_height: minUpdatedHeightOwner, + }), + holders: initBalances.map((e) => ({ + address: e.address, + amount: e.amount, + last_updated_height: e.event_height, + })), + }) + .transacting(trx); + id = newCw20Contract.id; + lastUpdatedHeight = newCw20Contract.last_updated_height; + }); + // handle from minUpdatedHeightOwner to blockHeight + await this.broker.call( + SERVICE.V1.Cw20UpdateByContract.UpdateByContract.path, + { + cw20Contracts: [ + { + id, + last_updated_height: lastUpdatedHeight, + }, + ], + startBlock: minUpdatedHeightOwner, + endBlock: maxUpdatedHeightOwner, + } satisfies IContextUpdateCw20 + ); + // insert histories + await this.createJob( + BULL_JOB_NAME.REINDEX_CW20_HISTORY, + BULL_JOB_NAME.REINDEX_CW20_HISTORY, + { + smartContractId, + startBlock: config.crawlBlock.startBlock, + endBlock: maxUpdatedHeightOwner, + prevId: 0, + contractAddress, + } satisfies ICw20ReindexingHistoryParams, + { + removeOnComplete: true, + } + ); + } + + async _start(): Promise { + await this.broker.waitForServices([ + SERVICE.V1.Cw20.name, + SERVICE.V1.Cw20UpdateByContract.name, + ]); + return super._start(); + } +} diff --git a/src/services/cw20/cw20_update_by_contract.service.ts b/src/services/cw20/cw20_update_by_contract.service.ts index f4741a785..09c865863 100644 --- a/src/services/cw20/cw20_update_by_contract.service.ts +++ b/src/services/cw20/cw20_update_by_contract.service.ts @@ -78,7 +78,7 @@ export default class Cw20UpdateByContractService extends BullableService { const { startBlock, endBlock } = ctx.params; // eslint-disable-next-line no-restricted-syntax for (const cw20Contract of ctx.params.cw20Contracts) { - const startUpdateBlock = Math.min( + const startUpdateBlock = Math.max( startBlock, cw20Contract.last_updated_height ); diff --git a/test/unit/services/cw20/cw20.spec.ts b/test/unit/services/cw20/cw20.spec.ts index f14426e2d..d87ab76d6 100644 --- a/test/unit/services/cw20/cw20.spec.ts +++ b/test/unit/services/cw20/cw20.spec.ts @@ -4,6 +4,7 @@ import { BULL_JOB_NAME } from '../../../../src/common'; import knex from '../../../../src/common/utils/db_connection'; import { Block, + BlockCheckpoint, Code, Cw20Contract, Cw20Event, @@ -12,6 +13,7 @@ import { import { SmartContractEvent } from '../../../../src/models/smart_contract_event'; import Cw20Service from '../../../../src/services/cw20/cw20.service'; import Cw20UpdateByContractService from '../../../../src/services/cw20/cw20_update_by_contract.service'; +import CrawlContractEventService from '../../../../src/services/crawl-cosmwasm/crawl_contract_event.service'; @Describe('Test cw20 service') export default class Cw20 { @@ -19,6 +21,10 @@ export default class Cw20 { cw20Service = this.broker.createService(Cw20Service) as Cw20Service; + crawlContractEventService = this.broker.createService( + CrawlContractEventService + ) as CrawlContractEventService; + cw20UpdateByContractService = this.broker.createService( Cw20UpdateByContractService ) as Cw20UpdateByContractService; @@ -31,6 +37,36 @@ export default class Cw20 { data: {}, }); + codeId = { + ...Code.fromJson({ + creator: 'code_id_creator', + code_id: 100, + data_hash: 'code_id_data_hash', + instantiate_permission: { permission: '', address: '', addresses: [] }, + store_hash: 'code_id_store_hash', + store_height: 1000, + type: 'CW20', + }), + contracts: [ + { + name: 'Base Contract 2', + address: 'mock_contract_address', + creator: 'phamphong_creator', + code_id: 100, + instantiate_hash: 'abc', + instantiate_height: 300000, + }, + { + code_id: 100, + address: 'mock_contract_address_2', + name: 'name', + creator: 'phamphong_creator 2', + instantiate_hash: 'abc', + instantiate_height: 300000, + }, + ], + }; + txInsert = { ...Transaction.fromJson({ height: this.block.height, @@ -57,24 +93,114 @@ export default class Cw20 { content: {}, events: [ { - type: 'execute', block_height: this.block.height, source: 'TX_EVENT', + type: 'instantiate', attributes: [ { - block_height: this.block.height, index: 0, + block_height: this.block.height, composite_key: 'execute._contract_address', key: '_contract_address', - value: 'this.mockInitContract_2.smart_contract.address', + value: this.codeId.contracts[0].address, }, { + index: 1, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: 'code_id', + value: '6', + }, + ], + }, + { + block_height: this.block.height, + source: 'TX_EVENT', + type: 'wasm', + attributes: [ + { + index: 0, block_height: this.block.height, + composite_key: 'execute._contract_address', + key: '_contract_address', + value: this.codeId.contracts[0].address, + }, + { index: 1, - // tx_id: 1, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: 'action', + value: 'add_whitelist', + }, + { + index: 2, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: 'token_id', + value: 'test2', + }, + ], + }, + { + block_height: this.block.height, + source: 'TX_EVENT', + type: 'wasm', + attributes: [ + { + index: 2, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: '_contract_address', + value: this.codeId.contracts[1].address, + }, + { + index: 3, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: 'action', + value: 'add_mint_phase', + }, + { + index: 4, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: 'token_id', + value: 'test1', + }, + ], + }, + { + block_height: this.block.height, + source: 'TX_EVENT', + type: 'wasm', + attributes: [ + { + index: 0, + block_height: this.block.height, composite_key: 'execute._contract_address', key: '_contract_address', - value: 'fdgdgdfgdfg', + value: this.codeId.contracts[0].address, + }, + { + index: 1, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: 'action', + value: 'dfgdfgdfgdfg', + }, + { + index: 2, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: 'bcvbcb', + value: 'fdsdfsdf', + }, + { + index: 3, + block_height: this.block.height, + composite_key: 'execute._contract_address', + key: 'vbvbv', + value: 'sesesese', }, ], }, @@ -83,42 +209,16 @@ export default class Cw20 { ], }; - codeId = { - ...Code.fromJson({ - creator: 'code_id_creator', - code_id: 100, - data_hash: 'code_id_data_hash', - instantiate_permission: { permission: '', address: '', addresses: [] }, - store_hash: 'code_id_store_hash', - store_height: 1000, - type: 'CW721', + mockBlockCheckpoint = [ + BlockCheckpoint.fromJson({ + job_name: BULL_JOB_NAME.CRAWL_CONTRACT_EVENT, + height: this.block.height - 1, }), - contracts: [ - { - name: 'Base Contract 2', - address: 'mock_contract_address', - creator: 'phamphong_creator', - code_id: 100, - instantiate_hash: 'abc', - instantiate_height: 300000, - }, - { - code_id: 100, - address: 'mock_contract_address_2', - name: 'name', - creator: 'phamphong_creator 2', - instantiate_hash: 'abc', - instantiate_height: 300000, - }, - ], - }; - - smartContractEvent = SmartContractEvent.fromJson({ - smart_contract_id: 1, - action: 'huh', - event_id: '1', - index: 1, - }); + BlockCheckpoint.fromJson({ + job_name: BULL_JOB_NAME.CRAWL_SMART_CONTRACT, + height: this.block.height, + }), + ]; @BeforeAll() async initSuite() { @@ -126,12 +226,13 @@ export default class Cw20 { this.cw20UpdateByContractService.getQueueManager().stopAll(); await this.broker.start(); await knex.raw( - 'TRUNCATE TABLE code, cw20_contract, block, transaction RESTART IDENTITY CASCADE' + 'TRUNCATE TABLE code, cw20_contract, block, transaction, smart_contract_event, block_checkpoint RESTART IDENTITY CASCADE' ); await Block.query().insert(this.block); await Transaction.query().insertGraph(this.txInsert); await Code.query().insertGraph(this.codeId); - await SmartContractEvent.query().insert(this.smartContractEvent); + await BlockCheckpoint.query().insert(this.mockBlockCheckpoint); + await this.crawlContractEventService.jobHandler(); } @AfterAll() @@ -337,4 +438,176 @@ export default class Cw20 { await trx.rollback(); }); } + + @Test('test getCw20ContractEvent function') + public async testGetCw20ContractEvent() { + const extractData = await this.cw20Service.getCw20ContractEvents( + this.block.height - 1, + this.block.height + ); + expect( + extractData.map((data) => ({ + action: data.action, + sender: data.sender, + contractAddress: data.contract_address, + attributes: data.attributes, + hash: data.hash, + height: data.height, + })) + ).toEqual([ + { + action: 'instantiate', + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[0].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[0].attributes[0], + this.txInsert.messages[0].events[0].attributes[1], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + { + action: this.txInsert.messages[0].events[1].attributes[1].value, + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[1].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[1].attributes[0], + this.txInsert.messages[0].events[1].attributes[1], + this.txInsert.messages[0].events[1].attributes[2], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + { + action: this.txInsert.messages[0].events[2].attributes[1].value, + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[2].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[2].attributes[0], + this.txInsert.messages[0].events[2].attributes[1], + this.txInsert.messages[0].events[2].attributes[2], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + { + action: this.txInsert.messages[0].events[3].attributes[1].value, + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[3].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[3].attributes[0], + this.txInsert.messages[0].events[3].attributes[1], + this.txInsert.messages[0].events[3].attributes[2], + this.txInsert.messages[0].events[3].attributes[3], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + ]); + } + + @Test('test getCw20ContractEvent function by contract') + public async testGetCw20ContractEventByContract() { + const extractData = await this.cw20Service.getCw20ContractEvents( + this.block.height - 1, + this.block.height, + 1 + ); + expect( + extractData.map((data) => ({ + action: data.action, + sender: data.sender, + contractAddress: data.contract_address, + attributes: data.attributes, + hash: data.hash, + height: data.height, + })) + ).toEqual([ + { + action: 'instantiate', + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[0].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[0].attributes[0], + this.txInsert.messages[0].events[0].attributes[1], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + { + action: this.txInsert.messages[0].events[1].attributes[1].value, + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[1].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[1].attributes[0], + this.txInsert.messages[0].events[1].attributes[1], + this.txInsert.messages[0].events[1].attributes[2], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + { + action: this.txInsert.messages[0].events[3].attributes[1].value, + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[3].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[3].attributes[0], + this.txInsert.messages[0].events[3].attributes[1], + this.txInsert.messages[0].events[3].attributes[2], + this.txInsert.messages[0].events[3].attributes[3], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + ]); + const extractData2 = await this.cw20Service.getCw20ContractEvents( + this.block.height - 1, + this.block.height, + 1, + { prevId: 0, limit: 2 } + ); + expect( + extractData2.map((data) => ({ + action: data.action, + sender: data.sender, + contractAddress: data.contract_address, + attributes: data.attributes, + hash: data.hash, + height: data.height, + })) + ).toEqual([ + { + action: 'instantiate', + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[0].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[0].attributes[0], + this.txInsert.messages[0].events[0].attributes[1], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + { + action: this.txInsert.messages[0].events[1].attributes[1].value, + sender: this.txInsert.messages[0].sender, + contractAddress: + this.txInsert.messages[0].events[1].attributes[0].value, + attributes: [ + this.txInsert.messages[0].events[1].attributes[0], + this.txInsert.messages[0].events[1].attributes[1], + this.txInsert.messages[0].events[1].attributes[2], + ].map((attribute) => ({ key: attribute.key, value: attribute.value })), + hash: this.txInsert.hash, + height: this.txInsert.height, + }, + ]); + } } diff --git a/test/unit/services/cw20/cw20_reindexing.spec.ts b/test/unit/services/cw20/cw20_reindexing.spec.ts new file mode 100644 index 000000000..8d6eba414 --- /dev/null +++ b/test/unit/services/cw20/cw20_reindexing.spec.ts @@ -0,0 +1,161 @@ +import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { ServiceBroker } from 'moleculer'; +import knex from '../../../../src/common/utils/db_connection'; +import Cw20ReindexingContract from '../../../../src/services/cw20/cw20_reindexing.service'; +import { Code, Cw20Contract, CW20Holder } from '../../../../src/models'; +import Cw20UpdateByContractService from '../../../../src/services/cw20/cw20_update_by_contract.service'; +import Cw20Service from '../../../../src/services/cw20/cw20.service'; + +@Describe('Test cw20 reindexing service') +export default class TestCw20ReindexingService { + codeId = { + ...Code.fromJson({ + creator: 'code_id_creator', + code_id: 100, + data_hash: 'code_id_data_hash', + instantiate_permission: { permission: '', address: '', addresses: [] }, + store_hash: 'code_id_store_hash', + store_height: 1000, + type: 'CW721', + }), + contracts: [ + { + name: 'Base Contract 2', + address: 'mock_contract_address', + creator: 'phamphong_creator', + code_id: 100, + instantiate_hash: 'abc', + instantiate_height: 300000, + }, + { + code_id: 100, + address: 'mock_contract_address_2', + name: 'name', + creator: 'phamphong_creator 2', + instantiate_hash: 'abc', + instantiate_height: 300000, + }, + ], + }; + + cw20Contract = { + ...Cw20Contract.fromJson({ + smart_contract_id: 1, + marketing_info: {}, + total_supply: '1121112133', + symbol: 'TEST SyMbol', + minter: 'jfglkdfjgklfdgklklfdkl', + name: 'dgbdfmnlkgsdfklgjksdfl', + track: true, + last_updated_height: 10000, + }), + holders: [ + { + address: 'holder_1', + amount: '123134134434', + last_updated_height: 8000, + }, + { + address: 'holder_2', + amount: '20032204', + last_updated_height: 8500, + }, + ], + }; + + broker = new ServiceBroker({ logger: false }); + + cw20reindexingService = this.broker.createService( + Cw20ReindexingContract + ) as Cw20ReindexingContract; + + cw20UpdateByContractService = this.broker.createService( + Cw20UpdateByContractService + ) as Cw20UpdateByContractService; + + cw20Service = this.broker.createService(Cw20Service) as Cw20Service; + + @BeforeAll() + async initSuite() { + await this.broker.start(); + await knex.raw( + 'TRUNCATE TABLE code, cw20_contract, block_checkpoint RESTART IDENTITY CASCADE' + ); + await Code.query().insertGraph(this.codeId); + } + + @AfterAll() + async tearDown() { + await this.broker.stop(); + } + + @Test('Test ReindexingService function') + public async testReindexingService() { + const mockContractInfo = { + address: this.codeId.contracts[0].address, + name: 'dgjkfjgdkg', + symbol: 'NNNJNJ', + minter: 'hfgjksghkjsf', + }; + const mockHolders = [ + { + address: 'holder_1', + amount: '123134134434', + event_height: 8000, + contract_address: this.codeId.contracts[0].address, + }, + { + address: 'holder_2', + amount: '20032204', + event_height: 8500, + contract_address: this.codeId.contracts[0].address, + }, + { + address: 'holder_3', + amount: '5467987', + event_height: 9600, + contract_address: this.codeId.contracts[0].address, + }, + { + address: 'holder_4', + amount: '11111111', + event_height: 23655422, + contract_address: this.codeId.contracts[0].address, + }, + ]; + this.cw20Service.reindexHistory = jest.fn(() => Promise.resolve()); + this.cw20UpdateByContractService.UpdateByContract = jest.fn(() => + Promise.resolve() + ); + Cw20Contract.getContractsInfo = jest.fn(() => + Promise.resolve([mockContractInfo]) + ); + Cw20Contract.getInstantiateBalances = jest.fn(() => + Promise.resolve(mockHolders) + ); + await this.cw20reindexingService.jobHandler({ + contractAddress: this.codeId.contracts[0].address, + smartContractId: 1, + }); + const cw20Contract = await Cw20Contract.query() + .withGraphJoined('smart_contract') + .where('smart_contract.address', this.codeId.contracts[0].address) + .first() + .throwIfNotFound(); + expect(cw20Contract.name).toEqual(mockContractInfo.name); + expect(cw20Contract.minter).toEqual(mockContractInfo.minter); + expect(cw20Contract.symbol).toEqual(mockContractInfo.symbol); + const cw20Holders = await CW20Holder.query() + .withGraphJoined('token') + .where('token.id', cw20Contract.id) + .orderBy('address', 'asc'); + expect( + cw20Holders.map((cw20Holder) => ({ + address: cw20Holder.address, + amount: cw20Holder.amount, + event_height: cw20Holder.last_updated_height, + contract_address: this.codeId.contracts[0].address, + })) + ).toEqual(mockHolders); + } +} From 92795eb9e99d37d45602b9d4ee801577436eb900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20Th=C3=A0nh=20Phong?= <49814372+phamphong9981@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:53:07 +0700 Subject: [PATCH 15/54] refactor: cw20/cw721 index and add relation ( main ) (#306) * refactor: cw20 index and add relation * refactor: code --- .../20230809073622_index_cw20_models.ts | 27 +++++++++++++++++++ src/models/cw20_activity.ts | 22 +++++++++++++++ src/models/cw721_tx.ts | 13 +++++++++ 3 files changed, 62 insertions(+) create mode 100644 migrations/20230809073622_index_cw20_models.ts diff --git a/migrations/20230809073622_index_cw20_models.ts b/migrations/20230809073622_index_cw20_models.ts new file mode 100644 index 000000000..76f46a0ed --- /dev/null +++ b/migrations/20230809073622_index_cw20_models.ts @@ -0,0 +1,27 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable('cw20_activity', (table) => { + table.index('from'); + table.index('to'); + table.index('action'); + }); + await knex.schema.alterTable('cw721_activity', (table) => { + table.index('from'); + table.index('to'); + table.index('action'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable('cw20_activity', (table) => { + table.dropIndex('from'); + table.dropIndex('to'); + table.dropIndex('action'); + }); + await knex.schema.alterTable('cw721_activity', (table) => { + table.dropIndex('from'); + table.dropIndex('to'); + table.dropIndex('action'); + }); +} diff --git a/src/models/cw20_activity.ts b/src/models/cw20_activity.ts index 19d8a6eb0..777b835ae 100644 --- a/src/models/cw20_activity.ts +++ b/src/models/cw20_activity.ts @@ -3,6 +3,8 @@ import BaseModel from './base'; // eslint-disable-next-line import/no-cycle import { Cw20Contract } from './cw20_contract'; import { SmartContract } from './smart_contract'; +import { SmartContractEvent } from './smart_contract_event'; +import { Event } from './event'; export class Cw20Event extends BaseModel { static softDelete = false; @@ -70,6 +72,26 @@ export class Cw20Event extends BaseModel { }, }, }, + smart_contract_event: { + relation: Model.BelongsToOneRelation, + modelClass: SmartContractEvent, + join: { + from: 'cw20_activity.smart_contract_event_id', + to: 'smart_contract_event.id', + }, + }, + event: { + relation: Model.HasOneThroughRelation, + modelClass: Event, + join: { + from: 'cw20_activity.smart_contract_event_id', + to: 'event.id', + through: { + from: 'smart_contract_event.id', + to: 'smart_contract_event.event_id', + }, + }, + }, }; } } diff --git a/src/models/cw721_tx.ts b/src/models/cw721_tx.ts index fd94f8abe..7ec84fbc3 100644 --- a/src/models/cw721_tx.ts +++ b/src/models/cw721_tx.ts @@ -4,6 +4,7 @@ import BaseModel from './base'; import CW721Contract from './cw721_contract'; import CW721Token from './cw721_token'; import { SmartContractEvent } from './smart_contract_event'; +import { Event } from './event'; export default class CW721Activity extends BaseModel { static softDelete = false; @@ -77,6 +78,18 @@ export default class CW721Activity extends BaseModel { to: 'smart_contract_event.id', }, }, + event: { + relation: Model.HasOneThroughRelation, + modelClass: Event, + join: { + from: 'cw721_activity.smart_contract_event_id', + to: 'event.id', + through: { + from: 'smart_contract_event.id', + to: 'smart_contract_event.event_id', + }, + }, + }, }; } } From b6b007c25a1e4cdb764fd84dfe4b137e26184b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20Th=C3=A0nh=20Phong?= <49814372+phamphong9981@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:54:17 +0700 Subject: [PATCH 16/54] fix: duplicate latest migrate contract (#304) --- .../crawl-cosmwasm/crawl_smart_contract.service.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/services/crawl-cosmwasm/crawl_smart_contract.service.ts b/src/services/crawl-cosmwasm/crawl_smart_contract.service.ts index e6e9cff83..b4c82ca32 100644 --- a/src/services/crawl-cosmwasm/crawl_smart_contract.service.ts +++ b/src/services/crawl-cosmwasm/crawl_smart_contract.service.ts @@ -182,10 +182,12 @@ export default class CrawlSmartContractService extends BullableService { 'code_id', codeContractValues.map((contract) => contract.codeId) ), - SmartContract.query().whereIn( - 'address', - codeContractValues.map((contract) => contract.address) - ), + SmartContract.query() + .whereIn( + 'address', + codeContractValues.map((contract) => contract.address) + ) + .andWhere('status', SmartContract.STATUS.LATEST), ]); const codeContractsByKey = _.keyBy(contractsWithMigrateCode, 'code_id'); From 3640cff9b734ad3cfdb2a10a1a0b23de28a7d0fe Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Wed, 16 Aug 2023 14:15:21 +0700 Subject: [PATCH 17/54] test: ibc app --- src/services/ibc/crawl_ibc_app.service.ts | 2 +- test/unit/services/ibc/crawl_ibc_app.spec.ts | 472 +++++++++++++++++++ 2 files changed, 473 insertions(+), 1 deletion(-) create mode 100644 test/unit/services/ibc/crawl_ibc_app.spec.ts diff --git a/src/services/ibc/crawl_ibc_app.service.ts b/src/services/ibc/crawl_ibc_app.service.ts index 5da8eaa40..c28392aff 100644 --- a/src/services/ibc/crawl_ibc_app.service.ts +++ b/src/services/ibc/crawl_ibc_app.service.ts @@ -97,7 +97,7 @@ export default class CrawlIbcAppService extends BullableService { type: event.type, sequence, sequence_key: `${srcChannel}.${srcPort}.${dstChannel}.${dstPort}.${sequence}`, - data: fromUtf8(fromHex(dataHex)), + data: dataHex ? fromUtf8(fromHex(dataHex)) : null, }); }); if (ibcMessage.length > 0) { diff --git a/test/unit/services/ibc/crawl_ibc_app.spec.ts b/test/unit/services/ibc/crawl_ibc_app.spec.ts new file mode 100644 index 000000000..f1776e01d --- /dev/null +++ b/test/unit/services/ibc/crawl_ibc_app.spec.ts @@ -0,0 +1,472 @@ +import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { ServiceBroker } from 'moleculer'; +import knex from '../../../../src/common/utils/db_connection'; +import { Block, Event, IbcMessage, Transaction } from '../../../../src/models'; +import CrawlIbcAppService from '../../../../src/services/ibc/crawl_ibc_app.service'; + +@Describe('Test crawl ibc service') +export default class CrawlIbcTest { + broker = new ServiceBroker({ logger: false }); + + crawlIbcAppSerivce = this.broker.createService( + CrawlIbcAppService + ) as CrawlIbcAppService; + + block: Block = Block.fromJson({ + height: 1300000, + hash: '4801997745BDD354C8F11CE4A4137237194099E664CD8F83A5FBA9041C43FE9F', + time: '2023-01-12T01:53:57.216Z', + proposer_address: 'auraomd;cvpio3j4eg', + data: {}, + }); + + transaction = { + ...Transaction.fromJson({ + height: this.block.height, + hash: '4A8B0DE950F563553A81360D4782F6EC451F6BEF7AC50E2459D1997FA168997D', + codespace: '', + code: 0, + gas_used: '123035', + gas_wanted: '141106', + gas_limit: '141106', + fee: 353, + timestamp: '2023-01-12T01:53:57.000Z', + index: 0, + data: { + tx_response: { + logs: [], + }, + }, + }), + messages: [ + { + index: 1, + type: '/cosmwasm.wasm.v1.MsgExecuteContract', + sender: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + content: { + msg: '{"add_mint_phase":{"phase_data":{"start_time":"1679976124941000000","end_time":"1679982024941000000","max_supply":2000,"max_nfts_per_address":20,"price":{"amount":"10","denom":"ueaura"},"is_public":false},"token_id": "test"}}', + '@type': '/cosmwasm.wasm.v1.MsgExecuteContract', + funds: [], + sender: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + }, + }, + ], + }; + + @BeforeAll() + async initSuite() { + this.crawlIbcAppSerivce.getQueueManager().stopAll(); + await knex.raw( + 'TRUNCATE TABLE block, transaction, ibc_message RESTART IDENTITY CASCADE' + ); + await Block.query().insert(this.block); + await Transaction.query().insertGraph(this.transaction); + } + + @AfterAll() + async tearDown() { + await this.broker.stop(); + } + + @Test('Test HandleIbcSendPacket') + async testHandleIbcSendPacket() { + await knex.transaction(async (trx) => { + const content = { + '@type': '/ibc.applications.transfer.v1.MsgTransfer', + token: { + denom: 'ueaura', + amount: '2000000', + }, + sender: 'aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw', + receiver: 'axelar1qqq4cecm6yvaep46729096urqq30k3kp4repqk', + source_port: 'transfer', + source_channel: 'channel-5', + timeout_height: { + revision_height: '5272159', + revision_number: '3', + }, + timeout_timestamp: '0', + }; + const attributes = [ + { + key: 'packet_data', + value: + '{"amount":"2000000","denom":"ueaura","receiver":"axelar1qqq4cecm6yvaep46729096urqq30k3kp4repqk","sender":"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw"}', + event_id: '1', + }, + { + event_id: '1', + key: 'packet_data_hex', + value: + '7b22616d6f756e74223a2232303030303030222c2264656e6f6d223a22756561757261222c227265636569766572223a226178656c617231717171346365636d36797661657034363732393039367572717133306b336b7034726570716b222c2273656e646572223a226175726131717171346365636d36797661657034363732393039367572717133306b336b70326d63746677227d', + }, + { + event_id: '1', + key: 'packet_timeout_height', + value: '3-5272159', + }, + { + event_id: '1', + key: 'packet_timeout_timestamp', + value: '0', + }, + { + event_id: '1', + key: 'packet_sequence', + value: '68', + }, + { + event_id: '1', + key: 'packet_src_port', + value: 'transfer', + }, + { + event_id: '1', + key: 'packet_src_channel', + value: 'channel-5', + }, + { + event_id: '1', + key: 'packet_dst_port', + value: 'transfer', + }, + { + event_id: '1', + key: 'packet_dst_channel', + value: 'channel-48', + }, + { + event_id: '1', + key: 'packet_channel_ordering', + value: 'ORDER_UNORDERED', + }, + { + event_id: '1', + key: 'packet_connection', + value: 'connection-3', + }, + ]; + const event = Event.fromJson({ + type: IbcMessage.EVENT_TYPE.SEND_PACKET, + attributes, + content, + message_id: 1, + }); + await this.crawlIbcAppSerivce.handleIbcMessage([event], trx); + const newPacket = await IbcMessage.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newPacket.src_channel_id).toEqual(attributes[6].value); + expect(newPacket.src_port_id).toEqual(attributes[5].value); + expect(newPacket.dst_port_id).toEqual(attributes[7].value); + expect(newPacket.dst_channel_id).toEqual(attributes[8].value); + expect(newPacket.type).toEqual(event.type); + expect(newPacket.sequence).toEqual(parseInt(attributes[4].value, 10)); + expect(newPacket.data).toEqual(JSON.parse(attributes[0].value)); + await trx.rollback(); + }); + } + + @Test('Test HandleIbcReceivePacket') + async testHandleIbcReceivePacket() { + await knex.transaction(async (trx) => { + const content = { + '@type': '/ibc.core.channel.v1.MsgRecvPacket', + packet: { + data: 'eyJzb3VyY2VfaWQiOiJkcmFuZDo4OTkwZTdhOWFhZWQyZmZlZDczZGJkNzA5MjEyM2Q2ZjI4OTkzMDU0MGQ3NjUxMzM2MjI1ZGMxNzJlNTFiMmNlOjI1MTg3NDMiLCJyYW5kb21uZXNzIjoiYzY3ZmI4NzBmZDkyOTY1YmJjOTIzMjFiYmZlNzEyZmE3ZDhlZTI3NzUwNzg5NDdjOGIxYmZjZTcwOTIwNGRjNyIsInNlbmRlciI6ImF1cmExbW5kY2V1Y3czYzZqOXNxanFkbWxzNGNmcHhrNWUwbTRwMGF4ZnJwN3p3c2ZuM2tuZmg0czJ5a2Z6cyIsImpvYl9pZCI6ImpvYiB0ZXN0In0=', + sequence: '1', + source_port: + 'wasm.nois1s9ly26evj8ehurptws5d6dm4a9g2z0htcqvlvn95kc30eucl4s5sd8hkgp', + source_channel: 'channel-24', + destination_port: + 'wasm.aura1qrf8f9kyh4zzckz2zy52z5gppwweumvrlxqrgd4xr3ydf3sx4dlqt8lnt8', + timeout_timestamp: '1671007842016312592', + destination_channel: 'channel-15', + }, + signer: 'aura1rynf2jnj38sh96xztpp4f7v93ceefp0592kqu9', + proof_height: { + revision_height: '2051376', + revision_number: '0', + }, + proof_commitment: + 'CrUICrIICnZjb21taXRtZW50cy9wb3J0cy93YXNtLm5vaXMxczlseTI2ZXZqOGVodXJwdHdzNWQ2ZG00YTlnMnowaHRjcXZsdm45NWtjMzBldWNsNHM1c2Q4aGtncC9jaGFubmVscy9jaGFubmVsLTI0L3NlcXVlbmNlcy8xEiBr6YV/hrYcXxxkci3Xg1iZO+R16mZdozncKdfF2Gk63hoOCAEYASABKgYAAra0+gEiLAgBEigCBNy0+gEgkbRLrBYqrFQSnylScee3SDJG9IoyvSE1NNKbhkMHjOkgIiwIARIoBAjctPoBIFJzzlvVwrtiCBp6IW4mz1U331PxUnDG3SWldAaUkqfKICIsCAESKAYO3LT6ASCjyAp5hOD2V8Ief2zE1EHS9ij+4lqh8GuQFftw1HGXySAiLggBEgcIHNy0+gEgGiEgJwCb/SGlVkH+fKcOy+VGdm52r9/CAIOXf3YJiLqBB1EiLAgBEigMRty0+gEg/Ydg1h01rorKZB7q9yuNLzyIogB6CEsbkyAbvDpoRZ0gIiwIARIoDnbctPoBIJc1GUCFYMDe51mf7/PX8k7Nsygm/97skEy3Md/hB9TSICItCAESKRCwAty0+gEgGY4ZdbCvnIfCl6jIyIpEYN3s27Fr1vyz8R3IHvX3Xm4gIi0IARIpErAF3LT6ASCRW2Sj0oCdYO4sfTggs1obpqP0SpyxZ5MNwnDNxOttzCAiLQgBEikUvAnctPoBIKc6KVNR+IjCyjUf5EmvbhCZ4ECjFfvkTS07NGG6KatHICItCAESKRb8Ddy0+gEgIk0VggsrBqM0r7bRn8tGGGb/F0OAsrkFEQlX/iIieCAgIi0IARIpGNIo3LT6ASCzl9mIXiGdiLc3Uxffs3deucwOkYLHyrOPT43POSwokSAiLwgBEggawDbctPoBIBohIDh+V4oXbhCCw2amCons5wswvWLj9GrgDmXX9ZyHzSnHIi4IARIqHsycAdy0+gEgd8O9IlSfy76C4PEut+OmsBaczmZAQIk+9lKXjKi5tPQgIi4IARIqIOSKBNy0+gEgRGhbs7TW2G7HEGOkWlnscrIJzrGFK7Av1al0Kn0evkIgIjAIARIJIu6GBdy0+gEgGiEgdYbS/e3tGdCHBK0jStUByTHfWzSAgSTuPFSABAtrvUIiMAgBEgkknPEG3LT6ASAaISAknxyxnWyS0b5tJENZdboudAgOecP6nXWoNxKCyEviaiIwCAESCSa2zw3ctPoBIBohIPNrCQdaLVoHfv59FzZZ0mu0dpj4s1yHQJDSXgI9LgyXIi4IARIqKIzpIdy0+gEgO8+y5H2wuEYl5+E29n5CHoa9L0Gv7VDK/c18U3RbpbggIi4IARIqLMjqSty0+gEg1bxzwt2bpr7IcwoFmALxeBtNJnp9orfUEwo/J2ejlo8gCoACCv0BCgNpYmMSIKpvHTP9V5wDV+p0HZXGImeQnu1m2wR4GuhY+MtrIZQNGgkIARgBIAEqAQAiJwgBEgEBGiDFBcD9SLHPK2VhnxKzFE4ZxgtOa2JSXuk2vpPQQWI+vyInCAESAQEaIB383KmKPeE797r2aZUc+sDGl1kl76j7wuBRZXW53aliIicIARIBARogHzoHZq3bsksS+szusIxZLYKclF851/17/U+U55WvjH0iJQgBEiEBh8vGkXW12Po8VvqN1YvoDbUvWlI7YprnoRvl4apyqUQiJwgBEgEBGiBWtkpHzlP2mQZ1Go55X55ylakOo+xlkABwsGtDJUAQ5Q==', + }; + const attributes = [ + { + event_id: 1, + key: 'packet_data', + value: + '{"source_id":"drand:8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce:2518743","randomness":"c67fb870fd92965bbc92321bbfe712fa7d8ee2775078947c8b1bfce709204dc7","sender":"aura1mndceucw3c6j9sqjqdmls4cfpxk5e0m4p0axfrp7zwsfn3knfh4s2ykfzs","job_id":"job test"}', + }, + { + event_id: 1, + key: 'packet_data_hex', + value: + '7b22736f757263655f6964223a226472616e643a383939306537613961616564326666656437336462643730393231323364366632383939333035343064373635313333363232356463313732653531623263653a32353138373433222c2272616e646f6d6e657373223a2263363766623837306664393239363562626339323332316262666537313266613764386565323737353037383934376338623162666365373039323034646337222c2273656e646572223a2261757261316d6e6463657563773363366a3973716a71646d6c7334636670786b3565306d3470306178667270377a7773666e336b6e6668347332796b667a73222c226a6f625f6964223a226a6f622074657374227d', + }, + { + event_id: 1, + key: 'packet_timeout_height', + value: '0-0', + }, + { + event_id: 1, + key: 'packet_timeout_timestamp', + value: '1671007842016312592', + }, + { + event_id: 1, + key: 'packet_sequence', + value: '1', + }, + { + event_id: 1, + key: 'packet_src_port', + value: + 'wasm.nois1s9ly26evj8ehurptws5d6dm4a9g2z0htcqvlvn95kc30eucl4s5sd8hkgp', + }, + { + event_id: 1, + key: 'packet_src_channel', + value: 'channel-24', + }, + { + event_id: 1, + key: 'packet_dst_port', + value: + 'wasm.aura1qrf8f9kyh4zzckz2zy52z5gppwweumvrlxqrgd4xr3ydf3sx4dlqt8lnt8', + }, + { + event_id: 1, + key: 'packet_dst_channel', + value: 'channel-15', + }, + { + event_id: 1, + key: 'packet_channel_ordering', + value: 'ORDER_UNORDERED', + }, + { + event_id: 1, + key: 'packet_connection', + value: 'connection-18', + }, + ]; + const event = Event.fromJson({ + type: IbcMessage.EVENT_TYPE.RECV_PACKET, + attributes, + content, + message_id: 1, + }); + await this.crawlIbcAppSerivce.handleIbcMessage([event], trx); + const newPacket = await IbcMessage.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newPacket.src_channel_id).toEqual(attributes[6].value); + expect(newPacket.src_port_id).toEqual(attributes[5].value); + expect(newPacket.dst_port_id).toEqual(attributes[7].value); + expect(newPacket.dst_channel_id).toEqual(attributes[8].value); + expect(newPacket.type).toEqual(event.type); + expect(newPacket.sequence).toEqual(parseInt(attributes[4].value, 10)); + expect(newPacket.data).toEqual(JSON.parse(attributes[0].value)); + await trx.rollback(); + }); + } + + @Test('Test HandleIbcAckPacket') + async testHandleIbcAckPacket() { + await knex.transaction(async (trx) => { + const content = { + '@type': '/ibc.core.channel.v1.MsgAcknowledgement', + packet: { + data: { + denom: 'ueaura', + amount: '2000000', + sender: 'aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw', + receiver: 'axelar1qqq4cecm6yvaep46729096urqq30k3kp4repqk', + }, + sequence: '70', + source_port: 'transfer', + source_channel: 'channel-5', + timeout_height: { + revision_height: '5272433', + revision_number: '3', + }, + destination_port: 'transfer', + timeout_timestamp: '0', + destination_channel: 'channel-48', + }, + signer: 'aura144uvpm209vujung88063yguqu2t4pxam8qlzcl', + proof_acked: + 'CvcHCvQHCjRhY2tzL3BvcnRzL3RyYW5zZmVyL2NoYW5uZWxzL2NoYW5uZWwtNDgvc2VxdWVuY2VzLzcwEiDk4mS81on8LJWRjs2NgRMw+qa5Riss4mL5z7CaATu7zxoOCAEYASABKgYAAtTLgwUiLAgBEigCBNTLgwUgsY73NAQp95aEYITDO1TYaZ89+2YcDYor+a1LC4XJctkgIi4IARIHBAbUy4MFIBohIIlBYKacV7IR2q8WXyJnVWmAeZVic61uXSehtFr2y702Ii4IARIHBgrUy4MFIBohIDHUbUw8RoCZl2Wx0dz54+WUsbBfDa6Zmexb/7ds7FrlIiwIARIoCBLUy4MFIJcwzVz1/aEQTrPbUSXeOu6V0GIwR9B5Fe4LgNPGP2HsICIsCAESKAoq1MuDBSBEUv4A7XistOZCvSLVYneqE5eyJVL71u8dlQM269ks1CAiLggBEgcMRNTLgwUgGiEgK4rDgfNiiMmzxJCBnFgfdk7gotZJUwuTisUwWIALky8iLAgBEigOcNTLgwUggR67/Cj4/9HbvCMEVApYUOyrpNKPi+ixw3Hu69oOEM0gIi0IARIpEL4B1MuDBSBLtWls5uYSbwyCEqFrRX0qrkskHYFpxl6iMqUsbg15TCAiLQgBEikUhAbUy4MFIGPlCJx+DRlvaAdVOlmkdR4yrwa12u7X+MNuJWnstcm+ICItCAESKRbQDNTLgwUgne8KWVfwUTHwtKt+N4JeuLA2eTdMLTPae16sBleZ9i8gIi8IARIIGJwU1MuDBSAaISAlyQEy4co8HScp9mOwxmT+54xC60l/RujMLZ0UQY1D1yItCAESKRqoJ9TLgwUg4/16hHKjfU/b/cGJ/elVImTflmDbNGJTUmH7TyAHDY4gIi0IARIpHJ5P1MuDBSCrlFOsxMBTwG/ULmdZd1FxNzMclVji1hITNEIjuTzeUiAiMAgBEgkeloQB1MuDBSAaISBamzC6oAWPluSakQhJDDL28Ick6W7bzK3Wci+zaQ5ZmCIwCAESCSCYgQLUy4MFIBohIO2Q0PKbX4ycf0DLLoiM46g6/cIGa/YuXoGnybF/ajXcIi4IARIqIuTGA9TLgwUgl7ev7UVaOt4Y+2+3MzrCd4I5tCy/QnXC6HW2cS3Jw/AgIi4IARIqJNLIBtTLgwUgIqkmWLJRQoaumYnv30l160wf56bqxe5VlqeyNKdvnd0gIjAIARIJKMrtFNTLgwUgGiEgAGipp/NfgeyJ/oAnXFeaxChIBfsJIYlHqPerz5RYV18iMAgBEgkqvOgg1MuDBSAaISBvQKHSn0bGkrQOLwDrR0dLpqOWo13BUGWEZD9cuYS5owr+AQr7AQoDaWJjEiAXBMSV5wfjh6PYbvEp/1nimbltZtOVgouKXb0KZtuXxxoJCAEYASABKgEAIiUIARIhAYaEml36WNEYpP9Nh1yNmrYXZTzq4bYigQ5h/xryTzNeIicIARIBARogFc/U9A/9Uia3YLYvLg9IUjtNLuXbpHoWAIptWKyS3/UiJwgBEgEBGiBjazpYRtM9Y+HZBtOQHXcWeJo3Ay4wqT//MpJ91bxz/CIlCAESIQHU1rzKX7ZidvkTePQ5DYHMxbnqKGC8jQoT9fF3ninSTyInCAESAQEaIEB8bwhFio2i6oAVI0jph4+6adeJMizyI8IFM6iUM+gK', + proof_height: { + revision_height: '5272299', + revision_number: '3', + }, + acknowledgement: { + error: + 'ABCI code: 1: error handling packet on destination chain: see events for details', + }, + }; + const attributes = [ + { + key: 'packet_timeout_height', + value: '3-5272433', + event_id: 1, + }, + { + key: 'packet_timeout_timestamp', + value: '0', + event_id: 1, + }, + { + key: 'packet_sequence', + value: '70', + event_id: 1, + }, + { + key: 'packet_src_port', + value: 'transfer', + event_id: 1, + }, + { + key: 'packet_src_channel', + value: 'channel-5', + event_id: 1, + }, + { + key: 'packet_dst_port', + value: 'transfer', + event_id: 1, + }, + { + key: 'packet_dst_channel', + value: 'channel-48', + event_id: 1, + }, + { + key: 'packet_channel_ordering', + value: 'ORDER_UNORDERED', + event_id: 1, + }, + { + key: 'packet_connection', + value: 'connection-3', + event_id: 1, + }, + ]; + const event = Event.fromJson({ + type: IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET, + attributes, + content, + message_id: 1, + }); + await this.crawlIbcAppSerivce.handleIbcMessage([event], trx); + const newPacket = await IbcMessage.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newPacket.src_channel_id).toEqual(attributes[4].value); + expect(newPacket.src_port_id).toEqual(attributes[3].value); + expect(newPacket.dst_port_id).toEqual(attributes[5].value); + expect(newPacket.dst_channel_id).toEqual(attributes[6].value); + expect(newPacket.type).toEqual(event.type); + expect(newPacket.sequence).toEqual(parseInt(attributes[2].value, 10)); + expect(newPacket.data).toEqual(null); + await trx.rollback(); + }); + } + + @Test('Test HandleIbcTimeoutPacket') + async testHandleIbcTimeoutPacket() { + await knex.transaction(async (trx) => { + const content = { + '@type': '/ibc.core.channel.v1.MsgTimeout', + packet: { + data: 'eyJhbW91bnQiOiIxMDAwIiwiZGVub20iOiJ1ZWF1cmEiLCJyZWNlaXZlciI6ImF4ZWxhcjF4cW4ydG5yZTg0Y211d2Z1ZGZmcnd4bXFrMDcwMnk2ZjNuNmwzZyIsInNlbmRlciI6ImF1cmExOGh3eWZ5dHZqY3VoN2praDR1ZTluNjBoMjR4cDd0bndoamh5bTYifQ==', + sequence: '76', + source_port: 'transfer', + source_channel: 'channel-5', + timeout_height: { + revision_height: '6254585', + revision_number: '3', + }, + destination_port: 'transfer', + timeout_timestamp: '1676722677558827165', + destination_channel: 'channel-48', + }, + signer: 'aura1wzjuznqenty8ryv43sq08w6rp8g25hye7wmy38', + proof_height: { + revision_height: '6268541', + revision_number: '3', + }, + proof_unreceived: + 'CogPEoUPCjhyZWNlaXB0cy9wb3J0cy90cmFuc2Zlci9jaGFubmVscy9jaGFubmVsLTQ4L3NlcXVlbmNlcy83NhKhBwo4cmVjZWlwdHMvcG9ydHMvdHJhbnNmZXIvY2hhbm5lbHMvY2hhbm5lbC00OC9zZXF1ZW5jZXMvNzUSAQEaDggBGAEgASoGAAKe48MFIiwIARIoAgSe48MFIN3Vb+hlVv+zvhy3kd1THkOR4SFjcrTbuQqXwiNhpFMCICIsCAESKAQGnuPDBSBQTbdqCqNqQAB53hvr5gfuesvX2fn9OK0XQZJ3Zzxf5yAiLAgBEigGCp7jwwUg1nx/OtK/sGaeHNT+VJWN50hwXGWsoo5dBfqE+a8ICfcgIi4IARIHCiKIw98FIBohIDvfZH45Z13if/Ny+54SgV2U2FJ3jpcABF9qpE1482ECIiwIARIoDDqIw98FINku+gKJjsRa5Tfa8Y2R4lR1kY+IYxCWALmhFTSTfuZ/ICIsCAESKA5kiMPfBSCBI5WGqXmXQBcRaVXB2YEeIkgph1r3xyMGXnwmwYflySAiLwgBEggQ2gGIw98FIBohIDsamD3Lpmx3oq+BFt9vIDnt1Bcg05Sf+uxGVYtKLBvWIi0IARIpEpQDiMPfBSAZUDMl3fWVGqTezqlP5Lr6uBst/u54+SoyHvhNPzHR5yAiLQgBEikW8AiIw98FIPZJTLCCsasYB8vDD8rXw//7MNlwTdWata9S3BWxByAaICItCAESKRjAFIjD3wUgxNwUCnbU/eE32+rLWKNzlX/E7cUVRoPG5ZwsBybaOnUgIi0IARIpGsQuiMPfBSBwGtRH1qq46BwlB730ILSQznSPI+1QULj7baSTsIFpXiAiLQgBEikc9lmIw98FIDOzBeIRZD0ocOttUekUn1lZqO6eQn7A68yOnia/dRc5ICIwCAESCR78rwGAlP0FIBohIHCzxdFnxRJKnEJSqq/WYTx9eYyxnicsfOh1fWMTIS4QIi4IARIqILTBAoCU/QUgDUn8gD4vL/pSDdsWPY6OoIm3J974x5a6TvS7zV5wcF8gIi4IARIqIviLBMiZ/QUguuzhS+i2AlQNzB63FzFX2AiplTASFAwTE9oaEVaYxDEgIi4IARIqJKr2BciZ/QUgGowiOzih90Ccv8pByzwI30hR9bsSjpcYugnG1xrwOk8gIi4IARIqJsLADM6Z/QUgTdCIyxxcM2IGhzd8mp15gJ60UcXWJZGFWTirLvFOYW0gIi4IARIqKoDVJO6Z/QUg3HlPWIXUn3ZmEKOWkF8DIVrYUwIn7XOrsGN74R7XARggGqQHCjdyZWNlaXB0cy9wb3J0cy90cmFuc2Zlci9jaGFubmVscy9jaGFubmVsLTQ4L3NlcXVlbmNlcy84EgEBGg4IARgBIAEqBgACqP6uAyIuCAESBwIEyKGYBSAaISC1E8GUqxWmC4V03fic1pXfM67XWqCJKpwePLpmsWhS1SIuCAESBwYKiMPfBSAaISCoHpaUcIJJ5Y+Dir4rs0v92ln95UGyawStiRtpaP7aaiIuCAESBwgYiMPfBSAaISDC93Qf3Dmgi3XzR1KBcKryu7m5MHA7pCqPWlWp8AKrLiIsCAESKAoiiMPfBSD3LEp2vtbcelb8GZ8MMbNWtWM2BqpnOAxr3LhO4oMXpyAiLAgBEigMOojD3wUg2S76AomOxFrlN9rxjZHiVHWRj4hjEJYAuaEVNJN+5n8gIiwIARIoDmSIw98FIIEjlYapeZdAFxFpVcHZgR4iSCmHWvfHIwZefCbBh+XJICIvCAESCBDaAYjD3wUgGiEgOxqYPcumbHeir4EW328gOe3UFyDTlJ/67EZVi0osG9YiLQgBEikSlAOIw98FIBlQMyXd9ZUapN7OqU/kuvq4Gy3+7nj5KjIe+E0/MdHnICItCAESKRbwCIjD3wUg9klMsIKxqxgHy8MPytfD//sw2XBN1Zq1r1LcFbEHIBogIi0IARIpGMAUiMPfBSDE3BQKdtT94Tfb6stYo3OVf8TtxRVGg8blnCwHJto6dSAiLQgBEikaxC6Iw98FIHAa1EfWqrjoHCUHvfQgtJDOdI8j7VBQuPttpJOwgWleICItCAESKRz2WYjD3wUgM7MF4hFkPShw621R6RSfWVmo7p5CfsDrzI6eJr91FzkgIjAIARIJHvyvAYCU/QUgGiEgcLPF0WfFEkqcQlKqr9ZhPH15jLGeJyx86HV9YxMhLhAiLggBEiogtMECgJT9BSANSfyAPi8v+lIN2xY9jo6gibcn3vjHlrpO9LvNXnBwXyAiLggBEioi+IsEyJn9BSC67OFL6LYCVA3MHrcXMVfYCKmVMBIUDBMT2hoRVpjEMSAiLggBEiokqvYFyJn9BSAajCI7OKH3QJy/ykHLPAjfSFH1uxKOlxi6CcbXGvA6TyAiLggBEiomwsAMzpn9BSBN0IjLHFwzYgaHN3yanXmAnrRRxdYlkYVZOKsu8U5hbSAiLggBEioqgNUk7pn9BSDceU9YhdSfdmYQo5aQXwMhWthTAiftc6uwY3vhHtcBGCAK/gEK+wEKA2liYxIgXNTiISTgnF7vIC4drLL5j19W79dvWWv4j6s35pfRAFAaCQgBGAEgASoBACIlCAESIQFmzX0n9gBWiQS0wk2PDkiUg/OVlZC+Yx5kx+ZxMn5c4CInCAESAQEaIAKyhN/eeksFqt2XbKyKfbvvJ/kLFo0F9fHbnPiocTHyIicIARIBARog1CdJ3IWWPMBZMOQbHImE2JZNOfV9VeJtolbOdwBoIC0iJQgBEiEBG1sS5rbCFpKt+VeumIkXhBwDI5WnBrX3wtzlsmFbMMMiJwgBEgEBGiBT7wAf0cZ/wVLGRzF/wbvikxZ8IHEPnBxaLMXcVhukAg==', + next_sequence_recv: '76', + }; + const attributes = [ + { + key: 'packet_timeout_height', + value: '3-6254585', + event_id: 1, + }, + { + key: 'packet_timeout_timestamp', + value: '1676722677558827165', + event_id: 1, + }, + { + key: 'packet_sequence', + value: '76', + event_id: 1, + }, + { + key: 'packet_src_port', + value: 'transfer', + event_id: 1, + }, + { + key: 'packet_src_channel', + value: 'channel-5', + event_id: 1, + }, + { + key: 'packet_dst_port', + value: 'transfer', + event_id: 1, + }, + { + key: 'packet_dst_channel', + value: 'channel-48', + event_id: 1, + }, + { + key: 'packet_channel_ordering', + value: 'ORDER_UNORDERED', + event_id: 1, + }, + ]; + const event = Event.fromJson({ + type: IbcMessage.EVENT_TYPE.TIMEOUT_PACKET, + attributes, + content, + message_id: 1, + }); + await this.crawlIbcAppSerivce.handleIbcMessage([event], trx); + const newPacket = await IbcMessage.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newPacket.src_channel_id).toEqual(attributes[4].value); + expect(newPacket.src_port_id).toEqual(attributes[3].value); + expect(newPacket.dst_port_id).toEqual(attributes[5].value); + expect(newPacket.dst_channel_id).toEqual(attributes[6].value); + expect(newPacket.type).toEqual(event.type); + expect(newPacket.sequence).toEqual(parseInt(attributes[2].value, 10)); + expect(newPacket.data).toEqual(null); + await trx.rollback(); + }); + } +} From a5a406ab0a0014255fce97296067ecbe9a3973f5 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Mon, 21 Aug 2023 09:47:21 +0700 Subject: [PATCH 18/54] fix: review --- src/models/ibc_message.ts | 3 --- src/services/ibc/crawl_ibc_app.service.ts | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/models/ibc_message.ts b/src/models/ibc_message.ts index 54845c24d..c393c3a2b 100644 --- a/src/models/ibc_message.ts +++ b/src/models/ibc_message.ts @@ -5,8 +5,6 @@ import { IbcChannel } from './ibc_channel'; import { TransactionMessage } from './transaction_message'; export class IbcMessage extends BaseModel { - [relation: string]: any | any[]; - id!: number; transaction_message_id!: number; @@ -53,7 +51,6 @@ export class IbcMessage extends BaseModel { type: { type: 'string' }, sequence: { type: 'number' }, sequence_key: { type: 'string' }, - status: { type: 'boolean' }, }, }; } diff --git a/src/services/ibc/crawl_ibc_app.service.ts b/src/services/ibc/crawl_ibc_app.service.ts index c28392aff..7ae993bf3 100644 --- a/src/services/ibc/crawl_ibc_app.service.ts +++ b/src/services/ibc/crawl_ibc_app.service.ts @@ -63,7 +63,7 @@ export default class CrawlIbcAppService extends BullableService { } async handleIbcMessage(events: Event[], trx: Knex.Transaction) { - const ibcMessage = events.map((event) => { + const ibcMessages = events.map((event) => { const srcChannel = getAttributeFrom( event.attributes, EventAttribute.ATTRIBUTE_KEY.SRC_CHANNEL @@ -100,8 +100,8 @@ export default class CrawlIbcAppService extends BullableService { data: dataHex ? fromUtf8(fromHex(dataHex)) : null, }); }); - if (ibcMessage.length > 0) { - await IbcMessage.query().insert(ibcMessage).transacting(trx); + if (ibcMessages.length > 0) { + await IbcMessage.query().insert(ibcMessages).transacting(trx); } } From 887ecfd00aa562c05b2ebd3a6fcb938031c215af Mon Sep 17 00:00:00 2001 From: Vu Ngoc Quang Date: Tue, 22 Aug 2023 15:29:55 +0700 Subject: [PATCH 19/54] Feat/statistics (#272) * feat: move statistic feature from v1 to v2 (not tested yet) * feat: change job params so it can aggregate data at a specific date * fix: query fields from exact table * fix: query exact fields * feat: add logger when done * feat: finish statistic jobs (not tested) * feat: create a separate interval job that gets the current date and create statistics jobs with date * feat: update logic using dayjs and lodash lib * feat: update cron jobs logic, move api actions to its service folder * fix: query event from db and group it based on event_id and tx_id * fix: just use a single job to query all daily data * fix: move all queries in daily_stats job into a single Promise all * fix: move dayjs.extend to after import statements * fix: remove _start * fix: only count native token in account_stats job * feat: add api to sync stats from prev dates * fix: support case when user just pass startDate to api * fix: fix whitelist graphql * fix: update column in account_statistic table, update bignum top_tx_sent * fix: remove drop index in modify account statistic table --------- Co-authored-by: AnDQK Co-authored-by: Phan Anh Tuan --- ci/config.json.ci | 10 + config.json | 10 + ..._create_table_daily_stats_account_stats.ts | 26 + ...6_modify_column_table_account_statistic.ts | 13 + package.json | 1 + src/common/constant.ts | 25 + src/common/utils/request.ts | 8 + src/models/account_statistics.ts | 56 + src/models/daily_statistics.ts | 37 + src/models/event.ts | 4 + src/models/event_attribute.ts | 25 +- src/models/index.ts | 2 + .../api-gateways/api_gateway.service.ts | 12 +- .../dashboard_statistics.service.ts | 52 - .../api-gateways/statistics.service.ts | 99 ++ .../statistics/account_statistics.service.ts | 420 +++++ .../statistics/api_statistics.service.ts | 91 + .../statistics/daily_statistics.service.ts | 127 ++ .../statistics/daily_stats_jobs.service.ts | 59 + .../dashboard_statistics.service.ts | 116 +- yarn.lock | 1551 +++++++++-------- 21 files changed, 1872 insertions(+), 872 deletions(-) create mode 100644 migrations/20230704102018_create_table_daily_stats_account_stats.ts create mode 100644 migrations/20230817015916_modify_column_table_account_statistic.ts create mode 100644 src/models/account_statistics.ts create mode 100644 src/models/daily_statistics.ts delete mode 100644 src/services/api-gateways/dashboard_statistics.service.ts create mode 100644 src/services/api-gateways/statistics.service.ts create mode 100644 src/services/statistics/account_statistics.service.ts create mode 100644 src/services/statistics/api_statistics.service.ts create mode 100644 src/services/statistics/daily_statistics.service.ts create mode 100644 src/services/statistics/daily_stats_jobs.service.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index 31bcf5173..bf53142cf 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -162,6 +162,16 @@ "dispatchMilisecond": 1000, "batchSizeLimit": 10 }, + "dailyStatistics": { + "recordsPerCall": 100 + }, + "accountStatistics": { + "numberOfTopRecords": 10, + "dayRange": [3, 15, 30] + }, + "dailyStatsJobs": { + "jobPattern": "0 0 0 * * ?" + }, "jobRedecodeTx": { "limitRecordGet": 100 } diff --git a/config.json b/config.json index e2b66e521..d6766950a 100644 --- a/config.json +++ b/config.json @@ -162,6 +162,16 @@ "dispatchMilisecond": 1000, "batchSizeLimit": 10 }, + "dailyStatistics": { + "recordsPerCall": 100 + }, + "accountStatistics": { + "numberOfTopRecords": 10, + "dayRange": [3, 15, 30] + }, + "dailyStatsJobs": { + "jobPattern": "0 0 0 * * ?" + }, "jobRedecodeTx": { "limitRecordGet": 100 } diff --git a/migrations/20230704102018_create_table_daily_stats_account_stats.ts b/migrations/20230704102018_create_table_daily_stats_account_stats.ts new file mode 100644 index 000000000..20c24a761 --- /dev/null +++ b/migrations/20230704102018_create_table_daily_stats_account_stats.ts @@ -0,0 +1,26 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('daily_statistics', (table: any) => { + table.increments(); + table.bigint('daily_txs').index().notNullable(); + table.bigint('daily_active_addresses').index().notNullable(); + table.bigint('unique_addresses').index().notNullable(); + table.timestamp('date').unique().notNullable(); + }); + await knex.schema.createTable('account_statistics', (table: any) => { + table.increments(); + table.string('address').index().notNullable(); + table.bigint('amount_sent').index().notNullable(); + table.bigint('amount_received').index().notNullable(); + table.bigint('tx_sent').index().notNullable(); + table.bigint('gas_used').index().notNullable(); + table.timestamp('date').index().notNullable(); + table.unique(['address', 'date']); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTable('account_statistics'); + await knex.schema.dropTable('daily_statistics'); +} diff --git a/migrations/20230817015916_modify_column_table_account_statistic.ts b/migrations/20230817015916_modify_column_table_account_statistic.ts new file mode 100644 index 000000000..0912a4d56 --- /dev/null +++ b/migrations/20230817015916_modify_column_table_account_statistic.ts @@ -0,0 +1,13 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable('account_statistics', (table) => { + table.integer('tx_sent').alter(); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable('account_statistics', (table) => { + table.bigint('tx_sent').alter(); + }); +} diff --git a/package.json b/package.json index 968b55113..a97b72840 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "bull": "^4.10.2", "bullmq": "^3.13.3", "cosmjs-types": "^0.6.1", + "dayjs": "^1.11.9", "dotenv": "^16.0.3", "file-type": "^18.4.0", "graphql": "^16.6.0", diff --git a/src/common/constant.ts b/src/common/constant.ts index 624cb63ec..9e08c3600 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -1,6 +1,7 @@ export const REDIS_KEY = { IBC_DENOM: 'ibc_denom', DASHBOARD_STATISTICS: 'dashboard_statistics', + TOP_ACCOUNTS: 'top_accounts', }; export const URL_TYPE_CONSTANTS = { @@ -62,6 +63,10 @@ export const BULL_JOB_NAME = { 'job:check-need-create-event-attr-partition', JOB_CREATE_EVENT_ATTR_PARTITION: 'job:create-event-attr-partition', CRAWL_GENESIS_FEEGRANT: 'crawl:genesis-feegrant', + CRAWL_DAILY_STATISTICS: 'crawl:daily-statistics', + CRAWL_ACCOUNT_STATISTICS: 'crawl:account-statistics', + HANDLE_TOP_ACCOUNTS: 'handle:top-accounts', + HANDLE_DAILY_STATS_JOBS: 'handle:daily-stats-jobs', REINDEX_CW721_CONTRACT: 'reindex:cw721-contract', REINDEX_CW721_HISTORY: 'reindex:cw721-history', HANDLE_MIGRATE_CONTRACT: 'handle:migrate-contract', @@ -225,6 +230,26 @@ export const SERVICE = { path: 'v1.ReDecodeTx', }, }, + DailyStatisticsService: { + key: 'DailyStatisticsService', + name: 'v1.DailyStatisticsService', + CreateSpecificDateJob: { + key: 'CreateSpecificDateJob', + path: 'v1.DailyStatisticsService.CreateSpecificDateJob', + }, + }, + AccountStatisticsService: { + key: 'AccountStatisticsService', + name: 'v1.AccountStatisticsService', + CreateSpecificDateJob: { + key: 'CreateSpecificDateJob', + path: 'v1.AccountStatisticsService.CreateSpecificDateJob', + }, + }, + DailyStatsJobsService: { + key: 'DailyStatsJobsService', + name: 'v1.DailyStatsJobsService', + }, Cw20ReindexingService: { key: 'Cw20ReindexingService', name: 'v1.Cw20ReindexingService', diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index 854947411..8be6126f8 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -8,3 +8,11 @@ export interface IProposalIdParam { export interface ITxIdsParam { txIds: number[]; } + +export interface IStatisticsParam { + date: string; +} + +export interface ICreateSpecificDateJob { + date: string; +} diff --git a/src/models/account_statistics.ts b/src/models/account_statistics.ts new file mode 100644 index 000000000..d097e61ee --- /dev/null +++ b/src/models/account_statistics.ts @@ -0,0 +1,56 @@ +import BaseModel from './base'; + +export class AccountStatistics extends BaseModel { + address!: string; + + amount_sent!: string; + + amount_received!: string; + + tx_sent!: number; + + gas_used!: string; + + date!: Date; + + static get tableName() { + return 'account_statistics'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'address', + 'amount_sent', + 'amount_received', + 'tx_sent', + 'gas_used', + 'date', + ], + properties: { + address: { type: 'string' }, + amount_sent: { type: 'string' }, + amount_received: { type: 'string' }, + tx_sent: { type: 'number' }, + gas_used: { type: 'string' }, + date: { type: 'string', format: 'date-time' }, + }, + }; + } + + static get relationMappings() { + return {}; + } + + static newAccountStat(address: string, date: string) { + return AccountStatistics.fromJson({ + address, + amount_sent: '0', + amount_received: '0', + tx_sent: 0, + gas_used: '0', + date, + }); + } +} diff --git a/src/models/daily_statistics.ts b/src/models/daily_statistics.ts new file mode 100644 index 000000000..e9991a281 --- /dev/null +++ b/src/models/daily_statistics.ts @@ -0,0 +1,37 @@ +import BaseModel from './base'; + +export class DailyStatistics extends BaseModel { + daily_txs!: number; + + daily_active_addresses!: number; + + unique_addresses!: number; + + date!: Date; + + static get tableName() { + return 'daily_statistics'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'daily_txs', + 'daily_active_addresses', + 'unique_addresses', + 'date', + ], + properties: { + daily_txs: { type: 'number' }, + daily_active_addresses: { type: 'number' }, + unique_addresses: { type: 'number' }, + date: { type: 'string', format: 'date-time' }, + }, + }; + } + + static get relationMappings() { + return {}; + } +} diff --git a/src/models/event.ts b/src/models/event.ts index e5cc1a9d1..3f983faea 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -89,6 +89,10 @@ export class Event extends BaseModel { REVOKE_FEEGRANT: 'revoke_feegrant', USE_FEEGRANT: 'use_feegrant', SET_FEEGRANT: 'set_feegrant', + COIN_SPENT: 'coin_spent', + COIN_RECEIVED: 'coin_received', + TX: 'tx', + TRANSFER: 'transfer', MIGRATE: 'migrate', }; } diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 49200ac98..2fb47b872 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -2,6 +2,7 @@ import { Model } from 'objection'; import BaseModel from './base'; // eslint-disable-next-line import/no-cycle import { Event } from './event'; +import { Transaction } from './transaction'; export class EventAttribute extends BaseModel { event_id!: string; @@ -52,6 +53,14 @@ export class EventAttribute extends BaseModel { to: 'event.id', }, }, + transaction: { + relation: Model.BelongsToOneRelation, + modelClass: Transaction, + join: { + from: 'event_attribute.tx_id', + to: 'transaction.id', + }, + }, }; } @@ -61,13 +70,10 @@ export class EventAttribute extends BaseModel { REDELEGATION_RESPONSES: 'redelegation_responses', UNBONDING_RESPONSES: 'unbonding_responses', ACTION: 'action', - TRANSFER: 'transfer', SENDER: 'sender', RECEIVER: 'receiver', SPENDER: 'spender', RECIPIENT: 'recipient', - COIN_RECEIVED: 'coin_received', - COIN_SPENT: 'coin_spent', WITHDRAW_REWARDS: 'withdraw_rewards', AMOUNT: 'amount', VALIDATOR: 'validator', @@ -90,5 +96,18 @@ export class EventAttribute extends BaseModel { GRANTER: 'granter', GRANTEE: 'grantee', FROM: 'from', + FEE: 'fee', + FEE_PAYER: 'fee_payer', + }; + + static ATTRIBUTE_COMPOSITE_KEY = { + COIN_SPENT_SPENDER: 'coin_spent.spender', + COIN_RECEIVED_RECEIVER: 'coin_received.receiver', + COIN_SPENT_AMOUNT: 'coin_spent.amount', + COIN_RECEIVED_AMOUNT: 'coin_received.amount', + USE_FEEGRANT_GRANTER: 'use_feegrant.granter', + USE_FEEGRANT_GRANTEE: 'use_feegrant.grantee', + TX_FEE: 'tx.fee', + TX_FEE_PAYER: 'tx.fee_payer', }; } diff --git a/src/models/index.ts b/src/models/index.ts index 3ab4c3c6e..9ee83f061 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -22,4 +22,6 @@ export * from './delegator'; export * from './code_id_verification'; export * from './feegrant'; export * from './feegrant_history'; +export * from './daily_statistics'; +export * from './account_statistics'; export * from './cw20_total_holder_stats'; diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index 0503fd3a3..9ac09560a 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -21,7 +21,17 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; path: '/api', autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services mappingPolicy: 'restrict', // allow action called with exact method - whitelist: ['v2.dashboard-statistics.*', 'v2.graphql.*'], + whitelist: [ + 'v2.graphql.*', + 'v2.statistics.getDashboardStatisticsByChainId', + 'v2.statistics.getTopAccountsByChainId', + ], + }, + { + path: '/admin', + autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services + mappingPolicy: 'restrict', // allow action called with exact method + whitelist: ['v2.statistics.syncPrevDateStatsByChainId'], }, { path: '/admin', diff --git a/src/services/api-gateways/dashboard_statistics.service.ts b/src/services/api-gateways/dashboard_statistics.service.ts deleted file mode 100644 index 4c378b14e..000000000 --- a/src/services/api-gateways/dashboard_statistics.service.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - Action, - Get, - Service, -} from '@ourparentcenter/moleculer-decorators-extended'; -import { Context, ServiceBroker } from 'moleculer'; -import { REDIS_KEY } from '../../common'; -import BaseService from '../../base/base.service'; -import networks from '../../../network.json' assert { type: 'json' }; - -@Service({ - name: 'dashboard-statistics', - version: 2, -}) -export default class DashboardStatisticsService extends BaseService { - public constructor(public broker: ServiceBroker) { - super(broker); - } - - @Get('/', { - name: 'getDashboardStatisticsByChainId', - params: { - chainid: { - type: 'string', - optional: false, - enum: networks.map((network) => network.chainId), - }, - }, - }) - async getDashboardStatisticsByChainId( - ctx: Context<{ chainid: string }, Record> - ) { - const selectedChain = networks.find( - (network) => network.chainId === ctx.params.chainid - ); - - return this.broker.call( - `v2.dashboard-statistics.getDashboardStatistics@${selectedChain?.moleculerNamespace}` - ); - } - - @Action({ - name: 'getDashboardStatistics', - params: {}, - }) - async getDashboardStatistics() { - const result = await this.broker.cacher?.get( - REDIS_KEY.DASHBOARD_STATISTICS - ); - return result; - } -} diff --git a/src/services/api-gateways/statistics.service.ts b/src/services/api-gateways/statistics.service.ts new file mode 100644 index 000000000..aae362274 --- /dev/null +++ b/src/services/api-gateways/statistics.service.ts @@ -0,0 +1,99 @@ +import { + Get, + Post, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import { Context, ServiceBroker } from 'moleculer'; +import BaseService from '../../base/base.service'; +import networks from '../../../network.json' assert { type: 'json' }; + +@Service({ + name: 'statistics', + version: 2, +}) +export default class StatisticsService extends BaseService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Get('/dashboard', { + name: 'getDashboardStatisticsByChainId', + params: { + chainid: { + type: 'string', + optional: false, + enum: networks.map((network) => network.chainId), + }, + }, + }) + async getDashboardStatisticsByChainId( + ctx: Context<{ chainid: string }, Record> + ) { + const selectedChain = networks.find( + (network) => network.chainId === ctx.params.chainid + ); + + return this.broker.call( + `v2.api-statistics.getDashboardStatistics@${selectedChain?.moleculerNamespace}` + ); + } + + @Get('/top-accounts', { + name: 'getTopAccountsByChainId', + params: { + chainid: { + type: 'string', + optional: false, + enum: networks.map((network) => network.chainId), + }, + }, + }) + async getTopAccountsByChainId( + ctx: Context<{ chainid: string }, Record> + ) { + const selectedChain = networks.find( + (network) => network.chainId === ctx.params.chainid + ); + + return this.broker.call( + `v2.api-statistics.getTopAccounts@${selectedChain?.moleculerNamespace}` + ); + } + + @Post('/sync-prev-date-stats', { + name: 'syncPrevDateStatsByChainId', + params: { + chainid: { + type: 'string', + optional: false, + enum: networks.map((network) => network.chainId), + }, + startDate: { + type: 'string', + optional: false, + }, + endDate: { + type: 'string', + optional: true, + }, + }, + }) + async syncPrevDateStatsByChainId( + ctx: Context< + { chainid: string; startDate: string; endDate: string }, + Record + > + ) { + const selectedChain = networks.find( + (network) => network.chainId === ctx.params.chainid + ); + + return this.broker.call( + `v2.api-statistics.syncPrevDateStats@${selectedChain?.moleculerNamespace}`, + { + startDate: ctx.params.startDate, + endDate: ctx.params.endDate ?? null, + } + ); + } +} diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts new file mode 100644 index 000000000..dd97d64bf --- /dev/null +++ b/src/services/statistics/account_statistics.service.ts @@ -0,0 +1,420 @@ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-case-declarations */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { + Action, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import { Context, ServiceBroker } from 'moleculer'; +import { parseCoins } from '@cosmjs/proto-signing'; +import BigNumber from 'bignumber.js'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import _ from 'lodash'; +import { AccountStatistics, EventAttribute, Transaction } from '../../models'; +import { + BULL_JOB_NAME, + IStatisticsParam, + ICreateSpecificDateJob, + REDIS_KEY, + SERVICE, +} from '../../common'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import knex from '../../common/utils/db_connection'; + +dayjs.extend(utc); + +@Service({ + name: SERVICE.V1.AccountStatisticsService.key, + version: 1, +}) +export default class AccountStatisticsService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Action({ + name: SERVICE.V1.AccountStatisticsService.CreateSpecificDateJob.key, + params: { + date: 'string', + }, + }) + public async actionCreateSpecificDateJob( + ctx: Context + ) { + await this.createJob( + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + { + date: ctx.params.date, + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + attempts: config.jobRetryAttempt, + backoff: config.jobRetryBackoff, + } + ); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + jobName: BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async handleJob(_payload: IStatisticsParam): Promise { + const { date } = _payload; + const accountStats: any = {}; + + const endTime = dayjs.utc(date).startOf('day').toDate(); + const startTime = dayjs.utc(endTime).subtract(1, 'day').toDate(); + + const startTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', startTime) + .limit(1) + .orderBy('id'); + const endTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '<', endTime) + .limit(1) + .orderBy('id', 'desc'); + + this.logger.info( + `Get account statistic events for day ${new Date(startTime)}` + ); + + await Promise.all([ + this.calculateSpendReceive( + startTx[0].id, + endTx[0].id, + accountStats, + startTime.toISOString() + ), + this.calculateGasUsedTxSent( + startTx[0].id, + endTx[0].id, + accountStats, + startTime.toISOString() + ), + ]); + + const dailyAccountStats = Object.keys(accountStats).map( + (acc) => accountStats[acc] + ); + + this.logger.info(`Insert new account statistics for date ${startTime}`); + if (dailyAccountStats.length > 0) { + await AccountStatistics.query().insert(dailyAccountStats); + } + + await this.createJob( + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + attempts: config.jobRetryAttempt, + backoff: config.jobRetryBackoff, + } + ); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + jobName: BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async handleTopAccounts(_payload: object): Promise { + const now = dayjs.utc().startOf('day').toDate(); + + const { dayRange } = config.accountStatistics; + const [threeDayStat, fifteenDayStat, thirtyDayStat] = await Promise.all([ + this.getStatsFromSpecificDaysAgo(dayRange[0], now), + this.getStatsFromSpecificDaysAgo(dayRange[1], now), + this.getStatsFromSpecificDaysAgo(dayRange[2], now), + ]); + + const topAccounts = { + three_days: this.calculateTop(threeDayStat), + fifteen_days: this.calculateTop(fifteenDayStat), + thirty_days: this.calculateTop(thirtyDayStat), + }; + + this.logger.info(`Update top accounts for day ${new Date(now)}`); + await this.broker.cacher?.set(REDIS_KEY.TOP_ACCOUNTS, topAccounts); + } + + private async calculateSpendReceive( + startTxId: number, + endTxId: number, + accountStats: any, + date: string + ) { + const dailyEvents: any[] = await EventAttribute.query() + .select(knex.raw('jsonb_agg(jsonb_build_object(composite_key, value))')) + .where('tx_id', '>=', startTxId) + .andWhere('tx_id', '<', endTxId) + .andWhere((builder) => + builder + // Get the address that actually spent or received token + .whereIn('composite_key', [ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER, + ]) + ) + .groupBy('event_id'); + + if (dailyEvents.length > 0) { + dailyEvents + .map((event) => Object.assign({}, ...event.jsonb_agg)) + .map( + (event) => + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER] ?? + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER] + ) + .forEach((address) => { + if (!accountStats[address]) { + accountStats[address] = AccountStatistics.newAccountStat( + address, + date + ); + } + }); + + dailyEvents + .map((event) => Object.assign({}, ...event.jsonb_agg)) + .forEach((event) => { + if ( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER] + ) { + const addrSpent = + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER]; + const amountSpent = parseCoins( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT] + )[0]; + + if (amountSpent.denom === config.networkDenom) { + accountStats[addrSpent].amount_sent = ( + BigInt(accountStats[addrSpent].amount_sent) + + BigInt(amountSpent.amount) + ).toString(); + } + } else if ( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER] + ) { + const addrReceived = + event[ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER + ]; + const amountReceived = parseCoins( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT] + )[0]; + + if (amountReceived.denom === config.networkDenom) { + accountStats[addrReceived].amount_received = ( + BigInt(accountStats[addrReceived].amount_received) + + BigInt(amountReceived.amount) + ).toString(); + } + } + }); + } + } + + private async calculateGasUsedTxSent( + startTxId: number, + endTxId: number, + accountStats: any, + date: string + ) { + const feeEvents: any[] = await EventAttribute.query() + .joinRelated('transaction') + .select( + knex.raw('jsonb_agg(jsonb_build_object(composite_key, value))'), + 'gas_used' + ) + .where('tx_id', '>=', startTxId) + .andWhere('tx_id', '<', endTxId) + .andWhere((builder) => + builder + // If fee_grant is involved, then needs to track to the granters and grantees + .whereIn('composite_key', [ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE, + ]) + ) + .groupBy('tx_id', 'gas_used'); + + if (feeEvents.length > 0) { + feeEvents + .map((event) => Object.assign({}, ...event.jsonb_agg)) + .map( + (event) => + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER] ?? + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE] + ) + .forEach((address) => { + if (!accountStats[address]) { + accountStats[address] = AccountStatistics.newAccountStat( + address, + date + ); + } + }); + + feeEvents + .map((event) => + Object.assign({ gas_used: event.gas_used }, ...event.jsonb_agg) + ) + .forEach((event) => { + let addr = event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER]; + + if ( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE] + ) { + addr = + event[ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE + ]; + } + + accountStats[addr].tx_sent += 1; + accountStats[addr].gas_used = ( + BigInt(accountStats[addr].gas_used) + BigInt(event.gas_used) + ).toString(); + }); + } + } + + private async getStatsFromSpecificDaysAgo( + daysAgo: number, + endTime: Date + ): Promise { + const startTime = dayjs + .utc() + .subtract(daysAgo, 'day') + .startOf('day') + .toDate(); + + const result = await AccountStatistics.query() + .select('address') + .sum('amount_sent as amount_sent') + .sum('amount_received as amount_received') + .sum('tx_sent as tx_sent') + .sum('gas_used as gas_used') + .where('date', '>=', startTime) + .andWhere('date', '<', endTime) + .groupBy('address'); + + return result; + } + + private calculateTop(dayStat: AccountStatistics[]) { + let topAmountSent: any[] = []; + let topAmountReceived: any[] = []; + let topTxSent: any[] = []; + let topGasUsed: any[] = []; + + const dayStatAmountSent = dayStat + .reduce( + (init: bigint, accStat: AccountStatistics) => + init + BigInt(accStat.amount_sent), + BigInt(0) + ) + .toString(); + const dayStatAmountReceived = dayStat + .reduce( + (init: bigint, accStat: AccountStatistics) => + init + BigInt(accStat.amount_received), + BigInt(0) + ) + .toString(); + const dayStatTxSent = dayStat + .reduce( + (init: bigint, accStat: AccountStatistics) => + init + BigInt(accStat.tx_sent), + BigInt(0) + ) + .toString(); + const dayStatGasUsed = dayStat + .reduce( + (init: bigint, accStat: AccountStatistics) => + init + BigInt(accStat.gas_used), + BigInt(0) + ) + .toString(); + + dayStat.forEach((stat) => { + topAmountSent.push({ + address: stat.address, + amount: stat.amount_sent, + percentage: Number( + BigNumber(stat.amount_sent) + .multipliedBy(100) + .dividedBy(BigNumber(dayStatAmountSent)) + ), + }); + topAmountReceived.push({ + address: stat.address, + amount: stat.amount_received, + percentage: Number( + BigNumber(stat.amount_received) + .multipliedBy(100) + .dividedBy(BigNumber(dayStatAmountReceived)) + ), + }); + topTxSent.push({ + address: stat.address, + amount: stat.tx_sent, + percentage: Number( + BigNumber(stat.tx_sent) + .multipliedBy(100) + .dividedBy(BigNumber(dayStatTxSent)) + ), + }); + topGasUsed.push({ + address: stat.address, + amount: stat.gas_used, + percentage: Number( + BigNumber(stat.gas_used) + .multipliedBy(100) + .dividedBy(BigNumber(dayStatGasUsed)) + ), + }); + }); + + topAmountSent = _.orderBy(topAmountSent, 'percentage', 'desc').slice( + 0, + config.accountStatistics.numberOfTopRecords + ); + topAmountReceived = _.orderBy( + topAmountReceived, + 'percentage', + 'desc' + ).slice(0, config.accountStatistics.numberOfTopRecords); + topTxSent = _.orderBy(topTxSent, 'percentage', 'desc').slice( + 0, + config.accountStatistics.numberOfTopRecords + ); + topGasUsed = _.orderBy(topGasUsed, 'percentage', 'desc').slice( + 0, + config.accountStatistics.numberOfTopRecords + ); + + return { + top_amount_sent: topAmountSent, + top_amount_received: topAmountReceived, + top_tx_sent: topTxSent, + top_gas_used: topGasUsed, + }; + } +} diff --git a/src/services/statistics/api_statistics.service.ts b/src/services/statistics/api_statistics.service.ts new file mode 100644 index 000000000..e58abfaea --- /dev/null +++ b/src/services/statistics/api_statistics.service.ts @@ -0,0 +1,91 @@ +/* eslint-disable no-await-in-loop */ +import { + Action, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import { Context, ServiceBroker } from 'moleculer'; +import { REDIS_KEY, SERVICE } from '../../common'; +import BaseService from '../../base/base.service'; +import { ErrorCode, ErrorMessage } from '../../common/types/errors'; + +dayjs.extend(utc); + +@Service({ + name: 'api-statistics', + version: 2, +}) +export default class ApiStatisticsService extends BaseService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Action({ + name: 'getDashboardStatistics', + params: {}, + }) + async getDashboardStatistics() { + const result = await this.broker.cacher?.get( + REDIS_KEY.DASHBOARD_STATISTICS + ); + return result; + } + + @Action({ + name: 'getTopAccounts', + params: {}, + }) + async getTopAccounts() { + const result = await this.broker.cacher?.get(REDIS_KEY.TOP_ACCOUNTS); + return result; + } + + @Action({ + name: 'syncPrevDateStats', + params: { + startDate: { + type: 'string', + }, + endDate: { + type: 'string', + optional: true, + }, + }, + }) + async syncPrevDateStats( + ctx: Context< + { startDate: string; endDate: string }, + Record + > + ) { + // Since each stats job query data of the prev date, + // so the start and end date needs to change to the following date + const startTime = dayjs.utc(ctx.params.startDate).add(1, 'day').toDate(); + const endTime = ctx.params.endDate + ? dayjs.utc(ctx.params.endDate).add(1, 'day').toDate() + : dayjs.utc(ctx.params.startDate).add(1, 'day').toDate(); + + for ( + let date = startTime; + date <= endTime; + date.setDate(date.getDate() + 1) + ) { + await Promise.all([ + this.broker.call( + SERVICE.V1.DailyStatisticsService.CreateSpecificDateJob.path, + { date: date.toString() } + ), + this.broker.call( + SERVICE.V1.AccountStatisticsService.CreateSpecificDateJob.path, + { date: date.toString() } + ), + ]); + } + + return { + code: ErrorCode.SUCCESSFUL, + message: ErrorMessage.SUCCESSFUL, + }; + } +} diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts new file mode 100644 index 000000000..775d93a5f --- /dev/null +++ b/src/services/statistics/daily_statistics.service.ts @@ -0,0 +1,127 @@ +import { + Action, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import { Context, ServiceBroker } from 'moleculer'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import { + Account, + DailyStatistics, + EventAttribute, + Transaction, +} from '../../models'; +import { + BULL_JOB_NAME, + ICreateSpecificDateJob, + IStatisticsParam, + SERVICE, +} from '../../common'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import Utils from '../../common/utils/utils'; + +dayjs.extend(utc); + +@Service({ + name: SERVICE.V1.DailyStatisticsService.key, + version: 1, +}) +export default class DailyStatisticsService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Action({ + name: SERVICE.V1.DailyStatisticsService.CreateSpecificDateJob.key, + params: { + date: 'string', + }, + }) + public async actionCreateSpecificDateJob( + ctx: Context + ) { + await this.createJob( + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + { + date: ctx.params.date, + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + attempts: config.jobRetryAttempt, + backoff: config.jobRetryBackoff, + } + ); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + jobName: BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async handleJob(_payload: IStatisticsParam): Promise { + const endTime = dayjs.utc(_payload.date).startOf('day').toDate(); + const startTime = dayjs.utc(endTime).subtract(1, 'day').toDate(); + this.logger.info( + `Get daily statistic events for day ${new Date(startTime)}` + ); + + const startTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', startTime) + .limit(1) + .orderBy('id'); + const endTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '<', endTime) + .limit(1) + .orderBy('id', 'desc'); + + const [dailyTxs, dailyAddresses, uniqueAddrs] = await Promise.all([ + Transaction.query() + .count('id') + .where('timestamp', '>=', startTime) + .andWhere('timestamp', '<', endTime) + .andWhere('code', 0), + EventAttribute.query() + .distinct('value') + .where('tx_id', '>=', startTx[0].id) + .andWhere('tx_id', '<=', endTx[0].id) + .andWhere('value', 'like', `${config.networkPrefixAddress}%`), + Account.query().count('id'), + ]); + // TODO: Need to re-define if it just count normal addresses only or also the contract addresses + const activeAddrs = Array.from( + new Set( + dailyAddresses + .filter((event) => + Utils.isValidAccountAddress( + event.value, + config.networkPrefixAddress, + 20 + ) + ) + .map((event) => event.value) + ) + ); + + const dailyStat = DailyStatistics.fromJson({ + daily_txs: dailyTxs[0].count, + daily_active_addresses: activeAddrs.length, + unique_addresses: Number(uniqueAddrs[0].count), + date: startTime.toISOString(), + }); + + this.logger.info(`Insert new daily statistic for date ${startTime}`); + await DailyStatistics.query() + .insert(dailyStat) + .catch((error) => { + this.logger.error('Error insert new daily statistic record'); + this.logger.error(error); + }); + } +} diff --git a/src/services/statistics/daily_stats_jobs.service.ts b/src/services/statistics/daily_stats_jobs.service.ts new file mode 100644 index 000000000..668b53fc5 --- /dev/null +++ b/src/services/statistics/daily_stats_jobs.service.ts @@ -0,0 +1,59 @@ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; + +@Service({ + name: SERVICE.V1.DailyStatsJobsService.key, + version: 1, +}) +export default class DailyStatsJobsService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.HANDLE_DAILY_STATS_JOBS, + jobName: BULL_JOB_NAME.HANDLE_DAILY_STATS_JOBS, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async handleJob(_payload: object): Promise { + const date = new Date().toString(); + + await Promise.all([ + this.broker.call( + SERVICE.V1.DailyStatisticsService.CreateSpecificDateJob.path, + { date } + ), + this.broker.call( + SERVICE.V1.AccountStatisticsService.CreateSpecificDateJob.path, + { date } + ), + ]); + } + + public async _start() { + await this.broker.waitForServices([ + SERVICE.V1.DailyStatisticsService.name, + SERVICE.V1.AccountStatisticsService.name, + ]); + + this.createJob( + BULL_JOB_NAME.HANDLE_DAILY_STATS_JOBS, + BULL_JOB_NAME.HANDLE_DAILY_STATS_JOBS, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + pattern: config.dailyStatsJobs.jobPattern, + }, + } + ); + + return super._start(); + } +} diff --git a/src/services/statistics/dashboard_statistics.service.ts b/src/services/statistics/dashboard_statistics.service.ts index 7727909be..b998d3d4e 100644 --- a/src/services/statistics/dashboard_statistics.service.ts +++ b/src/services/statistics/dashboard_statistics.service.ts @@ -32,76 +32,70 @@ export default class DashboardStatisticsService extends BullableService { // prefix: `horoscope-v2-${config.chainId}`, }) public async handleJob(_payload: object): Promise { - try { - this.logger.info('Update AuraScan dashboard statistics'); - this._lcdClient = await getLcdClient(); + this.logger.info('Update AuraScan dashboard statistics'); + this._lcdClient = await getLcdClient(); - const [totalBlocks, totalTxs, totalValidators] = await Promise.all([ - BlockCheckpoint.query().findOne('job_name', BULL_JOB_NAME.CRAWL_BLOCK), - Transaction.query().count('id'), - Validator.query(), - ]); + const [totalBlocks, totalTxs, totalValidators] = await Promise.all([ + BlockCheckpoint.query().findOne('job_name', BULL_JOB_NAME.CRAWL_BLOCK), + Transaction.query().count('id'), + Validator.query(), + ]); - const [communityPool, inflation, distribution, supply] = - await Promise.all([ - this._lcdClient.auranw.cosmos.distribution.v1beta1.communityPool(), - this._lcdClient.auranw.cosmos.mint.v1beta1.inflation(), - this._lcdClient.auranw.cosmos.distribution.v1beta1.params(), - this._lcdClient.auranw.cosmos.bank.v1beta1.supplyOf({ - denom: config.networkDenom, - }), - ]); - let bondedTokens = BigInt(0); - totalValidators - .filter( - (val) => - val.status === Validator.STATUS.BONDED && val.jailed === false - ) - .forEach((val) => { - bondedTokens += BigInt(val.tokens); - }); - const totalAura = supply.amount.amount; + const [communityPool, inflation, distribution, supply] = await Promise.all([ + this._lcdClient.auranw.cosmos.distribution.v1beta1.communityPool(), + this._lcdClient.auranw.cosmos.mint.v1beta1.inflation(), + this._lcdClient.auranw.cosmos.distribution.v1beta1.params(), + this._lcdClient.auranw.cosmos.bank.v1beta1.supplyOf({ + denom: config.networkDenom, + }), + ]); + let bondedTokens = BigInt(0); + totalValidators + .filter( + (val) => val.status === Validator.STATUS.BONDED && val.jailed === false + ) + .forEach((val) => { + bondedTokens += BigInt(val.tokens); + }); + const totalAura = supply.amount.amount; - const dashboardStatistics = { - total_blocks: totalBlocks?.height, - community_pool: communityPool.pool.find( - (pool: DecCoinSDKType) => pool.denom === config.networkDenom - ).amount, - total_transactions: Number(totalTxs[0].count), - total_validators: totalValidators.length, - total_active_validators: totalValidators.filter( - (val) => val.status === Validator.STATUS.BONDED - ).length, - total_inactive_validators: totalValidators.filter( - (val) => val.status === Validator.STATUS.UNBONDED - ).length, - bonded_tokens: bondedTokens.toString(), - inflation: inflation.inflation, - total_aura: totalAura, - staking_apr: Number( - BigNumber(inflation.inflation) - .multipliedBy( - BigNumber(1 - Number(distribution.params.community_tax)) - ) - .multipliedBy(BigNumber(totalAura)) - .dividedBy(BigNumber(bondedTokens.toString())) - .multipliedBy(100) - ), - }; + const dashboardStatistics = { + total_blocks: totalBlocks?.height, + community_pool: communityPool.pool.find( + (pool: DecCoinSDKType) => pool.denom === config.networkDenom + ).amount, + total_transactions: Number(totalTxs[0].count), + total_validators: totalValidators.length, + total_active_validators: totalValidators.filter( + (val) => val.status === Validator.STATUS.BONDED + ).length, + total_inactive_validators: totalValidators.filter( + (val) => val.status === Validator.STATUS.UNBONDED + ).length, + bonded_tokens: bondedTokens.toString(), + inflation: inflation.inflation, + total_aura: totalAura, + staking_apr: Number( + BigNumber(inflation.inflation) + .multipliedBy( + BigNumber(1 - Number(distribution.params.community_tax)) + ) + .multipliedBy(BigNumber(totalAura)) + .dividedBy(BigNumber(bondedTokens.toString())) + .multipliedBy(100) + ), + }; - await this.broker.cacher?.set( - REDIS_KEY.DASHBOARD_STATISTICS, - dashboardStatistics - ); - } catch (error) { - this.logger.error(error); - } + await this.broker.cacher?.set( + REDIS_KEY.DASHBOARD_STATISTICS, + dashboardStatistics + ); } public async _start() { this.createJob( BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, - 'crawl', + BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, {}, { removeOnComplete: true, diff --git a/yarn.lock b/yarn.lock index 5688a451e..4f62687f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -16,9 +21,9 @@ integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg== "@aura-nw/aurajs@^0.1.3-alpha": - version "0.1.3-alpha" - resolved "https://npm.pkg.github.com/download/@aura-nw/aurajs/0.1.3-alpha/89ae79c11848c0530c0cd46e94f7f1149786a5b1#89ae79c11848c0530c0cd46e94f7f1149786a5b1" - integrity sha512-BMsaxIVNK58ugn3kqnxoomnTAPh5D58b61dWDCcNV0SoRWBAbcbb/1Y82fDXznOR1S1nJc+H3hXw1B2HA1v/Fw== + version "0.1.3-beta-smart-account" + resolved "https://npm.pkg.github.com/download/@aura-nw/aurajs/0.1.3-beta-smart-account/6e88fd3bcd73d81fc73e1c9f7ea4fdf18be9b5e2#6e88fd3bcd73d81fc73e1c9f7ea4fdf18be9b5e2" + integrity sha512-C0fLBowtAG671SQLH3Z0FDcoQo/7yZRbJ2m9qJfB6EGUGgSWr3bIlTFC2dxEgiUkLamiKymgxMgNJZ4/gMlOIw== dependencies: "@babel/runtime" "^7.19.4" "@cosmjs/amino" "0.29.4" @@ -36,10 +41,10 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4", "@babel/compat-data@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" - integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4", "@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@7.18.10": version "7.18.10" @@ -84,25 +89,25 @@ semver "^6.3.0" "@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" - integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" + integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helpers" "^7.22.5" - "@babel/parser" "^7.22.5" + "@babel/generator" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" + "@babel/traverse" "^7.22.8" "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" - semver "^6.3.0" + semver "^6.3.1" "@babel/generator@7.18.12": version "7.18.12" @@ -123,10 +128,10 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/generator@^7.18.10", "@babel/generator@^7.21.4", "@babel/generator@^7.22.5", "@babel/generator@^7.7.2": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" - integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== +"@babel/generator@^7.18.10", "@babel/generator@^7.21.4", "@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.7.2": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" + integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== dependencies: "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" @@ -147,40 +152,40 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4", "@babel/helper-compilation-targets@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz#fc7319fc54c5e2fa14b2909cf3c5fd3046813e02" - integrity sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" + integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== dependencies: - "@babel/compat-data" "^7.22.5" + "@babel/compat-data" "^7.22.9" "@babel/helper-validator-option" "^7.22.5" - browserslist "^4.21.3" + browserslist "^4.21.9" lru-cache "^5.1.1" - semver "^6.3.0" + semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" - integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz#c36ea240bb3348f942f08b0fbe28d6d979fab236" + integrity sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-member-expression-to-functions" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - semver "^6.3.0" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz#bb2bf0debfe39b831986a4efbf4066586819c6e4" - integrity sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" + integrity sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" regexpu-core "^5.3.1" - semver "^6.3.0" + semver "^6.3.1" "@babel/helper-define-polyfill-provider@^0.3.2", "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -228,19 +233,16 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-module-transforms@^7.18.9", "@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" - integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== +"@babel/helper-module-transforms@^7.18.9", "@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-module-imports" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -255,26 +257,22 @@ integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== "@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz#14a38141a7bf2165ad38da61d61cf27b43015da2" - integrity sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" + integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-wrap-function" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.9" -"@babel/helper-replace-supers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" - integrity sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg== +"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" + integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-member-expression-to-functions" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" "@babel/helper-simple-access@^7.22.5": version "7.22.5" @@ -290,10 +288,10 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" - integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== +"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: "@babel/types" "^7.22.5" @@ -312,23 +310,22 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== -"@babel/helper-wrap-function@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz#44d205af19ed8d872b4eefb0d2fa65f45eb34f06" - integrity sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw== +"@babel/helper-wrap-function@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz#189937248c45b0182c1dcf32f3444ca153944cb9" + integrity sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q== dependencies: "@babel/helper-function-name" "^7.22.5" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/helpers@^7.18.9", "@babel/helpers@^7.21.0", "@babel/helpers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.5.tgz#74bb4373eb390d1ceed74a15ef97767e63120820" - integrity sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q== +"@babel/helpers@^7.18.9", "@babel/helpers@^7.21.0", "@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== dependencies: "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" + "@babel/traverse" "^7.22.6" "@babel/types" "^7.22.5" "@babel/highlight@^7.22.5": @@ -345,10 +342,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" - integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.22.5" @@ -685,18 +682,18 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-classes@^7.18.9", "@babel/plugin-transform-classes@^7.21.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz#635d4e98da741fad814984639f4c0149eb0135e1" - integrity sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ== + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" + integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-replace-supers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.18.9", "@babel/plugin-transform-computed-properties@^7.20.7": @@ -826,9 +823,9 @@ "@babel/helper-replace-supers" "^7.22.5" "@babel/plugin-transform-optional-chaining@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.5.tgz#1003762b9c14295501beb41be72426736bedd1e0" - integrity sha512-AconbMKOMkyG+xCng2JogMCDcqW8wedQAqpVIL4cOSescZ7+iW8utC6YDZLMCSUIReEA733gzRSaOSXMAt/4WQ== + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz#4bacfe37001fe1901117672875e931d439811564" + integrity sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -924,12 +921,12 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-typescript@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz#5c0f7adfc1b5f38c4dbc8f79b1f0f8074134bd7d" - integrity sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz#91e08ad1eb1028ecc62662a842e93ecfbf3c7234" + integrity sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.9" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.22.5" @@ -1138,9 +1135,9 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.11.2", "@babel/runtime@^7.18.9", "@babel/runtime@^7.19.0", "@babel/runtime@^7.19.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.8.4": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" - integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" + integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== dependencies: regenerator-runtime "^0.13.11" @@ -1185,18 +1182,18 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.21.4", "@babel/traverse@^7.22.5", "@babel/traverse@^7.7.2": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" - integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== +"@babel/traverse@^7.18.10", "@babel/traverse@^7.21.4", "@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== dependencies: "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" + "@babel/generator" "^7.22.7" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - "@babel/parser" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" @@ -1233,38 +1230,38 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@bull-board/api@5.2.1", "@bull-board/api@^5.1.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.2.1.tgz#cda18d20c0b2a1ddc79d96f2313bb9043233d961" - integrity sha512-+WRK5pkjly8bVgedBY4f0zzuywFb9bbd2ABv8unvV5F9dWKc1+8W76xAycfvMQNshbWCjAJISWsc5Y/13xcT3Q== +"@bull-board/api@5.6.1", "@bull-board/api@^5.1.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.6.1.tgz#2dd149620110748cc5b64e8908a1f77737c7fceb" + integrity sha512-iq+VpvCt7v2kYCtapDDmgHFyTyHiCbJkSeYbgIDtMIspXfsKxz0ZWKsqaY/VOCRE26jAX2asK17S2XSrANEU0A== dependencies: redis-info "^3.0.8" "@bull-board/express@^5.1.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@bull-board/express/-/express-5.2.1.tgz#90ffcc5bb6c3ef4db340c89f4d1b52c4c0d8e479" - integrity sha512-fW6FHka4EvQAqydGlDky6FYcf98HT2vjXWn/qhVbt+X7JoFB0WkMbCthoQy96Y7ac1+Cd8PbvTHYUktSArfSPg== + version "5.6.1" + resolved "https://registry.yarnpkg.com/@bull-board/express/-/express-5.6.1.tgz#616f81c16c159f5d162d0a6c27a423d63a6cdeb7" + integrity sha512-/K5lRN40c0mcu6ye+oMWrBb+luJaz4mbGOqrOoYZif5FJ0jOoyYy+WFc9LlMdSUarLFVX0dbOYV5RmYrUxZJkg== dependencies: - "@bull-board/api" "5.2.1" - "@bull-board/ui" "5.2.1" + "@bull-board/api" "5.6.1" + "@bull-board/ui" "5.6.1" ejs "3.1.7" express "4.17.3" -"@bull-board/ui@5.2.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@bull-board/ui/-/ui-5.2.1.tgz#40bc4e4553d0e9bedb95c1292c8cf4e1fc56e207" - integrity sha512-G5gazsvCXm8J2cVS4QEKctGLoXhh9GOFs8fooXEWGd5AAnJZrjGcvI3ICAa4lx8taxlLG0tyF9y3R39iwzccuw== +"@bull-board/ui@5.6.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@bull-board/ui/-/ui-5.6.1.tgz#da062505d5e3d7e0c485625abaffdf23c7f841bb" + integrity sha512-cy4fEXxOBHR5+3Ez3bfax+Cd2TUTPyuSYRyQWL0WIwd3bivn/3zov9IThcnHu1YmJ8rjmUNoMHz7JIDXwUg8zg== dependencies: - "@bull-board/api" "5.2.1" + "@bull-board/api" "5.6.1" "@commitlint/cli@^17.4.1": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.6.5.tgz#3a8abd6499f9d4aeafe3bf9201338ccb868a14b9" - integrity sha512-3PQrWr/uo6lzF5k7n5QuosCYnzaxP9qGBp3jhWP0Vmsa7XA6wrl9ccPqfQyXpSbQE3zBROVO3TDqgPKe4tfmLQ== + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.6.7.tgz#1d352a6cfdb6b6a6ae49a959e6c13dcef1b63782" + integrity sha512-nzZmfO5KIOupYppn1MsnYX/80I+KDlxiwkks3CJT0XT+t34UgqGi3eSyEuzgcIjPlORk5/GMaAEiys78iLfGMg== dependencies: "@commitlint/format" "^17.4.4" - "@commitlint/lint" "^17.6.5" - "@commitlint/load" "^17.5.0" + "@commitlint/lint" "^17.6.7" + "@commitlint/load" "^17.6.7" "@commitlint/read" "^17.5.1" "@commitlint/types" "^17.4.4" execa "^5.0.0" @@ -1274,24 +1271,24 @@ yargs "^17.0.0" "@commitlint/config-conventional@^17.3.0": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-17.6.5.tgz#a8ec286e634a071329fe45dc4955032c2176aeb5" - integrity sha512-Xl9H9KLl86NZm5CYNTNF9dcz1xelE/EbvhWIWcYxG/rn3UWYWdWmmnX2q6ZduNdLFSGbOxzUpIx61j5zxbeXxg== + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-17.6.7.tgz#8469d977def36148615e9516b1a521e38ca27ddd" + integrity sha512-4oTpEUC0HRM54QRHBPMOJW1pETp7usxXn9RuNYNWHcmu8wi1mpws95hvS20u2n6HtIkTn0jfn7vHioCm4AGUTw== dependencies: conventional-changelog-conventionalcommits "^5.0.0" -"@commitlint/config-validator@^17.4.4": - version "17.4.4" - resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.4.4.tgz#d0742705719559a101d2ee49c0c514044af6d64d" - integrity sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg== +"@commitlint/config-validator@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.6.7.tgz#c664d42a1ecf5040a3bb0843845150f55734df41" + integrity sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ== dependencies: "@commitlint/types" "^17.4.4" ajv "^8.11.0" -"@commitlint/ensure@^17.4.4": - version "17.4.4" - resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-17.4.4.tgz#a36e7719bdb9c2b86c8b8c2e852b463a7bfda5fa" - integrity sha512-AHsFCNh8hbhJiuZ2qHv/m59W/GRE9UeOXbkOqxYMNNg9pJ7qELnFcwj5oYpa6vzTSHtPGKf3C2yUFNy1GGHq6g== +"@commitlint/ensure@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-17.6.7.tgz#77a77a0c05e6a1c34589f59e82e6cb937101fc4b" + integrity sha512-mfDJOd1/O/eIb/h4qwXzUxkmskXDL9vNPnZ4AKYKiZALz4vHzwMxBSYtyL2mUIDeU9DRSpEUins8SeKtFkYHSw== dependencies: "@commitlint/types" "^17.4.4" lodash.camelcase "^4.3.0" @@ -1313,32 +1310,32 @@ "@commitlint/types" "^17.4.4" chalk "^4.1.0" -"@commitlint/is-ignored@^17.6.5": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-17.6.5.tgz#cea24cd2031fe7d242590b91fab3352750887194" - integrity sha512-CQvAPt9gX7cuUbMrIaIMKczfWJqqr6m8IlJs0F2zYwyyMTQ87QMHIj5jJ5HhOaOkaj6dvTMVGx8Dd1I4xgUuoQ== +"@commitlint/is-ignored@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-17.6.7.tgz#711897f19180f1121ecf302a3c5496f9a920a59e" + integrity sha512-vqyNRqtbq72P2JadaoWiuoLtXIs9SaAWDqdtef6G2zsoXqKFc7vqj1f+thzVgosXG3X/5K9jNp+iYijmvOfc/g== dependencies: "@commitlint/types" "^17.4.4" - semver "7.5.0" + semver "7.5.2" -"@commitlint/lint@^17.6.5": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-17.6.5.tgz#dfa437f14430c9874d6b1a3ba8a2d44b79780c02" - integrity sha512-BSJMwkE4LWXrOsiP9KoHG+/heSDfvOL/Nd16+ojTS/DX8HZr8dNl8l3TfVr/d/9maWD8fSegRGtBtsyGuugFrw== +"@commitlint/lint@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-17.6.7.tgz#fb49c2722749e3ef83e2b41258fc32531068a13b" + integrity sha512-TW+AozfuOFMrHn+jdwtz0IWu8REKFp0eryOvoBp2r8IXNc4KihKB1spAiUB6SFyHD6hVVeolz12aHnJ3Mb+xVQ== dependencies: - "@commitlint/is-ignored" "^17.6.5" - "@commitlint/parse" "^17.6.5" - "@commitlint/rules" "^17.6.5" + "@commitlint/is-ignored" "^17.6.7" + "@commitlint/parse" "^17.6.7" + "@commitlint/rules" "^17.6.7" "@commitlint/types" "^17.4.4" -"@commitlint/load@^17.5.0": - version "17.5.0" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.5.0.tgz#be45dbbb50aaf5eb7e8e940e1e0d6171d1426bab" - integrity sha512-l+4W8Sx4CD5rYFsrhHH8HP01/8jEP7kKf33Xlx2Uk2out/UKoKPYMOIRcDH5ppT8UXLMV+x6Wm5osdRKKgaD1Q== +"@commitlint/load@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.6.7.tgz#c63b18ca8942a8fc095ec7a7ff7aa52f3854f6ba" + integrity sha512-QZ2rJTbX55BQdYrCm/p6+hh/pFBgC9nTJxfsrK6xRPe2thiQzHN0AQDBqBwAirn6gIkHrjIbCbtAE6kiDYLjrw== dependencies: - "@commitlint/config-validator" "^17.4.4" + "@commitlint/config-validator" "^17.6.7" "@commitlint/execute-rule" "^17.4.0" - "@commitlint/resolve-extends" "^17.4.4" + "@commitlint/resolve-extends" "^17.6.7" "@commitlint/types" "^17.4.4" "@types/node" "*" chalk "^4.1.0" @@ -1356,10 +1353,10 @@ resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-17.4.2.tgz#f4753a79701ad6db6db21f69076e34de6580e22c" integrity sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q== -"@commitlint/parse@^17.6.5": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-17.6.5.tgz#7b84b328a6a94ca08ab7c98c491d9d3dab68f09d" - integrity sha512-0zle3bcn1Hevw5Jqpz/FzEWNo2KIzUbc1XyGg6WrWEoa6GH3A1pbqNF6MvE6rjuy6OY23c8stWnb4ETRZyN+Yw== +"@commitlint/parse@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-17.6.7.tgz#b87c61213653d670f956faafe7783aef9ef13020" + integrity sha512-ibO03BgEns+JJpohpBZYD49mCdSNMg6fTv7vA5yqzEFWkBQk5NWhEBw2yG+Z1UClStIRkMkAYyI2HzoQG9tCQQ== dependencies: "@commitlint/types" "^17.4.4" conventional-changelog-angular "^5.0.11" @@ -1376,24 +1373,24 @@ git-raw-commits "^2.0.11" minimist "^1.2.6" -"@commitlint/resolve-extends@^17.4.4": - version "17.4.4" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.4.4.tgz#8f931467dea8c43b9fe38373e303f7c220de6fdc" - integrity sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A== +"@commitlint/resolve-extends@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.6.7.tgz#9c53a4601c96ab2dd20b90fb35c988639307735d" + integrity sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg== dependencies: - "@commitlint/config-validator" "^17.4.4" + "@commitlint/config-validator" "^17.6.7" "@commitlint/types" "^17.4.4" import-fresh "^3.0.0" lodash.mergewith "^4.6.2" resolve-from "^5.0.0" resolve-global "^1.0.0" -"@commitlint/rules@^17.6.5": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-17.6.5.tgz#fabcacdde923e26ac5ef90d4b3f8fc05526bbaa1" - integrity sha512-uTB3zSmnPyW2qQQH+Dbq2rekjlWRtyrjDo4aLFe63uteandgkI+cc0NhhbBAzcXShzVk0qqp8SlkQMu0mgHg/A== +"@commitlint/rules@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-17.6.7.tgz#2dbf52e82b5bcb1c74445637c6a9974571ab54b6" + integrity sha512-x/SDwDTN3w3Gr5xkhrIORu96rlKCc8ZLYEMXRqi9+MB33st2mKcGvKa5uJuigHlbl3xm75bAAubATrodVrjguQ== dependencies: - "@commitlint/ensure" "^17.4.4" + "@commitlint/ensure" "^17.6.7" "@commitlint/message" "^17.4.2" "@commitlint/to-lines" "^17.4.0" "@commitlint/types" "^17.4.4" @@ -1768,14 +1765,14 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== -"@eslint/eslintrc@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331" - integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ== +"@eslint/eslintrc@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" + integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.2" + espree "^9.6.0" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -1783,10 +1780,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.42.0": - version "8.42.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6" - integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw== +"@eslint/js@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" + integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== "@fastify/busboy@^1.0.0": version "1.2.1" @@ -1860,28 +1857,28 @@ resolved "https://registry.yarnpkg.com/@jest-decorated/shared/-/shared-0.1.7.tgz#fe2a25c9fc6e79ecfa79a75b4473c0adf4122877" integrity sha512-zmr/uMl7xqIe5jjSiaM3vitcDYUnVC3rGlG7Z2lKI4nvGmCiGv7g9I9U3a48G4X8PQEf1aZP0bLW4SiJMrhDGA== -"@jest/console@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" - integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== +"@jest/console@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.1.tgz#b48ba7b9c34b51483e6d590f46e5837f1ab5f639" + integrity sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-message-util "^29.6.1" + jest-util "^29.6.1" slash "^3.0.0" -"@jest/core@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" - integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== - dependencies: - "@jest/console" "^29.5.0" - "@jest/reporters" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" +"@jest/core@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.6.1.tgz#fac0d9ddf320490c93356ba201451825231e95f6" + integrity sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/reporters" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" @@ -1889,81 +1886,81 @@ exit "^0.1.2" graceful-fs "^4.2.9" jest-changed-files "^29.5.0" - jest-config "^29.5.0" - jest-haste-map "^29.5.0" - jest-message-util "^29.5.0" + jest-config "^29.6.1" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-resolve-dependencies "^29.5.0" - jest-runner "^29.5.0" - jest-runtime "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" - jest-watcher "^29.5.0" + jest-resolve "^29.6.1" + jest-resolve-dependencies "^29.6.1" + jest-runner "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + jest-watcher "^29.6.1" micromatch "^4.0.4" - pretty-format "^29.5.0" + pretty-format "^29.6.1" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" - integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== +"@jest/environment@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.6.1.tgz#ee358fff2f68168394b4a50f18c68278a21fe82f" + integrity sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A== dependencies: - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" - jest-mock "^29.5.0" + jest-mock "^29.6.1" -"@jest/expect-utils@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" - integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== +"@jest/expect-utils@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.1.tgz#ab83b27a15cdd203fe5f68230ea22767d5c3acc5" + integrity sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw== dependencies: jest-get-type "^29.4.3" -"@jest/expect@^29.0.1", "@jest/expect@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" - integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== +"@jest/expect@^29.0.1", "@jest/expect@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.1.tgz#fef18265188f6a97601f1ea0a2912d81a85b4657" + integrity sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg== dependencies: - expect "^29.5.0" - jest-snapshot "^29.5.0" + expect "^29.6.1" + jest-snapshot "^29.6.1" -"@jest/fake-timers@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" - integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== +"@jest/fake-timers@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.6.1.tgz#c773efddbc61e1d2efcccac008139f621de57c69" + integrity sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" - jest-util "^29.5.0" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" + jest-util "^29.6.1" -"@jest/globals@^29.3.1", "@jest/globals@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" - integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== +"@jest/globals@^29.3.1", "@jest/globals@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.6.1.tgz#c8a8923e05efd757308082cc22893d82b8aa138f" + integrity sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A== dependencies: - "@jest/environment" "^29.5.0" - "@jest/expect" "^29.5.0" - "@jest/types" "^29.5.0" - jest-mock "^29.5.0" + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/types" "^29.6.1" + jest-mock "^29.6.1" -"@jest/reporters@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" - integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== +"@jest/reporters@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.1.tgz#3325a89c9ead3cf97ad93df3a427549d16179863" + integrity sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/console" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -1975,9 +1972,9 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.5.0" - jest-util "^29.5.0" - jest-worker "^29.5.0" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + jest-worker "^29.6.1" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" @@ -1990,40 +1987,40 @@ dependencies: "@sinclair/typebox" "^0.24.1" -"@jest/schemas@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" - integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== dependencies: - "@sinclair/typebox" "^0.25.16" + "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" - integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== +"@jest/source-map@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.0.tgz#bd34a05b5737cb1a99d43e1957020ac8e5b9ddb1" + integrity sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA== dependencies: - "@jridgewell/trace-mapping" "^0.3.15" + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" - integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== +"@jest/test-result@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.6.1.tgz#850e565a3f58ee8ca6ec424db00cb0f2d83c36ba" + integrity sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw== dependencies: - "@jest/console" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/console" "^29.6.1" + "@jest/types" "^29.6.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" - integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== +"@jest/test-sequencer@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz#e3e582ee074dd24ea9687d7d1aaf05ee3a9b068e" + integrity sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg== dependencies: - "@jest/test-result" "^29.5.0" + "@jest/test-result" "^29.6.1" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.6.1" slash "^3.0.0" "@jest/transform@28.1.3": @@ -2047,22 +2044,22 @@ slash "^3.0.0" write-file-atomic "^4.0.1" -"@jest/transform@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" - integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== +"@jest/transform@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.1.tgz#acb5606019a197cb99beda3c05404b851f441c92" + integrity sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.5.0" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.6.1" jest-regex-util "^29.4.3" - jest-util "^29.5.0" + jest-util "^29.6.1" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -2080,12 +2077,12 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jest/types@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" - integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.0" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -2134,7 +2131,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== @@ -2429,10 +2426,10 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== -"@sinclair/typebox@^0.25.16": - version "0.25.24" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" - integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sinonjs/commons@^3.0.0": version "3.0.0" @@ -2442,9 +2439,9 @@ type-detect "4.0.8" "@sinonjs/fake-timers@^10.0.2": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz#b3e322a34c5f26e3184e7f6115695f299c1b1194" - integrity sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg== + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: "@sinonjs/commons" "^3.0.0" @@ -2568,9 +2565,9 @@ "@types/jest" "*" "@types/jest@*": - version "29.5.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b" - integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg== + version "29.5.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" + integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2613,14 +2610,14 @@ "@types/node" "*" "@types/node@*", "@types/node@>=13.7.0": - version "20.3.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" - integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== + version "20.4.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.2.tgz#129cc9ae69f93824f92fac653eebfb4812ab4af9" + integrity sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw== "@types/node@^18.11.11": - version "18.16.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.18.tgz#85da09bafb66d4bc14f7c899185336d0c1736390" - integrity sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw== + version "18.16.19" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.19.tgz#cb03fca8910fdeb7595b755126a8a78144714eea" + integrity sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2685,87 +2682,87 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.40.1": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz#8d466aa21abea4c3f37129997b198d141f09e76f" - integrity sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.59.11" - "@typescript-eslint/type-utils" "5.59.11" - "@typescript-eslint/utils" "5.59.11" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" natural-compare-lite "^1.4.0" semver "^7.3.7" tsutils "^3.21.0" "@typescript-eslint/parser@^5.40.1": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.11.tgz#af7d4b7110e3068ce0b97550736de455e4250103" - integrity sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== dependencies: - "@typescript-eslint/scope-manager" "5.59.11" - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/typescript-estree" "5.59.11" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz#5d131a67a19189c42598af9fb2ea1165252001ce" - integrity sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q== +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/visitor-keys" "5.59.11" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/type-utils@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz#5eb67121808a84cb57d65a15f48f5bdda25f2346" - integrity sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g== +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== dependencies: - "@typescript-eslint/typescript-estree" "5.59.11" - "@typescript-eslint/utils" "5.59.11" + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.11.tgz#1a9018fe3c565ba6969561f2a49f330cf1fe8db1" - integrity sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/typescript-estree@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz#b2caaa31725e17c33970c1197bcd54e3c5f42b9f" - integrity sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA== +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/visitor-keys" "5.59.11" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.11.tgz#9dbff49dc80bfdd9289f9f33548f2e8db3c59ba1" - integrity sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg== +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.59.11" - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/typescript-estree" "5.59.11" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz#dca561ddad169dc27d62396d64f45b2d2c3ecc56" - integrity sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA== +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: - "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" JSONStream@^1.0.4: @@ -2794,10 +2791,10 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^8.4.1, acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.4.1, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== aggregate-error@^3.0.0: version "3.1.0" @@ -2999,6 +2996,18 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +arraybuffer.prototype.slice@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz#9b5ea3868a6eebc30273da577eb888381c0044bb" + integrity sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -3044,9 +3053,9 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== aws-sdk@^2.1377.0: - version "2.1396.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1396.0.tgz#14dc92f874aca039a329d5a96f9f894fd9159252" - integrity sha512-5tAzB4pO9mfwb4XbDIv7wj4IsxaLI+KEAUZ8CR80sh2OdsP9AVGtMGH61dH6DQbHxCiwtLyQuoy7gZEuXv2ldQ== + version "2.1418.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1418.0.tgz#3c04e3484b9c826492b1d1d10f4ba19f4860e2a0" + integrity sha512-6WDMJQAWKwVt+44+61c/SAXKpUSwToqBMeaqizhEe3GN8TWfxMc9RfCnsYIIwS+L+5hedmKC5oc6Fg2ujs8KUQ== dependencies: buffer "4.9.2" events "1.1.1" @@ -3093,12 +3102,12 @@ axios@^1.3.4: form-data "^4.0.0" proxy-from-env "^1.1.0" -babel-jest@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" - integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== +babel-jest@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.1.tgz#a7141ad1ed5ec50238f3cd36127636823111233a" + integrity sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A== dependencies: - "@jest/transform" "^29.5.0" + "@jest/transform" "^29.6.1" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" babel-preset-jest "^29.5.0" @@ -3302,13 +3311,13 @@ brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.8" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.8.tgz#db2498e1f4b80ed199c076248a094935860b6017" - integrity sha512-j+7xYe+v+q2Id9qbBeCI8WX5NmZSRe8es1+0xntD/+gaWXznP8tFEkv5IgSaHf5dS1YwVMbX/4W6m937mj+wQw== +browserslist@^4.21.9: + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== dependencies: - caniuse-lite "^1.0.30001502" - electron-to-chromium "^1.4.428" + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" node-releases "^2.0.12" update-browserslist-db "^1.0.11" @@ -3368,9 +3377,9 @@ bull@^4.10.2, bull@^4.7.0: uuid "^8.3.0" bullmq@^3.13.3: - version "3.15.5" - resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-3.15.5.tgz#b8e80f47cd167a0ce77e34e47404adcb98447813" - integrity sha512-NotMUfU5wBAjZQgcl/F5UMUIs1DiXDRmZBN5BPuSQ841w7mYlaghTMGSy0H+kAwFa7388e7/5COTiX8EUxFo5w== + version "3.15.8" + resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-3.15.8.tgz#e8ec5b46b0b7d7ce57e509280d03745109411e05" + integrity sha512-k3uimHGhl5svqD7SEak+iI6c5DxeLOaOXzCufI9Ic0ST3nJr69v71TGR4cXCTXdgCff3tLec5HgoBnfyWjgn5A== dependencies: cron-parser "^4.6.0" glob "^8.0.3" @@ -3428,10 +3437,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001502: - version "1.0.30001502" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001502.tgz#f7e4a76eb1d2d585340f773767be1fefc118dca8" - integrity sha512-AZ+9tFXw1sS0o0jcpJQIXvFTOB/xGiQ4OQ2t98QX3NDn2EZTSRBC801gxrsGgViuq2ak/NLkNgSNEPtCr5lfKg== +caniuse-lite@^1.0.30001503: + version "1.0.30001517" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8" + integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA== case@1.6.3: version "1.6.3" @@ -3599,9 +3608,9 @@ co@^4.6.0: integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0: version "1.9.3" @@ -3654,7 +3663,7 @@ commander@^10.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== -commander@^9.1.0, commander@^9.4.1: +commander@^9.4.1: version "9.5.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== @@ -3739,11 +3748,11 @@ cookie@0.4.2: integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.25.1: - version "3.31.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.0.tgz#4030847c0766cc0e803dcdfb30055d7ef2064bf1" - integrity sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw== + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" + integrity sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA== dependencies: - browserslist "^4.21.5" + browserslist "^4.21.9" core-util-is@1.0.2: version "1.0.2" @@ -3842,6 +3851,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +dayjs@^1.11.9: + version "1.11.9" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" + integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + db-errors@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/db-errors/-/db-errors-0.2.3.tgz#a6a38952e00b20e790f2695a6446b3c65497ffa2" @@ -3995,9 +4009,9 @@ dot-prop@^5.1.0: is-obj "^2.0.0" dotenv@^16.0.3: - version "16.1.4" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.1.4.tgz#67ac1a10cd9c25f5ba604e4e08bc77c0ebe0ca8c" - integrity sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw== + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== dotty@0.1.2: version "0.1.2" @@ -4029,10 +4043,10 @@ ejs@3.1.7: dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.428: - version "1.4.428" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.428.tgz#c31fc88e854f49d8305cdabf6ec934ff1588a902" - integrity sha512-L7uUknyY286of0AYC8CKfgWstD0Smk2DvHDi9F0GWQhSH90Bzi7iDrmCbZKz75tYJxeGSAc7TYeKpmbjMDoh1w== +electron-to-chromium@^1.4.431: + version "1.4.466" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.466.tgz#17193d70f203da3d52a89c653b8d89f47a51d79d" + integrity sha512-TSkRvbXRXD8BwhcGlZXDsbI2lRoP8dvqR7LQnqQNk9KxXBc4tG8O+rTuXgTyIpEdiqSGKEBSqrxdqEntnjNncA== elliptic@^6.5.4: version "6.5.4" @@ -4075,17 +4089,18 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" - integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== + version "1.22.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" + integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== dependencies: array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.1" available-typed-arrays "^1.0.5" call-bind "^1.0.2" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.2.0" + get-intrinsic "^1.2.1" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" @@ -4105,14 +4120,18 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: object-inspect "^1.12.3" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.0" + safe-array-concat "^1.0.0" safe-regex-test "^1.0.0" string.prototype.trim "^1.2.7" string.prototype.trimend "^1.0.6" string.prototype.trimstart "^1.0.6" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" + which-typed-array "^1.1.10" es-set-tostringtag@^2.0.1: version "2.0.1" @@ -4243,9 +4262,9 @@ eslint-config-airbnb-base@^15.0.0: semver "^6.3.0" eslint-config-airbnb-typescript@^17.0.0: - version "17.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz#360dbcf810b26bbcf2ff716198465775f1c49a07" - integrity sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g== + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz#fda960eee4a510f092a9a1c139035ac588937ddc" + integrity sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig== dependencies: eslint-config-airbnb-base "^15.0.0" @@ -4307,9 +4326,9 @@ eslint-scope@^5.1.1: estraverse "^4.1.1" eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + version "7.2.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.1.tgz#936821d3462675f25a18ac5fd88a67cc15b393bd" + integrity sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -4320,14 +4339,14 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== eslint@^8.25.0: - version "8.42.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.42.0.tgz#7bebdc3a55f9ed7167251fe7259f75219cade291" - integrity sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A== + version "8.45.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.45.0.tgz#bab660f90d18e1364352c0a6b7c6db8edb458b78" + integrity sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.3" - "@eslint/js" "8.42.0" + "@eslint/eslintrc" "^2.1.0" + "@eslint/js" "8.44.0" "@humanwhocodes/config-array" "^0.11.10" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -4339,7 +4358,7 @@ eslint@^8.25.0: escape-string-regexp "^4.0.0" eslint-scope "^7.2.0" eslint-visitor-keys "^3.4.1" - espree "^9.5.2" + espree "^9.6.0" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -4349,7 +4368,6 @@ eslint@^8.25.0: globals "^13.19.0" graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" @@ -4359,9 +4377,8 @@ eslint@^8.25.0: lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" esm@^3.2.25: @@ -4369,12 +4386,12 @@ esm@^3.2.25: resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^9.5.2: - version "9.5.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b" - integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw== +espree@^9.6.0: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" @@ -4475,16 +4492,17 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^29.0.0, expect@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" - integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== +expect@^29.0.0, expect@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.1.tgz#64dd1c8f75e2c0b209418f2b8d36a07921adfdf1" + integrity sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g== dependencies: - "@jest/expect-utils" "^29.5.0" + "@jest/expect-utils" "^29.6.1" + "@types/node" "*" jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" express@4.17.3: version "4.17.3" @@ -4573,9 +4591,9 @@ fast-diff@^1.1.2: integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + version "3.3.0" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" + integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -4593,7 +4611,7 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fastest-validator@^1.16.0: +fastest-validator@^1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/fastest-validator/-/fastest-validator-1.17.0.tgz#0c032e9c42c40a237d24b20be187f732ebdcc062" integrity sha512-37U/JDP72QSFqcvNnO81f0Aeu9og+5I3mc55b2v2RbV0S2I7KvQEdBtrFeIvaYVgam1bDUgy9F9AK9HolByogA== @@ -4803,7 +4821,7 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== @@ -4897,15 +4915,15 @@ glob@8.0.3: once "^1.3.0" glob@^10.0.0: - version "10.2.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.2.7.tgz#9dd2828cd5bc7bd861e7738d91e7113dda41d7d8" - integrity sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA== + version "10.3.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" + integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== dependencies: foreground-child "^3.1.0" jackspeak "^2.0.3" minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2" - path-scurry "^1.7.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7, glob@^7.2.0: version "7.2.3" @@ -4980,11 +4998,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -4998,9 +5011,9 @@ graphql-tag@^2.12.6: tslib "^2.1.0" graphql@^16.6.0: - version "16.6.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" - integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== + version "16.7.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.7.1.tgz#11475b74a7bff2aefd4691df52a0eca0abd9b642" + integrity sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg== har-schema@^2.0.0: version "2.0.0" @@ -5343,7 +5356,7 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: +ipaddr.js@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== @@ -5532,15 +5545,11 @@ is-text-path@^1.0.1: text-extensions "^1.0.0" is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.11" is-typedarray@~1.0.0: version "1.0.0" @@ -5564,6 +5573,11 @@ isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -5648,87 +5662,87 @@ jest-changed-files@^29.5.0: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^29.0.1, jest-circus@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" - integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== +jest-circus@^29.0.1, jest-circus@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.1.tgz#861dab37e71a89907d1c0fabc54a0019738ed824" + integrity sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ== dependencies: - "@jest/environment" "^29.5.0" - "@jest/expect" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" is-generator-fn "^2.0.0" - jest-each "^29.5.0" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-runtime "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" + jest-each "^29.6.1" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" p-limit "^3.1.0" - pretty-format "^29.5.0" + pretty-format "^29.6.1" pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" - integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== +jest-cli@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.6.1.tgz#99d9afa7449538221c71f358f0fdd3e9c6e89f72" + integrity sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing== dependencies: - "@jest/core" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/core" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-config "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" - integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== +jest-config@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.6.1.tgz#d785344509065d53a238224c6cdc0ed8e2f2f0dd" + integrity sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.5.0" - "@jest/types" "^29.5.0" - babel-jest "^29.5.0" + "@jest/test-sequencer" "^29.6.1" + "@jest/types" "^29.6.1" + babel-jest "^29.6.1" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.5.0" - jest-environment-node "^29.5.0" + jest-circus "^29.6.1" + jest-environment-node "^29.6.1" jest-get-type "^29.4.3" jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-runner "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-resolve "^29.6.1" + jest-runner "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.5.0" + pretty-format "^29.6.1" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" - integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== +jest-diff@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.1.tgz#13df6db0a89ee6ad93c747c75c85c70ba941e545" + integrity sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg== dependencies: chalk "^4.0.0" diff-sequences "^29.4.3" jest-get-type "^29.4.3" - pretty-format "^29.5.0" + pretty-format "^29.6.1" jest-docblock@^29.4.3: version "29.4.3" @@ -5737,28 +5751,28 @@ jest-docblock@^29.4.3: dependencies: detect-newline "^3.0.0" -jest-each@^29.0.1, jest-each@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" - integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== +jest-each@^29.0.1, jest-each@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.6.1.tgz#975058e5b8f55c6780beab8b6ab214921815c89c" + integrity sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" chalk "^4.0.0" jest-get-type "^29.4.3" - jest-util "^29.5.0" - pretty-format "^29.5.0" + jest-util "^29.6.1" + pretty-format "^29.6.1" -jest-environment-node@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" - integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== +jest-environment-node@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.6.1.tgz#08a122dece39e58bc388da815a2166c58b4abec6" + integrity sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ== dependencies: - "@jest/environment" "^29.5.0" - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" - jest-mock "^29.5.0" - jest-util "^29.5.0" + jest-mock "^29.6.1" + jest-util "^29.6.1" jest-get-type@^29.4.3: version "29.4.3" @@ -5784,32 +5798,32 @@ jest-haste-map@^28.1.3: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" - integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== +jest-haste-map@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.1.tgz#62655c7a1c1b349a3206441330fb2dbdb4b63803" + integrity sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" jest-regex-util "^29.4.3" - jest-util "^29.5.0" - jest-worker "^29.5.0" + jest-util "^29.6.1" + jest-worker "^29.6.1" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" - integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== +jest-leak-detector@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz#66a902c81318e66e694df7d096a95466cb962f8e" + integrity sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ== dependencies: jest-get-type "^29.4.3" - pretty-format "^29.5.0" + pretty-format "^29.6.1" jest-light-runner@^0.4.1: version "0.4.1" @@ -5824,39 +5838,39 @@ jest-light-runner@^0.4.1: piscina "^3.1.0" supports-color "^9.2.1" -jest-matcher-utils@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" - integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== +jest-matcher-utils@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz#6c60075d84655d6300c5d5128f46531848160b53" + integrity sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA== dependencies: chalk "^4.0.0" - jest-diff "^29.5.0" + jest-diff "^29.6.1" jest-get-type "^29.4.3" - pretty-format "^29.5.0" + pretty-format "^29.6.1" -jest-message-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" - integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== +jest-message-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.1.tgz#d0b21d87f117e1b9e165e24f245befd2ff34ff8d" + integrity sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.5.0" + pretty-format "^29.6.1" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.0.1, jest-mock@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" - integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== +jest-mock@^29.0.1, jest-mock@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.6.1.tgz#049ee26aea8cbf54c764af649070910607316517" + integrity sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/node" "*" - jest-util "^29.5.0" + jest-util "^29.6.1" jest-pnp-resolver@^1.2.2: version "1.2.3" @@ -5873,112 +5887,110 @@ jest-regex-util@^29.4.3: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-resolve-dependencies@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" - integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== +jest-resolve-dependencies@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz#b85b06670f987a62515bbf625d54a499e3d708f5" + integrity sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw== dependencies: jest-regex-util "^29.4.3" - jest-snapshot "^29.5.0" + jest-snapshot "^29.6.1" -jest-resolve@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" - integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== +jest-resolve@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.1.tgz#4c3324b993a85e300add2f8609f51b80ddea39ee" + integrity sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.6.1" jest-pnp-resolver "^1.2.2" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-util "^29.6.1" + jest-validate "^29.6.1" resolve "^1.20.0" resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" - integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== - dependencies: - "@jest/console" "^29.5.0" - "@jest/environment" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" +jest-runner@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.6.1.tgz#54557087e7972d345540d622ab5bfc3d8f34688c" + integrity sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/environment" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" jest-docblock "^29.4.3" - jest-environment-node "^29.5.0" - jest-haste-map "^29.5.0" - jest-leak-detector "^29.5.0" - jest-message-util "^29.5.0" - jest-resolve "^29.5.0" - jest-runtime "^29.5.0" - jest-util "^29.5.0" - jest-watcher "^29.5.0" - jest-worker "^29.5.0" + jest-environment-node "^29.6.1" + jest-haste-map "^29.6.1" + jest-leak-detector "^29.6.1" + jest-message-util "^29.6.1" + jest-resolve "^29.6.1" + jest-runtime "^29.6.1" + jest-util "^29.6.1" + jest-watcher "^29.6.1" + jest-worker "^29.6.1" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" - integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== - dependencies: - "@jest/environment" "^29.5.0" - "@jest/fake-timers" "^29.5.0" - "@jest/globals" "^29.5.0" - "@jest/source-map" "^29.4.3" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" +jest-runtime@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.6.1.tgz#8a0fc9274ef277f3d70ba19d238e64334958a0dc" + integrity sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/globals" "^29.6.1" + "@jest/source-map" "^29.6.0" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" + jest-resolve "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" slash "^3.0.0" strip-bom "^4.0.0" -jest-snapshot@^29.0.1, jest-snapshot@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" - integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== +jest-snapshot@^29.0.1, jest-snapshot@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.1.tgz#0d083cb7de716d5d5cdbe80d598ed2fbafac0239" + integrity sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" - "@types/babel__traverse" "^7.0.6" + "@jest/expect-utils" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.5.0" + expect "^29.6.1" graceful-fs "^4.2.9" - jest-diff "^29.5.0" + jest-diff "^29.6.1" jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" natural-compare "^1.4.0" - pretty-format "^29.5.0" - semver "^7.3.5" + pretty-format "^29.6.1" + semver "^7.5.3" jest-util@^28.1.3: version "28.1.3" @@ -5992,42 +6004,42 @@ jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.0.0, jest-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" - integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== +jest-util@^29.0.0, jest-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.1.tgz#c9e29a87a6edbf1e39e6dee2b4689b8a146679cb" + integrity sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" - integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== +jest-validate@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.6.1.tgz#765e684af6e2c86dce950aebefbbcd4546d69f7b" + integrity sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" camelcase "^6.2.0" chalk "^4.0.0" jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^29.5.0" + pretty-format "^29.6.1" -jest-watcher@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" - integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== +jest-watcher@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.6.1.tgz#7c0c43ddd52418af134c551c92c9ea31e5ec942e" + integrity sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA== dependencies: - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.5.0" + jest-util "^29.6.1" string-length "^4.0.1" jest-worker@^28.1.3: @@ -6039,25 +6051,25 @@ jest-worker@^28.1.3: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" - integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== +jest-worker@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.1.tgz#64b015f0e985ef3a8ad049b61fe92b3db74a5319" + integrity sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA== dependencies: "@types/node" "*" - jest-util "^29.5.0" + jest-util "^29.6.1" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^29.3.1: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" - integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.1.tgz#74be1cb719c3abe439f2d94aeb18e6540a5b02ad" + integrity sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw== dependencies: - "@jest/core" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/core" "^29.6.1" + "@jest/types" "^29.6.1" import-local "^3.0.2" - jest-cli "^29.5.0" + jest-cli "^29.6.1" jmespath@0.16.0: version "0.16.0" @@ -6181,12 +6193,12 @@ kleur@^4.1.4, kleur@^4.1.5: integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== knex@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/knex/-/knex-2.4.2.tgz#a34a289d38406dc19a0447a78eeaf2d16ebedd61" - integrity sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg== + version "2.5.1" + resolved "https://registry.yarnpkg.com/knex/-/knex-2.5.1.tgz#a6c6b449866cf4229f070c17411f23871ba52ef9" + integrity sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA== dependencies: colorette "2.0.19" - commander "^9.1.0" + commander "^10.0.0" debug "4.3.4" escalade "^3.1.1" esm "^3.2.25" @@ -6194,7 +6206,7 @@ knex@^2.3.0: getopts "2.3.0" interpret "^2.2.0" lodash "^4.17.21" - pg-connection-string "2.5.0" + pg-connection-string "2.6.1" rechoir "^0.8.0" resolve-from "^5.0.0" tarn "^3.0.2" @@ -6258,9 +6270,9 @@ lines-and-columns@^1.1.6: integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lint-staged@^13.1.0: - version "13.2.2" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.2.tgz#5e711d3139c234f73402177be2f8dd312e6508ca" - integrity sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA== + version "13.2.3" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.3.tgz#f899aad6c093473467e9c9e316e3c2d8a28f87a7" + integrity sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg== dependencies: chalk "5.2.0" cli-truncate "^3.1.0" @@ -6438,10 +6450,10 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^9.1.1: - version "9.1.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.2.tgz#255fdbc14b75589d6d0e73644ca167a8db506835" - integrity sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ== +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" + integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== lru-queue@0.1, lru-queue@^0.1.0: version "0.1.0" @@ -6635,9 +6647,9 @@ minimatch@^5.0.1: brace-expansion "^2.0.1" minimatch@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" - integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" @@ -6660,10 +6672,10 @@ minimist@1.2.8, minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -"minipass@^5.0.0 || ^6.0.2": - version "6.0.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81" - integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" + integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== mkdirp@1.0.4, mkdirp@^1.0.4: version "1.0.4" @@ -6691,9 +6703,9 @@ moleculer-bull@^0.3.1: lodash "^4.17.21" moleculer-db@^0.8.21: - version "0.8.23" - resolved "https://registry.yarnpkg.com/moleculer-db/-/moleculer-db-0.8.23.tgz#f38aa4597917d40e2c7550150544e095c8af17c0" - integrity sha512-jjdRmdLBJGF8ZXDVcaS65dfXPffz4dKU1Hmw/z7J4kF/a2mYvg92/k8LNlX5j3uGYPMXFiSl3IDVXMERTmWWsA== + version "0.8.24" + resolved "https://registry.yarnpkg.com/moleculer-db/-/moleculer-db-0.8.24.tgz#9b6a39056c41f3a116d56767c6d4dc8c407067ad" + integrity sha512-ABJOTPTJFXta0w/nn5zY/o3cbkqUGFrfdPeY+VaurOt/wzVuCZ89wme+sgSRamnwhPbfmqUrDQG8MGy7BG0uTQ== dependencies: "@seald-io/nedb" "^3.0.0" bluebird "^3.7.2" @@ -6718,9 +6730,9 @@ moleculer-repl@^0.7.3: yargs-parser "^21.1.1" moleculer-web@^0.10.5: - version "0.10.5" - resolved "https://registry.yarnpkg.com/moleculer-web/-/moleculer-web-0.10.5.tgz#76be7bf8f7200aff45c6ca61bb96feba6f412b44" - integrity sha512-b4LFl67ESo8tmYtfuu91r8nRyLV158URAbf21dsNvf4hiVJW0u0BFwkkQF7Bn1Y+KRYCnAXNjGt4B+q5+cKnIw== + version "0.10.6" + resolved "https://registry.yarnpkg.com/moleculer-web/-/moleculer-web-0.10.6.tgz#a8bc99b7f153b882018aec9fa553496a86b864f5" + integrity sha512-MGNIH6mXLU2Wj63bAgoVzdhMKXALp99F5UHuiBgS2ywakdWEUl/q7GlMblvscioCCkXuUWezId85J0yioYxedg== dependencies: "@fastify/busboy" "^1.0.0" body-parser "^1.19.0" @@ -6731,19 +6743,19 @@ moleculer-web@^0.10.5: kleur "^4.1.4" lodash "^4.17.21" path-to-regexp "^3.1.0" - qs "^6.10.1" + qs "^6.11.0" serve-static "^1.14.1" moleculer@^0.14.16, moleculer@^0.14.27: - version "0.14.29" - resolved "https://registry.yarnpkg.com/moleculer/-/moleculer-0.14.29.tgz#5ac1fe45c6f2492ddb4601002a46ed7b14e6aa87" - integrity sha512-19SbGgZGL6tHpgQAiEg8ZTkPR3IagJ/ruZr3038fZchTUHZ0wMZSrh62Or3iUJBMEOFqZ5S5nHpKtV40jpoeew== + version "0.14.30" + resolved "https://registry.yarnpkg.com/moleculer/-/moleculer-0.14.30.tgz#ab735b80b05f5d5b2450bc0c5bf10b891cc719f8" + integrity sha512-XxM2oYPofd5FhHTnu8+gzEcEMy92rHw2ey6f4bd+aMCWNFQpueg2eKURZPn5rSpif7mS2AwKMPGGgEqoRnT7+A== dependencies: args "^5.0.3" eventemitter2 "^6.4.9" - fastest-validator "^1.16.0" + fastest-validator "^1.17.0" glob "^7.2.0" - ipaddr.js "^2.0.1" + ipaddr.js "^2.1.0" kleur "^4.1.5" lodash "^4.17.21" lru-cache "^6.0.0" @@ -6854,9 +6866,9 @@ node-addon-api@^3.0.0: integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== node-fetch@^2.6.7: - version "2.6.11" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" - integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w== + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== dependencies: whatwg-url "^5.0.0" @@ -6876,9 +6888,9 @@ node-int64@^0.4.0: integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" - integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== normalize-package-data@^2.5.0: version "2.5.0" @@ -6968,9 +6980,9 @@ object.values@^1.1.6: es-abstract "^1.20.4" objection@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/objection/-/objection-3.0.1.tgz#f67dc698187d10524e5d1b5d37a54e5bba49a42a" - integrity sha512-rqNnyQE+C55UHjdpTOJEKQHJGZ/BGtBBtgxdUpKG4DQXRUmqxfmgS/MhPWxB9Pw0mLSVLEltr6soD4c0Sddy0Q== + version "3.0.4" + resolved "https://registry.yarnpkg.com/objection/-/objection-3.0.4.tgz#d2f60cec49f7f917c4b97260add9e2c2cdb52c0b" + integrity sha512-0XaStHtOBcux4nlffUj8gOpxUsAPE+sLDZPml6n79WQhInYLBvjctU/uv2kEUPPybAJK8YL6ETkLtft9UOj7nQ== dependencies: ajv "^8.6.2" db-errors "^0.2.3" @@ -7017,17 +7029,17 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" ora@^5.4.1: version "5.4.1" @@ -7161,13 +7173,13 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.7.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.9.2.tgz#90f9d296ac5e37e608028e28a447b11d385b3f63" - integrity sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== dependencies: - lru-cache "^9.1.1" - minipass "^5.0.0 || ^6.0.2" + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-to-regexp@0.1.7: version "0.1.7" @@ -7194,20 +7206,15 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -pg-cloudflare@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.0.tgz#833d70870d610d14bf9df7afb40e1cba310c17a0" - integrity sha512-tGM8/s6frwuAIyRcJ6nWcIvd3+3NmUKIs6OjviIm1HPPFEt5MzQDOTBQyhPWg/m0kCl95M6gA1JaIXtS8KovOA== - -pg-connection-string@2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34" - integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ== +pg-cloudflare@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98" + integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q== -pg-connection-string@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.0.tgz#12a36cc4627df19c25cc1b9b736cc39ee1f73ae8" - integrity sha512-x14ibktcwlHKoHxx9X3uTVW9zIGR41ZB6QNhHb21OPNdCCO3NaRnpJuwKIQSR4u+Yqjx4HCvy7Hh7VSy1U4dGg== +pg-connection-string@2.6.1, pg-connection-string@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.1.tgz#78c23c21a35dd116f48e12e23c0965e8d9e2cbfb" + integrity sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg== pg-hstore@^2.3.4: version "2.3.4" @@ -7221,10 +7228,10 @@ pg-int8@1.0.1: resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== -pg-pool@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.0.tgz#3190df3e4747a0d23e5e9e8045bcd99bda0a712e" - integrity sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ== +pg-pool@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.1.tgz#5a902eda79a8d7e3c928b77abf776b3cb7d351f7" + integrity sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og== pg-protocol@^1.6.0: version "1.6.0" @@ -7243,19 +7250,19 @@ pg-types@^2.1.0: postgres-interval "^1.1.0" pg@^8.8.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.0.tgz#a37e534e94b57a7ed811e926f23a7c56385f55d9" - integrity sha512-meLUVPn2TWgJyLmy7el3fQQVwft4gU5NGyvV0XbD41iU9Jbg8lCH4zexhIkihDzVHJStlt6r088G6/fWeNjhXA== + version "8.11.1" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.1.tgz#297e0eb240306b1e9e4f55af8a3bae76ae4810b1" + integrity sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" - pg-connection-string "^2.6.0" - pg-pool "^3.6.0" + pg-connection-string "^2.6.1" + pg-pool "^3.6.1" pg-protocol "^1.6.0" pg-types "^2.1.0" pgpass "1.x" optionalDependencies: - pg-cloudflare "^1.1.0" + pg-cloudflare "^1.1.1" pgpass@1.x: version "1.0.5" @@ -7280,9 +7287,9 @@ pidtree@^0.6.0: integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== piscina@^3.1.0: version "3.2.0" @@ -7346,12 +7353,12 @@ pretty-bytes@^5.6.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -pretty-format@^29.0.0, pretty-format@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" - integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== +pretty-format@^29.0.0, pretty-format@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.1.tgz#ec838c288850b7c4f9090b867c2d4f4edbfb0f3e" + integrity sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.0" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -7388,9 +7395,9 @@ protobufjs@^6.11.2, protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: long "^4.0.0" protobufjs@^7.1.2: - version "7.2.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.3.tgz#01af019e40d9c6133c49acbb3ff9e30f4f0f70b2" - integrity sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg== + version "7.2.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae" + integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -7455,7 +7462,7 @@ qs@6.9.7: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== -qs@^6.10.1: +qs@^6.11.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== @@ -7647,7 +7654,7 @@ regenerator-transform@^0.15.1: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== @@ -7838,6 +7845,16 @@ rxjs@^7.8.0: dependencies: tslib "^2.1.0" +safe-array-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" + integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -7873,29 +7890,29 @@ sax@>=0.6.0: integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== "semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.5.0: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== +semver@7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: - version "7.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" - integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - send@0.17.2: version "0.17.2" resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" @@ -8287,7 +8304,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -8327,9 +8344,9 @@ supports-color@^8.0.0: has-flag "^4.0.0" supports-color@^9.2.1: - version "9.3.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.3.1.tgz#34e4ad3c71c9a39dae3254ecc46c9b74e89e15a6" - integrity sha512-knBY82pjmnIzK3NifMo3RxEIRD9E0kIzV4BKcyTZ9+9kWgLMxd4PrsTSMoFQUabgRBbF8KOLRDCyKgNV+iK44Q== + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" @@ -8486,9 +8503,9 @@ trim-newlines@^3.0.0: integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== ts-jest@^29.0.3: - version "29.1.0" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891" - integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA== + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -8496,7 +8513,7 @@ ts-jest@^29.0.3: json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" - semver "7.x" + semver "^7.5.3" yargs-parser "^21.0.1" ts-node@^10.8.1, ts-node@^10.9.1: @@ -8543,9 +8560,9 @@ tslib@^1.8.1, tslib@^1.9.0: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.0, tslib@^2.1.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== tsutils@^3.21.0: version "3.21.0" @@ -8626,6 +8643,36 @@ type@^2.7.2: resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -8636,9 +8683,9 @@ typed-array-length@^1.0.4: is-typed-array "^1.1.9" "typescript@^4.6.4 || ^5.0.0": - version "5.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" - integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== typescript@^4.8.4: version "4.9.5" @@ -8850,17 +8897,16 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-typed-array@^1.1.2, which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.10, which-typed-array@^1.1.11, which-typed-array@^1.1.2: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== dependencies: available-typed-arrays "^1.0.5" call-bind "^1.0.2" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" which@^2.0.1: version "2.0.2" @@ -8869,11 +8915,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" From 8c01c5c1afc92c2869c8b41e8bd53987356defae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20Th=C3=A0nh=20Phong?= <49814372+phamphong9981@users.noreply.github.com> Date: Wed, 23 Aug 2023 10:48:28 +0700 Subject: [PATCH 20/54] refactor: migrate cw721 activity missing smart contract event ( main ) (#322) * refactor: migrate cw721 activity missing smart contract event * feat: api * test: test params * test: test params * test: test params * test: test params * test: test params * test: test params * refactor: code * refactor: code * refactor: code * refactor: code * refactor: code * fix: ci --- ci/network.json.ci | 3 +- src/models/cw721_tx.ts | 2 +- .../api-gateways/api_gateway.service.ts | 2 +- .../api-gateways/cw721_admin.service.ts | 57 ++++++++++ src/services/cw20/cw20_reindexing.service.ts | 2 +- .../cw721/cw721-reindexing.service.ts | 105 +++++++++++++----- src/services/cw721/cw721.service.ts | 1 + .../services/api-gateways/cw721_admin.spec.ts | 59 ++++++++++ .../services/cw20/cw20_reindexing.spec.ts | 10 +- .../cw721/cw721-missing-contract.spec.ts | 41 ++++++- 10 files changed, 246 insertions(+), 36 deletions(-) create mode 100644 src/services/api-gateways/cw721_admin.service.ts create mode 100644 test/unit/services/api-gateways/cw721_admin.spec.ts diff --git a/ci/network.json.ci b/ci/network.json.ci index 7297b053b..e43b64657 100644 --- a/ci/network.json.ci +++ b/ci/network.json.ci @@ -3,6 +3,7 @@ "chainId": "aura-testnet-2", "RPC": ["http://localhost:26657"], "LCD": ["http://localhost:1317"], - "databaseName": "horoscope_dev_auratestnet1" + "databaseName": "horoscope_dev_auratestnet1", + "moleculerNamespace": "namespace-auratestnet" } ] diff --git a/src/models/cw721_tx.ts b/src/models/cw721_tx.ts index 7ec84fbc3..3f20ce128 100644 --- a/src/models/cw721_tx.ts +++ b/src/models/cw721_tx.ts @@ -29,7 +29,7 @@ export default class CW721Activity extends BaseModel { to?: string; - height?: number; + height!: number; smart_contract_event_id!: number; diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index 9ac09560a..1f4183319 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -37,7 +37,7 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; path: '/admin', autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services mappingPolicy: 'restrict', // allow action called with exact method - whitelist: ['v1.cw20-admin.*'], + whitelist: ['v1.cw721-admin.*', 'v1.cw20-admin.*'], }, ], // empty cors object will have moleculer to generate handler for preflight request and CORS header which allow all origin diff --git a/src/services/api-gateways/cw721_admin.service.ts b/src/services/api-gateways/cw721_admin.service.ts new file mode 100644 index 000000000..10ca84530 --- /dev/null +++ b/src/services/api-gateways/cw721_admin.service.ts @@ -0,0 +1,57 @@ +import { Post, Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { Context, ServiceBroker } from 'moleculer'; +import networks from '../../../network.json' assert { type: 'json' }; +import BaseService from '../../base/base.service'; +import { REINDEX_TYPE } from '../cw721/cw721-reindexing.service'; + +@Service({ + name: 'cw721-admin', + version: 1, +}) +export default class Cw721AdminService extends BaseService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Post('/cw721-reindexing', { + name: 'cw721Reindexing', + params: { + chainid: { + type: 'string', + optional: false, + enum: networks.map((network) => network.chainId), + }, + contractAddresses: { + type: 'array', + optional: false, + items: 'string', + }, + type: { + type: 'enum', + optional: false, + values: Object.values(REINDEX_TYPE), + }, + }, + }) + async cw721Reindexing( + ctx: Context< + { + chainid: string; + contractAddresses: string[]; + type: string; + }, + Record + > + ) { + const selectedChain = networks.find( + (network) => network.chainId === ctx.params.chainid + ); + return this.broker.call( + `v1.Cw721ReindexingService.reindexing@${selectedChain?.moleculerNamespace}`, + { + contractAddresses: ctx.params.contractAddresses, + type: ctx.params.type, + } + ); + } +} diff --git a/src/services/cw20/cw20_reindexing.service.ts b/src/services/cw20/cw20_reindexing.service.ts index b2caf4cb8..b0caab4fc 100644 --- a/src/services/cw20/cw20_reindexing.service.ts +++ b/src/services/cw20/cw20_reindexing.service.ts @@ -96,7 +96,7 @@ export default class Cw20ReindexingContract extends BullableService { const maxUpdatedHeightOwner = _.max(initBalances.map((holder) => holder.event_height)) || 0; let id = -1; - let lastUpdatedHeight = -1; + let lastUpdatedHeight = -1; await knex.transaction(async (trx) => { if (cw20Contract) { await Cw20Event.query() diff --git a/src/services/cw721/cw721-reindexing.service.ts b/src/services/cw721/cw721-reindexing.service.ts index 29f5ad442..274a9a074 100644 --- a/src/services/cw721/cw721-reindexing.service.ts +++ b/src/services/cw721/cw721-reindexing.service.ts @@ -19,14 +19,21 @@ import CW721Activity from '../../models/cw721_tx'; import { ICw721ReindexingHistoryParams } from './cw721.service'; export interface IAddressParam { - contractAddress: string; + contractAddresses: string[]; + type: string; } interface ICw721ReindexingServiceParams { contractAddress: string; smartContractId: number; + type: string; } +export const REINDEX_TYPE = { + ALL: 'all', + HISTORY: 'history', +}; + @Service({ name: SERVICE.V1.CW721ReindexingService.key, version: 1, @@ -41,7 +48,15 @@ export default class CW721ReindexingService extends BullableService { jobName: BULL_JOB_NAME.REINDEX_CW721_CONTRACT, }) async jobHandler(_payload: ICw721ReindexingServiceParams): Promise { - const { smartContractId, contractAddress } = _payload; + const { smartContractId, contractAddress, type } = _payload; + if (type === REINDEX_TYPE.ALL) { + await this.handleReindexAll(smartContractId, contractAddress); + } else if (type === REINDEX_TYPE.HISTORY) { + await this.handleReindexHistory(smartContractId, contractAddress); + } + } + + async handleReindexAll(smartContractId: number, contractAddress: string) { const cw721Contract = await CW721Contract.query() .withGraphJoined('smart_contract') .where('smart_contract.address', contractAddress) @@ -118,37 +133,77 @@ export default class CW721ReindexingService extends BullableService { ); } + async handleReindexHistory(smartContractId: number, contractAddress: string) { + const cw721Contract = await CW721Contract.query() + .withGraphJoined('smart_contract') + .where('smart_contract.address', contractAddress) + .select(['cw721_contract.id']) + .first() + .throwIfNotFound(); + const currentHeight = ( + await CW721Activity.query().max('height as height').throwIfNotFound() + )[0].height; + await CW721Activity.query() + .delete() + .where('cw721_contract_id', cw721Contract.id) + .andWhere('height', '<=', currentHeight); + // insert histories + await this.createJob( + BULL_JOB_NAME.REINDEX_CW721_HISTORY, + BULL_JOB_NAME.REINDEX_CW721_HISTORY, + { + smartContractId, + startBlock: config.crawlBlock.startBlock, + endBlock: currentHeight, + prevId: 0, + contractAddress, + } satisfies ICw721ReindexingHistoryParams, + { + removeOnComplete: true, + } + ); + } + @Action({ name: SERVICE.V1.CW721ReindexingService.Reindexing.key, params: { - contractAddress: 'string', + contractAddresses: { + type: 'array', + items: 'string', + optional: false, + }, + type: { + type: 'string', + optional: false, + }, }, }) public async reindexing(ctx: Context) { - const { contractAddress } = ctx.params; - const smartContract = await SmartContract.query() + const { contractAddresses, type } = ctx.params; + const smartContracts = await SmartContract.query() .withGraphJoined('code') - .where('address', contractAddress) - .first() - .throwIfNotFound(); - // check whether contract is CW721 type -> throw error to user - if (smartContract.code.type === 'CW721') { - await this.createJob( - BULL_JOB_NAME.REINDEX_CW721_CONTRACT, - BULL_JOB_NAME.REINDEX_CW721_CONTRACT, - { - contractAddress, - smartContractId: smartContract.id, - } satisfies ICw721ReindexingServiceParams, - { - jobId: contractAddress, + .whereIn('address', contractAddresses); + await Promise.all( + smartContracts.reduce((acc: Promise[], smartContract) => { + if (smartContract.code.type === 'CW721') { + acc.push( + this.createJob( + BULL_JOB_NAME.REINDEX_CW721_CONTRACT, + BULL_JOB_NAME.REINDEX_CW721_CONTRACT, + { + contractAddress: smartContract.address, + smartContractId: smartContract.id, + type, + } satisfies ICw721ReindexingServiceParams, + { + jobId: smartContract.address, + } + ) + ); } - ); - } else { - throw new Error( - `Smart contract ${ctx.params.contractAddress} is not CW721 type` - ); - } + return acc; + }, []) + ); } async _start(): Promise { diff --git a/src/services/cw721/cw721.service.ts b/src/services/cw721/cw721.service.ts index 0a505d672..b924d27c5 100644 --- a/src/services/cw721/cw721.service.ts +++ b/src/services/cw721/cw721.service.ts @@ -567,6 +567,7 @@ export default class Cw721HandlerService extends BullableService { BULL_JOB_NAME.REINDEX_CW721_CONTRACT ); await queue.remove(contractAddress); + this.logger.info('Reindex cw721 history done!!!'); } } diff --git a/test/unit/services/api-gateways/cw721_admin.spec.ts b/test/unit/services/api-gateways/cw721_admin.spec.ts new file mode 100644 index 000000000..0e2489c02 --- /dev/null +++ b/test/unit/services/api-gateways/cw721_admin.spec.ts @@ -0,0 +1,59 @@ +import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { Errors, ServiceBroker } from 'moleculer'; +import Cw721AdminService from '../../../../src/services/api-gateways/cw721_admin.service'; +import CW721ReindexingService from '../../../../src/services/cw721/cw721-reindexing.service'; +import Cw721HandlerService from '../../../../src/services/cw721/cw721.service'; + +@Describe('Test cw721 admin api service') +export default class Cw721AdminTest { + broker = new ServiceBroker({ + logger: false, + }); + + cw721Admin = this.broker.createService( + Cw721AdminService + ) as Cw721AdminService; + + cw721Reindex = this.broker.createService( + CW721ReindexingService + ) as CW721ReindexingService; + + cw721HandlerService = this.broker.createService( + Cw721HandlerService + ) as Cw721HandlerService; + + @BeforeAll() + async initSuite() { + await this.broker.start(); + } + + @AfterAll() + async tearDown() { + await this.broker.stop(); + } + + @Test('Invalid query format') + public async testInvalidQueryFormat() { + expect( + this.broker.call('v1.cw721-admin.cw721Reindexing', { + chainid: undefined, + contractAddress: 'abc', + type: 'all', + }) + ).rejects.toBeInstanceOf(Errors.ValidationError); + expect( + this.broker.call('v1.cw721-admin.cw721Reindexing', { + chainid: 'euphoria-2', + contractAddress: undefined, + type: 'all', + }) + ).rejects.toBeInstanceOf(Errors.ValidationError); + expect( + this.broker.call('v1.cw721-admin.cw721Reindexing', { + chainid: 'euphoria-2', + contractAddress: 'abc', + type: 'hihi', + }) + ).rejects.toBeInstanceOf(Errors.ValidationError); + } +} diff --git a/test/unit/services/cw20/cw20_reindexing.spec.ts b/test/unit/services/cw20/cw20_reindexing.spec.ts index 8d6eba414..868b7321c 100644 --- a/test/unit/services/cw20/cw20_reindexing.spec.ts +++ b/test/unit/services/cw20/cw20_reindexing.spec.ts @@ -151,11 +151,11 @@ export default class TestCw20ReindexingService { .orderBy('address', 'asc'); expect( cw20Holders.map((cw20Holder) => ({ - address: cw20Holder.address, - amount: cw20Holder.amount, - event_height: cw20Holder.last_updated_height, - contract_address: this.codeId.contracts[0].address, - })) + address: cw20Holder.address, + amount: cw20Holder.amount, + event_height: cw20Holder.last_updated_height, + contract_address: this.codeId.contracts[0].address, + })) ).toEqual(mockHolders); } } diff --git a/test/unit/services/cw721/cw721-missing-contract.spec.ts b/test/unit/services/cw721/cw721-missing-contract.spec.ts index 9ec072f55..fbe0ffec0 100644 --- a/test/unit/services/cw721/cw721-missing-contract.spec.ts +++ b/test/unit/services/cw721/cw721-missing-contract.spec.ts @@ -1,12 +1,14 @@ import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; -import { ServiceBroker } from 'moleculer'; +import { Errors, ServiceBroker } from 'moleculer'; import { BULL_JOB_NAME } from '../../../../src/common'; import knex from '../../../../src/common/utils/db_connection'; import { BlockCheckpoint, Code } from '../../../../src/models'; import CW721Contract from '../../../../src/models/cw721_contract'; import CW721Token from '../../../../src/models/cw721_token'; import { SmartContractEvent } from '../../../../src/models/smart_contract_event'; -import Cw721MissingContractService from '../../../../src/services/cw721/cw721-reindexing.service'; +import Cw721MissingContractService, { + REINDEX_TYPE, +} from '../../../../src/services/cw721/cw721-reindexing.service'; import Cw721HandlerService, { CW721_ACTION, } from '../../../../src/services/cw721/cw721.service'; @@ -331,6 +333,7 @@ export default class TestCw721MissingContractService { await this.cw721MissingContractService.jobHandler({ contractAddress: this.codeId.contracts[0].address, smartContractId: 1, + type: REINDEX_TYPE.ALL, }); const cw721Contract = await CW721Contract.query() .withGraphJoined('smart_contract') @@ -524,4 +527,38 @@ export default class TestCw721MissingContractService { expect(tokens[0].last_updated_height).toEqual(missingHistories[3].height); expect(tokens[0].burned).toEqual(true); } + + @Test('test action params') + public async testActionParams() { + expect( + this.broker.call('v1.Cw721ReindexingService.reindexing', { + contractAddresses: undefined, + type: 'all', + }) + ).rejects.toBeInstanceOf(Errors.ValidationError); + expect( + this.broker.call('v1.Cw721ReindexingService.reindexing', { + contractAddresses: this.codeId.contracts[1].address, + type: 'heell', + }) + ).rejects.toBeInstanceOf(Errors.ValidationError); + expect( + this.broker.call('v1.Cw721ReindexingService.reindexing', { + contractAddresses: [this.codeId.contracts[1].address], + type: 'heell', + }) + ).toBeDefined(); + expect( + this.broker.call('v1.Cw721ReindexingService.reindexing', { + contractAddresses: [this.codeId.contracts[1].address], + type: REINDEX_TYPE.ALL, + }) + ).toBeDefined(); + expect( + this.broker.call('v1.Cw721ReindexingService.reindexing', { + contractAddresses: [this.codeId.contracts[1].address], + type: REINDEX_TYPE.HISTORY, + }) + ).toBeDefined(); + } } From 4b36f2e87a99221309b1af4708c378274eb73649 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Wed, 23 Aug 2023 10:52:31 +0700 Subject: [PATCH 21/54] feat: ibc app ics20 --- ci/config.json.ci | 6 ++ config.json | 6 ++ src/common/constant.ts | 5 ++ src/models/ibc_message.ts | 2 + src/services/ibc/crawl_ibc_ics20.service.ts | 79 +++++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 src/services/ibc/crawl_ibc_ics20.service.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index def3d4328..fc161b7ac 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -174,5 +174,11 @@ "key": "crawlIbcApp", "millisecondRepeatJob": 2000, "blocksPerCall": 100 + }, + "crawlIbcIcs20": { + "key": "crawlIbcIcs20", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100, + "port": "transfer" } } diff --git a/config.json b/config.json index 4d8621708..fda0ee3c4 100644 --- a/config.json +++ b/config.json @@ -174,5 +174,11 @@ "key": "crawlIbcApp", "millisecondRepeatJob": 2000, "blocksPerCall": 100 + }, + "crawlIbcIcs20": { + "key": "crawlIbcIcs20", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100, + "port": "transfer" } } diff --git a/src/common/constant.ts b/src/common/constant.ts index 52fa928b0..5629944ce 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -71,6 +71,7 @@ export const BULL_JOB_NAME = { CRAWL_IBC_APP: 'crawl:ibc-app', REINDEX_CW20_CONTRACT: 'reindex:cw20-contract', REINDEX_CW20_HISTORY: 'reindex:cw20-history', + CRAWL_IBC_ICS20: 'crawl:ibc-ics20', }; export const SERVICE = { @@ -244,6 +245,10 @@ export const SERVICE = { path: 'v1.Cw20ReindexingService.reindexing', }, }, + CrawlIBCIcs20Service: { + key: 'CrawlIBCIcs20Service', + name: 'v1.CrawlIBCIcs20Service', + }, }, }; diff --git a/src/models/ibc_message.ts b/src/models/ibc_message.ts index c393c3a2b..6f96ded28 100644 --- a/src/models/ibc_message.ts +++ b/src/models/ibc_message.ts @@ -25,6 +25,8 @@ export class IbcMessage extends BaseModel { data!: any; + message!: TransactionMessage; + static get tableName() { return 'ibc_message'; } diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts new file mode 100644 index 000000000..3ee359c1c --- /dev/null +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -0,0 +1,79 @@ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import { BlockCheckpoint, IbcMessage } from '../../models'; + +const PORT = config.crawlIbcIcs20.port; +@Service({ + name: SERVICE.V1.CrawlIBCIcs20Service.key, + version: 1, +}) +export default class CrawlIBCIcs20Service extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_IBC_ICS20, + jobName: BULL_JOB_NAME.CRAWL_IBC_ICS20, + }) + public async crawlIbcIcs20(): Promise { + const [startHeight, endHeight, updateBlockCheckpoint] = + await BlockCheckpoint.getCheckpoint( + BULL_JOB_NAME.CRAWL_IBC_ICS20, + [BULL_JOB_NAME.CRAWL_IBC_APP], + config.crawlIbcIcs20.key + ); + this.logger.info( + `Handle IBC/ICS20, startHeight: ${startHeight}, endHeight: ${endHeight}` + ); + if (startHeight > endHeight) return; + const ics20Transfers = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder.where('type', 'fungible_token_packet'); + }, + }) + .where('src_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + const ics20Receives = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder.where('type', 'fungible_token_packet'); + }, + }) + .where('src_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + this.logger.info(ics20Transfers, ics20Receives, updateBlockCheckpoint); + } + + async _start(): Promise { + await this.createJob( + BULL_JOB_NAME.CRAWL_IBC_ICS20, + BULL_JOB_NAME.CRAWL_IBC_ICS20, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + every: config.crawlIbcIcs20.millisecondRepeatJob, + }, + } + ); + return super._start(); + } +} From 3c325dc6a85ac38fc7278412948220b37e0f959b Mon Sep 17 00:00:00 2001 From: Tuan Phan Anh <38557844+fibonacci998@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:49:14 +0700 Subject: [PATCH 22/54] Fix/account stat with feegrant (#337) * fix: get use_feegrant_grantee if has, other get tx_fee_payer * fix: remove code not used --- src/services/statistics/account_statistics.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index dd97d64bf..81731b6e9 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -259,8 +259,9 @@ export default class AccountStatisticsService extends BullableService { .map((event) => Object.assign({}, ...event.jsonb_agg)) .map( (event) => - event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER] ?? - event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE] + event[ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE + ] ?? event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER] ) .forEach((address) => { if (!accountStats[address]) { From 4adfdf0d346d64f637f84651ee0d6cfc686e3bb1 Mon Sep 17 00:00:00 2001 From: Tuan Phan Anh <38557844+fibonacci998@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:49:49 +0700 Subject: [PATCH 23/54] Update api_gateway.service.ts (#336) --- src/services/api-gateways/api_gateway.service.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index 1f4183319..8fd7ddb66 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -31,14 +31,8 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; path: '/admin', autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services mappingPolicy: 'restrict', // allow action called with exact method - whitelist: ['v2.statistics.syncPrevDateStatsByChainId'], - }, - { - path: '/admin', - autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services - mappingPolicy: 'restrict', // allow action called with exact method - whitelist: ['v1.cw721-admin.*', 'v1.cw20-admin.*'], - }, + whitelist: ['v2.statistics.syncPrevDateStatsByChainId', 'v1.cw20-admin.*', 'v1.cw721-admin.*'], + } ], // empty cors object will have moleculer to generate handler for preflight request and CORS header which allow all origin cors: {}, From be9a80d0000a1361a7275e77681cf6dd62df85d4 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 24 Aug 2023 10:51:50 +0700 Subject: [PATCH 24/54] feat: ibc ics20 send --- migrations/20230823070516_ics20_model.ts | 18 +++++++ src/models/ibc_ics20.ts | 58 +++++++++++++++++++++ src/models/index.ts | 1 + src/models/transaction_message.ts | 2 + src/services/ibc/crawl_ibc_ics20.service.ts | 48 +++++++++-------- 5 files changed, 104 insertions(+), 23 deletions(-) create mode 100644 migrations/20230823070516_ics20_model.ts create mode 100644 src/models/ibc_ics20.ts diff --git a/migrations/20230823070516_ics20_model.ts b/migrations/20230823070516_ics20_model.ts new file mode 100644 index 000000000..170f24f2a --- /dev/null +++ b/migrations/20230823070516_ics20_model.ts @@ -0,0 +1,18 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('ibc_ics20', (table) => { + table.increments(); + table.string('ibc_message_id').notNullable().unique(); + table.string('sender').index(); + table.string('receiver').index().notNullable(); + table.decimal('amount', 80, 0).notNullable(); + table.string('denom').notNullable().index(); + table.boolean('ack_status'); + table.foreign('ibc_message_id').references('ibc_message.id'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists('ibc_ics20'); +} diff --git a/src/models/ibc_ics20.ts b/src/models/ibc_ics20.ts new file mode 100644 index 000000000..f33eb829b --- /dev/null +++ b/src/models/ibc_ics20.ts @@ -0,0 +1,58 @@ +/* eslint-disable import/no-cycle */ +import { Model } from 'objection'; +import BaseModel from './base'; +import { IbcMessage } from './ibc_message'; + +export class IbcIcs20 extends BaseModel { + id!: number; + + ibc_message_id!: number; + + sender!: string; + + receiver!: string; + + amount!: string; + + denom!: string; + + ack_status!: boolean; + + ibc_message!: IbcMessage; + + static get tableName() { + return 'ibc_ics20'; + } + + static get jsonSchema() { + return { + type: 'object', + required: ['receiver', 'amount', 'denom', 'ibc_message_id'], + properties: { + receiver: { type: 'string' }, + denom: { type: 'string' }, + ibc_message_id: { type: 'number' }, + amount: { type: 'string' }, + }, + }; + } + + static get relationMappings() { + return { + ibc_message: { + relation: Model.BelongsToOneRelation, + modelClass: IbcMessage, + join: { + from: 'ibc_ics20.ibc_message_id', + to: 'ibc_message.id', + }, + }, + }; + } + + static EVENT_TYPE = { + TIMEOUT: 'timeout', + FUNGIBLE_TOKEN_PACKET: 'fungible_token_packet', + DENOM_TRACE: 'denomination_trace', + }; +} diff --git a/src/models/index.ts b/src/models/index.ts index 3056261c0..6acc2105e 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -27,3 +27,4 @@ export * from './ibc_client'; export * from './ibc_connection'; export * from './ibc_channel'; export * from './ibc_message'; +export * from './ibc_ics20'; diff --git a/src/models/transaction_message.ts b/src/models/transaction_message.ts index 4f4bbf680..327f4a0f0 100644 --- a/src/models/transaction_message.ts +++ b/src/models/transaction_message.ts @@ -22,6 +22,8 @@ export class TransactionMessage extends BaseModel { parent_id!: number; + events!: Event[]; + static get tableName() { return 'transaction_message'; } diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 3ee359c1c..7a005493d 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -1,9 +1,11 @@ import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { Knex } from 'knex'; import { ServiceBroker } from 'moleculer'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import { BULL_JOB_NAME, SERVICE } from '../../common'; -import { BlockCheckpoint, IbcMessage } from '../../models'; +import knex from '../../common/utils/db_connection'; +import { BlockCheckpoint, IbcIcs20, IbcMessage } from '../../models'; const PORT = config.crawlIbcIcs20.port; @Service({ @@ -30,33 +32,33 @@ export default class CrawlIBCIcs20Service extends BullableService { `Handle IBC/ICS20, startHeight: ${startHeight}, endHeight: ${endHeight}` ); if (startHeight > endHeight) return; - const ics20Transfers = await IbcMessage.query() - .withGraphFetched('message.events(selectIcs20Event).attributes') - .joinRelated('message.transaction') - .modifiers({ - selectIcs20Event(builder) { - builder.where('type', 'fungible_token_packet'); - }, - }) - .where('src_port_id', PORT) - .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) - .andWhere('message:transaction.height', '>', startHeight) - .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); - const ics20Receives = await IbcMessage.query() - .withGraphFetched('message.events(selectIcs20Event).attributes') + await knex.transaction(async (trx) => { + await this.handleIcs20Send(startHeight, endHeight, trx); + }); + this.logger.info(updateBlockCheckpoint); + } + + async handleIcs20Send( + startHeight: number, + endHeight: number, + trx: Knex.Transaction + ) { + const ics20Sends = await IbcMessage.query() .joinRelated('message.transaction') - .modifiers({ - selectIcs20Event(builder) { - builder.where('type', 'fungible_token_packet'); - }, - }) .where('src_port_id', PORT) - .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.SEND_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) .orderBy('message.id'); - this.logger.info(ics20Transfers, ics20Receives, updateBlockCheckpoint); + if (ics20Sends.length > 0) { + const ibcIcs20s = ics20Sends.map((msg) => + IbcIcs20.fromJson({ + ibc_message_id: msg.id, + ...msg.data, + }) + ); + await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + } } async _start(): Promise { From 91e03ce55e910de6c3039456a0147b88c9e5ed84 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 24 Aug 2023 11:04:31 +0700 Subject: [PATCH 25/54] feat: ibc ics20 send --- migrations/20230823070516_ics20_model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/20230823070516_ics20_model.ts b/migrations/20230823070516_ics20_model.ts index 170f24f2a..ed3937b9e 100644 --- a/migrations/20230823070516_ics20_model.ts +++ b/migrations/20230823070516_ics20_model.ts @@ -3,7 +3,7 @@ import { Knex } from 'knex'; export async function up(knex: Knex): Promise { await knex.schema.createTable('ibc_ics20', (table) => { table.increments(); - table.string('ibc_message_id').notNullable().unique(); + table.integer('ibc_message_id').notNullable().unique(); table.string('sender').index(); table.string('receiver').index().notNullable(); table.decimal('amount', 80, 0).notNullable(); From 1d438ea85c210d26c7b6735701623d429b6d9504 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 24 Aug 2023 15:20:46 +0700 Subject: [PATCH 26/54] feat: ibc ics20 recv --- src/common/utils/utils.ts | 11 ++++ src/models/event_attribute.ts | 2 + src/services/ibc/crawl_ibc_ics20.service.ts | 60 ++++++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/common/utils/utils.ts b/src/common/utils/utils.ts index 8563cf71f..da5a16690 100644 --- a/src/common/utils/utils.ts +++ b/src/common/utils/utils.ts @@ -210,3 +210,14 @@ export default class Utils { return [response, heightRange]; } } + +// get Attribute value by specified key from array of attributes +export function getAttributesFrom( + listAttributes: any, + attributesType: string[] +) { + return attributesType.map( + (attributeType) => + listAttributes?.find((attr: any) => attr.key === attributeType)?.value + ); +} diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 58e916f76..e6f777e2f 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -113,6 +113,8 @@ export class EventAttribute extends BaseModel { DST_CHANNEL: 'packet_dst_channel', FEE: 'fee', FEE_PAYER: 'fee_payer', + DENOM: 'denom', + SUCCESS: 'success', }; static ATTRIBUTE_COMPOSITE_KEY = { diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 7a005493d..8928f40dc 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -5,7 +5,13 @@ import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import { BULL_JOB_NAME, SERVICE } from '../../common'; import knex from '../../common/utils/db_connection'; -import { BlockCheckpoint, IbcIcs20, IbcMessage } from '../../models'; +import { + BlockCheckpoint, + EventAttribute, + IbcIcs20, + IbcMessage, +} from '../../models'; +import { getAttributesFrom } from '../../common/utils/utils'; const PORT = config.crawlIbcIcs20.port; @Service({ @@ -34,6 +40,7 @@ export default class CrawlIBCIcs20Service extends BullableService { if (startHeight > endHeight) return; await knex.transaction(async (trx) => { await this.handleIcs20Send(startHeight, endHeight, trx); + await this.handleIcs20Recv(startHeight, endHeight, trx); }); this.logger.info(updateBlockCheckpoint); } @@ -61,6 +68,57 @@ export default class CrawlIBCIcs20Service extends BullableService { } } + async handleIcs20Recv( + startHeight: number, + endHeight: number, + trx: Knex.Transaction + ) { + const ics20Recvs = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder + .where('type', IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET) + .orWhere('type', IbcIcs20.EVENT_TYPE.DENOM_TRACE); + }, + }) + .where('dst_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + if (ics20Recvs.length > 0) { + const ibcIcs20s = ics20Recvs.map((msg) => { + const recvEvent = msg.message.events.find( + (e) => e.type === IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET + ); + if (recvEvent === undefined) { + throw Error(`Recv ibc hasn't emmitted events: ${ msg.id}`); + } + const [sender, receiver, amount, denom, ackStatus] = getAttributesFrom( + recvEvent.attributes, + [ + EventAttribute.ATTRIBUTE_KEY.SENDER, + EventAttribute.ATTRIBUTE_KEY.RECEIVER, + EventAttribute.ATTRIBUTE_KEY.AMOUNT, + EventAttribute.ATTRIBUTE_KEY.DENOM, + EventAttribute.ATTRIBUTE_KEY.SUCCESS, + ] + ); + return IbcIcs20.fromJson({ + ibc_message_id: msg.id, + sender, + receiver, + amount, + denom, + ack_status: ackStatus === 'true', + }); + }); + await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + } + } + async _start(): Promise { await this.createJob( BULL_JOB_NAME.CRAWL_IBC_ICS20, From eab1ff18d9b9c03fd39c6ad5ba0463e5ef983696 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 24 Aug 2023 16:56:09 +0700 Subject: [PATCH 27/54] feat: ibc ics20 recv --- src/services/ibc/crawl_ibc_ics20.service.ts | 30 +++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 8928f40dc..72968c6f5 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -94,17 +94,24 @@ export default class CrawlIBCIcs20Service extends BullableService { (e) => e.type === IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET ); if (recvEvent === undefined) { - throw Error(`Recv ibc hasn't emmitted events: ${ msg.id}`); + throw Error(`Recv ibc hasn't emmitted events: ${msg.id}`); } - const [sender, receiver, amount, denom, ackStatus] = getAttributesFrom( - recvEvent.attributes, - [ + const [sender, receiver, amount, originalDenom, ackStatus] = + getAttributesFrom(recvEvent.attributes, [ EventAttribute.ATTRIBUTE_KEY.SENDER, EventAttribute.ATTRIBUTE_KEY.RECEIVER, EventAttribute.ATTRIBUTE_KEY.AMOUNT, EventAttribute.ATTRIBUTE_KEY.DENOM, EventAttribute.ATTRIBUTE_KEY.SUCCESS, - ] + ]); + const denomTraceEvent = msg.message.events.find( + (e) => e.type === IbcIcs20.EVENT_TYPE.DENOM_TRACE + ); + const denom = this.parseDenom( + originalDenom, + denomTraceEvent === undefined, + msg.dst_port_id, + msg.dst_channel_id ); return IbcIcs20.fromJson({ ibc_message_id: msg.id, @@ -119,6 +126,19 @@ export default class CrawlIBCIcs20Service extends BullableService { } } + parseDenom( + denom: string, + isSource: boolean, + dstPort: string, + dstChannel: string + ) { + if (isSource) { + const tokens2 = denom.split('/').slice(2); + return tokens2.join('/'); + } + return `${dstPort }/${ dstChannel }/${ denom}`; + } + async _start(): Promise { await this.createJob( BULL_JOB_NAME.CRAWL_IBC_ICS20, From 6cd21c380950ca17fa732dd7dbf2578f85bdc77d Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 25 Aug 2023 10:17:42 +0700 Subject: [PATCH 28/54] feat: handle ics20 ack --- src/services/ibc/crawl_ibc_ics20.service.ts | 51 ++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 72968c6f5..927306f82 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -41,6 +41,7 @@ export default class CrawlIBCIcs20Service extends BullableService { await knex.transaction(async (trx) => { await this.handleIcs20Send(startHeight, endHeight, trx); await this.handleIcs20Recv(startHeight, endHeight, trx); + await this.handleIcs20Ack(startHeight, endHeight, trx); }); this.logger.info(updateBlockCheckpoint); } @@ -126,6 +127,54 @@ export default class CrawlIBCIcs20Service extends BullableService { } } + async handleIcs20Ack( + startHeight: number, + endHeight: number, + trx: Knex.Transaction + ) { + const ics20Acks = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder.where('type', IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET); + }, + }) + .where('src_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + if (ics20Acks.length > 0) { + const ibcIcs20s = ics20Acks.map((msg) => { + const ackEvents = msg.message.events; + if (ackEvents.length !== 2) { + throw Error(`Ack ibc hasn't emmitted enough events: ${msg.id}`); + } + const [sender, receiver, amount, denom] = getAttributesFrom( + ackEvents[0].attributes, + [ + EventAttribute.ATTRIBUTE_KEY.SENDER, + EventAttribute.ATTRIBUTE_KEY.RECEIVER, + EventAttribute.ATTRIBUTE_KEY.AMOUNT, + EventAttribute.ATTRIBUTE_KEY.DENOM, + ] + ); + return IbcIcs20.fromJson({ + ibc_message_id: msg.id, + sender, + receiver, + amount, + denom, + ack_status: + ackEvents[1].attributes[0].key === + EventAttribute.ATTRIBUTE_KEY.SUCCESS, + }); + }); + await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + } + } + parseDenom( denom: string, isSource: boolean, @@ -136,7 +185,7 @@ export default class CrawlIBCIcs20Service extends BullableService { const tokens2 = denom.split('/').slice(2); return tokens2.join('/'); } - return `${dstPort }/${ dstChannel }/${ denom}`; + return `${dstPort}/${dstChannel}/${denom}`; } async _start(): Promise { From 3252d7c202c436a84fe0f13975be2e9c90b86bf0 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 25 Aug 2023 10:39:35 +0700 Subject: [PATCH 29/54] feat: handle ics20 timeout --- src/models/event_attribute.ts | 3 ++ src/services/ibc/crawl_ibc_ics20.service.ts | 48 ++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index e6f777e2f..8622b390a 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -115,6 +115,9 @@ export class EventAttribute extends BaseModel { FEE_PAYER: 'fee_payer', DENOM: 'denom', SUCCESS: 'success', + REFUND_RECEIVER: 'refund_receiver', + REFUND_DENOM: 'refund_denom', + REFUND_AMOUNT: 'refund_amount', }; static ATTRIBUTE_COMPOSITE_KEY = { diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 927306f82..d8deefbb8 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -42,8 +42,14 @@ export default class CrawlIBCIcs20Service extends BullableService { await this.handleIcs20Send(startHeight, endHeight, trx); await this.handleIcs20Recv(startHeight, endHeight, trx); await this.handleIcs20Ack(startHeight, endHeight, trx); + await this.handleIcs20Timeout(startHeight, endHeight, trx); + updateBlockCheckpoint.height = endHeight; + await BlockCheckpoint.query() + .transacting(trx) + .insert(updateBlockCheckpoint) + .onConflict('job_name') + .merge(); }); - this.logger.info(updateBlockCheckpoint); } async handleIcs20Send( @@ -175,6 +181,46 @@ export default class CrawlIBCIcs20Service extends BullableService { } } + async handleIcs20Timeout( + startHeight: number, + endHeight: number, + trx: Knex.Transaction + ) { + const ics20Timeouts = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder.where('type', IbcIcs20.EVENT_TYPE.TIMEOUT); + }, + }) + .where('src_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.TIMEOUT_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + if (ics20Timeouts.length > 0) { + const ibcIcs20s = ics20Timeouts.map((msg) => { + const timeoutEvent = msg.message.events[0]; + const [receiver, amount, denom] = getAttributesFrom( + timeoutEvent.attributes, + [ + EventAttribute.ATTRIBUTE_KEY.REFUND_RECEIVER, + EventAttribute.ATTRIBUTE_KEY.REFUND_AMOUNT, + EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM, + ] + ); + return IbcIcs20.fromJson({ + ibc_message_id: msg.id, + receiver, + amount, + denom, + }); + }); + await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + } + } + parseDenom( denom: string, isSource: boolean, From 93c2622b9bb9c45c9624cfeab81863de227e5397 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 25 Aug 2023 16:05:34 +0700 Subject: [PATCH 30/54] test: test --- src/services/ibc/crawl_ibc_ics20.service.ts | 21 +- .../unit/services/ibc/crawl_ibc_ics20.spec.ts | 519 ++++++++++++++++++ 2 files changed, 531 insertions(+), 9 deletions(-) create mode 100644 test/unit/services/ibc/crawl_ibc_ics20.spec.ts diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index d8deefbb8..38ee3eaf1 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -63,7 +63,8 @@ export default class CrawlIBCIcs20Service extends BullableService { .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.SEND_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); + .orderBy('message.id') + .transacting(trx); if (ics20Sends.length > 0) { const ibcIcs20s = ics20Sends.map((msg) => IbcIcs20.fromJson({ @@ -94,7 +95,8 @@ export default class CrawlIBCIcs20Service extends BullableService { .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); + .orderBy('message.id') + .transacting(trx); if (ics20Recvs.length > 0) { const ibcIcs20s = ics20Recvs.map((msg) => { const recvEvent = msg.message.events.find( @@ -150,20 +152,22 @@ export default class CrawlIBCIcs20Service extends BullableService { .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); + .orderBy('message.id') + .transacting(trx); if (ics20Acks.length > 0) { const ibcIcs20s = ics20Acks.map((msg) => { const ackEvents = msg.message.events; if (ackEvents.length !== 2) { throw Error(`Ack ibc hasn't emmitted enough events: ${msg.id}`); } - const [sender, receiver, amount, denom] = getAttributesFrom( - ackEvents[0].attributes, + const [sender, receiver, amount, denom, success] = getAttributesFrom( + [...ackEvents[0].attributes, ...ackEvents[1].attributes], [ EventAttribute.ATTRIBUTE_KEY.SENDER, EventAttribute.ATTRIBUTE_KEY.RECEIVER, EventAttribute.ATTRIBUTE_KEY.AMOUNT, EventAttribute.ATTRIBUTE_KEY.DENOM, + EventAttribute.ATTRIBUTE_KEY.SUCCESS, ] ); return IbcIcs20.fromJson({ @@ -172,9 +176,7 @@ export default class CrawlIBCIcs20Service extends BullableService { receiver, amount, denom, - ack_status: - ackEvents[1].attributes[0].key === - EventAttribute.ATTRIBUTE_KEY.SUCCESS, + ack_status: success !== undefined, }); }); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -198,7 +200,8 @@ export default class CrawlIBCIcs20Service extends BullableService { .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.TIMEOUT_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); + .orderBy('message.id') + .transacting(trx); if (ics20Timeouts.length > 0) { const ibcIcs20s = ics20Timeouts.map((msg) => { const timeoutEvent = msg.message.events[0]; diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts new file mode 100644 index 000000000..ff4c0d4bf --- /dev/null +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -0,0 +1,519 @@ +import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { ServiceBroker } from 'moleculer'; +import knex from '../../../../src/common/utils/db_connection'; +import { + Block, + Event, + EventAttribute, + IbcIcs20, + IbcMessage, + Transaction, +} from '../../../../src/models'; +import CrawlIbcIcs20 from '../../../../src/services/ibc/crawl_ibc_ics20.service'; +import config from '../../../../config.json' assert { type: 'json' }; +import { getAttributeFrom } from '../../../../src/common/utils/smart_contract'; + +const PORT = config.crawlIbcIcs20.port; +@Describe('Test crawl ibc-ics20 service') +export default class CrawlIbcIcs20Test { + broker = new ServiceBroker({ logger: false }); + + crawlIbcIcs20Serivce = this.broker.createService( + CrawlIbcIcs20 + ) as CrawlIbcIcs20; + + block: Block = Block.fromJson({ + height: 30000, + hash: '4801997745BDD354C8F11CE4A4137237194099E664CD8F83A5FBA9041C43FE9F', + time: '2023-01-12T01:53:57.216Z', + proposer_address: 'auraomd;cvpio3j4eg', + data: {}, + }); + + transaction = { + ...Transaction.fromJson({ + height: this.block.height, + hash: '4A8B0DE950F563553A81360D4782F6EC451F6BEF7AC50E2459D1997FA168997D', + codespace: '', + code: 0, + gas_used: '123035', + gas_wanted: '141106', + gas_limit: '141106', + fee: 353, + timestamp: '2023-01-12T01:53:57.000Z', + index: 0, + data: { + tx_response: { + logs: [], + }, + }, + }), + messages: [ + { + index: 1, + type: '/cosmwasm.wasm.v1.MsgExecuteContract', + sender: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + content: { + msg: '{"add_mint_phase":{"phase_data":{"start_time":"1679976124941000000","end_time":"1679982024941000000","max_supply":2000,"max_nfts_per_address":20,"price":{"amount":"10","denom":"ueaura"},"is_public":false},"token_id": "test"}}', + '@type': '/cosmwasm.wasm.v1.MsgExecuteContract', + funds: [], + sender: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + }, + }, + ], + }; + + @BeforeAll() + async initSuite() { + this.crawlIbcIcs20Serivce.getQueueManager().stopAll(); + await knex.raw( + 'TRUNCATE TABLE block, transaction, ibc_message RESTART IDENTITY CASCADE' + ); + await Block.query().insert(this.block); + await Transaction.query().insertGraph(this.transaction); + } + + @AfterAll() + async tearDown() { + await this.broker.stop(); + } + + @Test('Test handleIcs20Send') + async testHandleIcs20Send() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: PORT, + dst_channel_id: 'cccc', + dst_port_id: 'dddd', + type: IbcMessage.EVENT_TYPE.SEND_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Send( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.sender).toEqual(ibcMessage.data.sender); + expect(result?.receiver).toEqual(ibcMessage.data.receiver); + expect(result?.amount).toEqual(ibcMessage.data.amount); + expect(result?.denom).toEqual(ibcMessage.data.denom); + expect(result?.ack_status).toBeNull(); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20Recv from source chain') + async testHandleIcs20RecvFromSource() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: 'bbbb', + dst_channel_id: 'cccc', + dst_port_id: PORT, + type: IbcMessage.EVENT_TYPE.RECV_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'sender', + value: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + { + key: 'receiver', + value: 'stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl', + }, + { + key: 'denom', + value: 'uatom', + }, + { + key: 'amount', + value: '10000', + }, + { + key: 'memo', + value: '', + }, + { + key: 'success', + value: 'true', + }, + ]; + const event2Attrs = [ + { + key: 'trace_hash', + value: + '40CA5EF447F368B7F2276A689383BE3C427B15395D4BF6639B605D36C0846A20', + }, + { + key: 'denom', + value: + 'ibc/40CA5EF447F368B7F2276A689383BE3C427B15395D4BF6639B605D36C0846A20', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.DENOM_TRACE, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event2Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Recv( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.receiver).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) + ); + expect(result?.sender).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.SENDER) + ); + expect(result?.amount).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) + ); + expect(result?.denom).toEqual( + `${ibcMessage.dst_port_id + }/${ + ibcMessage.dst_channel_id + }/${ + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` + ); + expect(result?.ack_status).toEqual(true); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20Recv from sink chain') + async testHandleIcs20RecvFromSink() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: 'bbbb', + dst_channel_id: 'cccc', + dst_port_id: PORT, + type: IbcMessage.EVENT_TYPE.RECV_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'sender', + value: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + { + key: 'receiver', + value: 'stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl', + }, + { + key: 'denom', + value: 'hhhh/jjjjj/uatom', + }, + { + key: 'amount', + value: '10000', + }, + { + key: 'memo', + value: '', + }, + { + key: 'success', + value: 'true', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Recv( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.receiver).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) + ); + expect(result?.sender).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.SENDER) + ); + expect(result?.amount).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) + ); + expect(result?.denom).toEqual('uatom'); + expect(result?.ack_status).toEqual(true); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20Ack') + async testHandleIcs20Ack() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: PORT, + dst_channel_id: 'cccc', + dst_port_id: 'dddd', + type: IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'sender', + value: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + { + key: 'receiver', + value: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + }, + { + key: 'denom', + value: 'uatom', + }, + { + key: 'amount', + value: '10000', + }, + { + key: 'memo', + value: '', + }, + { + key: 'acknowledgement', + value: 'result:"\\001" ', + }, + ]; + const event2Attrs = [ + { + key: 'error', + value: '\u0001', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event2Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Ack( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.receiver).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) + ); + expect(result?.sender).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.SENDER) + ); + expect(result?.amount).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) + ); + expect(result?.denom).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM) + ); + expect(result?.ack_status).toEqual(false); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20Timeout') + async testHandleIcs20Timeout() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: PORT, + dst_channel_id: 'cccc', + dst_port_id: 'dddd', + type: IbcMessage.EVENT_TYPE.TIMEOUT_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'refund_receiver', + value: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + }, + { + key: 'refund_denom', + value: 'utaura', + }, + { + key: 'refund_amount', + value: '1000000', + }, + { + key: 'memo', + value: '', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.TIMEOUT, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Timeout( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.receiver).toEqual( + getAttributeFrom( + event1Attrs, + EventAttribute.ATTRIBUTE_KEY.REFUND_RECEIVER + ) + ); + expect(result?.sender).toBeNull(); + expect(result?.amount).toEqual( + getAttributeFrom( + event1Attrs, + EventAttribute.ATTRIBUTE_KEY.REFUND_AMOUNT + ) + ); + expect(result?.denom).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM) + ); + expect(result?.ack_status).toBeNull(); + await trx.rollback(); + }); + } +} From a439d90df70473c3b6a2040f64c40ac5c02d2616 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Mon, 28 Aug 2023 09:31:57 +0700 Subject: [PATCH 31/54] fix: code --- test/unit/services/ibc/crawl_ibc_ics20.spec.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index ff4c0d4bf..08ce519fe 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -222,11 +222,9 @@ export default class CrawlIbcIcs20Test { getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) ); expect(result?.denom).toEqual( - `${ibcMessage.dst_port_id - }/${ - ibcMessage.dst_channel_id - }/${ - getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` + `${ibcMessage.dst_port_id}/${ + ibcMessage.dst_channel_id + }/${getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` ); expect(result?.ack_status).toEqual(true); await trx.rollback(); From d7b5ea2b8815963744cc9173a26f5e9db4530956 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Mon, 28 Aug 2023 09:35:04 +0700 Subject: [PATCH 32/54] fix: code --- .../unit/services/ibc/crawl_ibc_ics20.spec.ts | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index 08ce519fe..853fd7f3e 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -185,9 +185,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event1Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -198,9 +200,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event2Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -290,9 +294,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event1Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -385,9 +391,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event2Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -398,9 +406,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event1Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -480,9 +490,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event1Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), From 1cab04ae6831d9b7d056f858674313782d5585ed Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Wed, 23 Aug 2023 10:52:31 +0700 Subject: [PATCH 33/54] feat: ibc app ics20 --- ci/config.json.ci | 6 ++ config.json | 6 ++ src/common/constant.ts | 5 ++ src/models/ibc_message.ts | 2 + src/services/ibc/crawl_ibc_ics20.service.ts | 79 +++++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 src/services/ibc/crawl_ibc_ics20.service.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index a2dd5d87c..492795279 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -184,5 +184,11 @@ "key": "crawlIbcApp", "millisecondRepeatJob": 2000, "blocksPerCall": 100 + }, + "crawlIbcIcs20": { + "key": "crawlIbcIcs20", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100, + "port": "transfer" } } diff --git a/config.json b/config.json index 731b618f4..c222e6d0d 100644 --- a/config.json +++ b/config.json @@ -184,5 +184,11 @@ "key": "crawlIbcApp", "millisecondRepeatJob": 2000, "blocksPerCall": 100 + }, + "crawlIbcIcs20": { + "key": "crawlIbcIcs20", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100, + "port": "transfer" } } diff --git a/src/common/constant.ts b/src/common/constant.ts index efd0c19be..c81642dcf 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -76,6 +76,7 @@ export const BULL_JOB_NAME = { REINDEX_CW20_CONTRACT: 'reindex:cw20-contract', REINDEX_CW20_HISTORY: 'reindex:cw20-history', CRAWL_IBC_APP: 'crawl:ibc-app', + CRAWL_IBC_ICS20: 'crawl:ibc-ics20', }; export const SERVICE = { @@ -269,6 +270,10 @@ export const SERVICE = { key: 'CrawlIBCAppService', name: 'v1.CrawlIBCAppService', }, + CrawlIBCIcs20Service: { + key: 'CrawlIBCIcs20Service', + name: 'v1.CrawlIBCIcs20Service', + }, }, }; diff --git a/src/models/ibc_message.ts b/src/models/ibc_message.ts index c393c3a2b..6f96ded28 100644 --- a/src/models/ibc_message.ts +++ b/src/models/ibc_message.ts @@ -25,6 +25,8 @@ export class IbcMessage extends BaseModel { data!: any; + message!: TransactionMessage; + static get tableName() { return 'ibc_message'; } diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts new file mode 100644 index 000000000..3ee359c1c --- /dev/null +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -0,0 +1,79 @@ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import { BlockCheckpoint, IbcMessage } from '../../models'; + +const PORT = config.crawlIbcIcs20.port; +@Service({ + name: SERVICE.V1.CrawlIBCIcs20Service.key, + version: 1, +}) +export default class CrawlIBCIcs20Service extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_IBC_ICS20, + jobName: BULL_JOB_NAME.CRAWL_IBC_ICS20, + }) + public async crawlIbcIcs20(): Promise { + const [startHeight, endHeight, updateBlockCheckpoint] = + await BlockCheckpoint.getCheckpoint( + BULL_JOB_NAME.CRAWL_IBC_ICS20, + [BULL_JOB_NAME.CRAWL_IBC_APP], + config.crawlIbcIcs20.key + ); + this.logger.info( + `Handle IBC/ICS20, startHeight: ${startHeight}, endHeight: ${endHeight}` + ); + if (startHeight > endHeight) return; + const ics20Transfers = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder.where('type', 'fungible_token_packet'); + }, + }) + .where('src_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + const ics20Receives = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder.where('type', 'fungible_token_packet'); + }, + }) + .where('src_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + this.logger.info(ics20Transfers, ics20Receives, updateBlockCheckpoint); + } + + async _start(): Promise { + await this.createJob( + BULL_JOB_NAME.CRAWL_IBC_ICS20, + BULL_JOB_NAME.CRAWL_IBC_ICS20, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + every: config.crawlIbcIcs20.millisecondRepeatJob, + }, + } + ); + return super._start(); + } +} From d6277cc7f5d6fa9797600f54470443946ed4679f Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 24 Aug 2023 10:51:50 +0700 Subject: [PATCH 34/54] feat: ibc ics20 send --- migrations/20230823070516_ics20_model.ts | 18 +++++++ src/models/ibc_ics20.ts | 58 +++++++++++++++++++++ src/models/index.ts | 1 + src/models/transaction_message.ts | 2 + src/services/ibc/crawl_ibc_ics20.service.ts | 48 +++++++++-------- 5 files changed, 104 insertions(+), 23 deletions(-) create mode 100644 migrations/20230823070516_ics20_model.ts create mode 100644 src/models/ibc_ics20.ts diff --git a/migrations/20230823070516_ics20_model.ts b/migrations/20230823070516_ics20_model.ts new file mode 100644 index 000000000..170f24f2a --- /dev/null +++ b/migrations/20230823070516_ics20_model.ts @@ -0,0 +1,18 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('ibc_ics20', (table) => { + table.increments(); + table.string('ibc_message_id').notNullable().unique(); + table.string('sender').index(); + table.string('receiver').index().notNullable(); + table.decimal('amount', 80, 0).notNullable(); + table.string('denom').notNullable().index(); + table.boolean('ack_status'); + table.foreign('ibc_message_id').references('ibc_message.id'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists('ibc_ics20'); +} diff --git a/src/models/ibc_ics20.ts b/src/models/ibc_ics20.ts new file mode 100644 index 000000000..f33eb829b --- /dev/null +++ b/src/models/ibc_ics20.ts @@ -0,0 +1,58 @@ +/* eslint-disable import/no-cycle */ +import { Model } from 'objection'; +import BaseModel from './base'; +import { IbcMessage } from './ibc_message'; + +export class IbcIcs20 extends BaseModel { + id!: number; + + ibc_message_id!: number; + + sender!: string; + + receiver!: string; + + amount!: string; + + denom!: string; + + ack_status!: boolean; + + ibc_message!: IbcMessage; + + static get tableName() { + return 'ibc_ics20'; + } + + static get jsonSchema() { + return { + type: 'object', + required: ['receiver', 'amount', 'denom', 'ibc_message_id'], + properties: { + receiver: { type: 'string' }, + denom: { type: 'string' }, + ibc_message_id: { type: 'number' }, + amount: { type: 'string' }, + }, + }; + } + + static get relationMappings() { + return { + ibc_message: { + relation: Model.BelongsToOneRelation, + modelClass: IbcMessage, + join: { + from: 'ibc_ics20.ibc_message_id', + to: 'ibc_message.id', + }, + }, + }; + } + + static EVENT_TYPE = { + TIMEOUT: 'timeout', + FUNGIBLE_TOKEN_PACKET: 'fungible_token_packet', + DENOM_TRACE: 'denomination_trace', + }; +} diff --git a/src/models/index.ts b/src/models/index.ts index bddfaad36..e0d8bd489 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -29,3 +29,4 @@ export * from './ibc_client'; export * from './ibc_connection'; export * from './ibc_channel'; export * from './ibc_message'; +export * from './ibc_ics20'; diff --git a/src/models/transaction_message.ts b/src/models/transaction_message.ts index 4f4bbf680..327f4a0f0 100644 --- a/src/models/transaction_message.ts +++ b/src/models/transaction_message.ts @@ -22,6 +22,8 @@ export class TransactionMessage extends BaseModel { parent_id!: number; + events!: Event[]; + static get tableName() { return 'transaction_message'; } diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 3ee359c1c..7a005493d 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -1,9 +1,11 @@ import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { Knex } from 'knex'; import { ServiceBroker } from 'moleculer'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import { BULL_JOB_NAME, SERVICE } from '../../common'; -import { BlockCheckpoint, IbcMessage } from '../../models'; +import knex from '../../common/utils/db_connection'; +import { BlockCheckpoint, IbcIcs20, IbcMessage } from '../../models'; const PORT = config.crawlIbcIcs20.port; @Service({ @@ -30,33 +32,33 @@ export default class CrawlIBCIcs20Service extends BullableService { `Handle IBC/ICS20, startHeight: ${startHeight}, endHeight: ${endHeight}` ); if (startHeight > endHeight) return; - const ics20Transfers = await IbcMessage.query() - .withGraphFetched('message.events(selectIcs20Event).attributes') - .joinRelated('message.transaction') - .modifiers({ - selectIcs20Event(builder) { - builder.where('type', 'fungible_token_packet'); - }, - }) - .where('src_port_id', PORT) - .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) - .andWhere('message:transaction.height', '>', startHeight) - .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); - const ics20Receives = await IbcMessage.query() - .withGraphFetched('message.events(selectIcs20Event).attributes') + await knex.transaction(async (trx) => { + await this.handleIcs20Send(startHeight, endHeight, trx); + }); + this.logger.info(updateBlockCheckpoint); + } + + async handleIcs20Send( + startHeight: number, + endHeight: number, + trx: Knex.Transaction + ) { + const ics20Sends = await IbcMessage.query() .joinRelated('message.transaction') - .modifiers({ - selectIcs20Event(builder) { - builder.where('type', 'fungible_token_packet'); - }, - }) .where('src_port_id', PORT) - .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.SEND_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) .orderBy('message.id'); - this.logger.info(ics20Transfers, ics20Receives, updateBlockCheckpoint); + if (ics20Sends.length > 0) { + const ibcIcs20s = ics20Sends.map((msg) => + IbcIcs20.fromJson({ + ibc_message_id: msg.id, + ...msg.data, + }) + ); + await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + } } async _start(): Promise { From 3e5301b98b37bb04ad85d2e2c823c6f548fcfa72 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 24 Aug 2023 11:04:31 +0700 Subject: [PATCH 35/54] feat: ibc ics20 send --- migrations/20230823070516_ics20_model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/20230823070516_ics20_model.ts b/migrations/20230823070516_ics20_model.ts index 170f24f2a..ed3937b9e 100644 --- a/migrations/20230823070516_ics20_model.ts +++ b/migrations/20230823070516_ics20_model.ts @@ -3,7 +3,7 @@ import { Knex } from 'knex'; export async function up(knex: Knex): Promise { await knex.schema.createTable('ibc_ics20', (table) => { table.increments(); - table.string('ibc_message_id').notNullable().unique(); + table.integer('ibc_message_id').notNullable().unique(); table.string('sender').index(); table.string('receiver').index().notNullable(); table.decimal('amount', 80, 0).notNullable(); From e13d5727a2b7bdb4574b44cd36228b341d4845c9 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 24 Aug 2023 15:20:46 +0700 Subject: [PATCH 36/54] feat: ibc ics20 recv --- src/common/utils/utils.ts | 11 ++++ src/models/event_attribute.ts | 2 + src/services/ibc/crawl_ibc_ics20.service.ts | 60 ++++++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/common/utils/utils.ts b/src/common/utils/utils.ts index 8563cf71f..da5a16690 100644 --- a/src/common/utils/utils.ts +++ b/src/common/utils/utils.ts @@ -210,3 +210,14 @@ export default class Utils { return [response, heightRange]; } } + +// get Attribute value by specified key from array of attributes +export function getAttributesFrom( + listAttributes: any, + attributesType: string[] +) { + return attributesType.map( + (attributeType) => + listAttributes?.find((attr: any) => attr.key === attributeType)?.value + ); +} diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index f67aad3b2..2d98fc2b0 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -114,6 +114,8 @@ export class EventAttribute extends BaseModel { SRC_CHANNEL: 'packet_src_channel', DST_PORT: 'packet_dst_port', DST_CHANNEL: 'packet_dst_channel', + DENOM: 'denom', + SUCCESS: 'success', }; static ATTRIBUTE_COMPOSITE_KEY = { diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 7a005493d..8928f40dc 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -5,7 +5,13 @@ import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import { BULL_JOB_NAME, SERVICE } from '../../common'; import knex from '../../common/utils/db_connection'; -import { BlockCheckpoint, IbcIcs20, IbcMessage } from '../../models'; +import { + BlockCheckpoint, + EventAttribute, + IbcIcs20, + IbcMessage, +} from '../../models'; +import { getAttributesFrom } from '../../common/utils/utils'; const PORT = config.crawlIbcIcs20.port; @Service({ @@ -34,6 +40,7 @@ export default class CrawlIBCIcs20Service extends BullableService { if (startHeight > endHeight) return; await knex.transaction(async (trx) => { await this.handleIcs20Send(startHeight, endHeight, trx); + await this.handleIcs20Recv(startHeight, endHeight, trx); }); this.logger.info(updateBlockCheckpoint); } @@ -61,6 +68,57 @@ export default class CrawlIBCIcs20Service extends BullableService { } } + async handleIcs20Recv( + startHeight: number, + endHeight: number, + trx: Knex.Transaction + ) { + const ics20Recvs = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder + .where('type', IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET) + .orWhere('type', IbcIcs20.EVENT_TYPE.DENOM_TRACE); + }, + }) + .where('dst_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + if (ics20Recvs.length > 0) { + const ibcIcs20s = ics20Recvs.map((msg) => { + const recvEvent = msg.message.events.find( + (e) => e.type === IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET + ); + if (recvEvent === undefined) { + throw Error(`Recv ibc hasn't emmitted events: ${ msg.id}`); + } + const [sender, receiver, amount, denom, ackStatus] = getAttributesFrom( + recvEvent.attributes, + [ + EventAttribute.ATTRIBUTE_KEY.SENDER, + EventAttribute.ATTRIBUTE_KEY.RECEIVER, + EventAttribute.ATTRIBUTE_KEY.AMOUNT, + EventAttribute.ATTRIBUTE_KEY.DENOM, + EventAttribute.ATTRIBUTE_KEY.SUCCESS, + ] + ); + return IbcIcs20.fromJson({ + ibc_message_id: msg.id, + sender, + receiver, + amount, + denom, + ack_status: ackStatus === 'true', + }); + }); + await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + } + } + async _start(): Promise { await this.createJob( BULL_JOB_NAME.CRAWL_IBC_ICS20, From e560026ebb6adc0e8e8e7932ff338d3a9ce03294 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 24 Aug 2023 16:56:09 +0700 Subject: [PATCH 37/54] feat: ibc ics20 recv --- src/services/ibc/crawl_ibc_ics20.service.ts | 30 +++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 8928f40dc..72968c6f5 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -94,17 +94,24 @@ export default class CrawlIBCIcs20Service extends BullableService { (e) => e.type === IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET ); if (recvEvent === undefined) { - throw Error(`Recv ibc hasn't emmitted events: ${ msg.id}`); + throw Error(`Recv ibc hasn't emmitted events: ${msg.id}`); } - const [sender, receiver, amount, denom, ackStatus] = getAttributesFrom( - recvEvent.attributes, - [ + const [sender, receiver, amount, originalDenom, ackStatus] = + getAttributesFrom(recvEvent.attributes, [ EventAttribute.ATTRIBUTE_KEY.SENDER, EventAttribute.ATTRIBUTE_KEY.RECEIVER, EventAttribute.ATTRIBUTE_KEY.AMOUNT, EventAttribute.ATTRIBUTE_KEY.DENOM, EventAttribute.ATTRIBUTE_KEY.SUCCESS, - ] + ]); + const denomTraceEvent = msg.message.events.find( + (e) => e.type === IbcIcs20.EVENT_TYPE.DENOM_TRACE + ); + const denom = this.parseDenom( + originalDenom, + denomTraceEvent === undefined, + msg.dst_port_id, + msg.dst_channel_id ); return IbcIcs20.fromJson({ ibc_message_id: msg.id, @@ -119,6 +126,19 @@ export default class CrawlIBCIcs20Service extends BullableService { } } + parseDenom( + denom: string, + isSource: boolean, + dstPort: string, + dstChannel: string + ) { + if (isSource) { + const tokens2 = denom.split('/').slice(2); + return tokens2.join('/'); + } + return `${dstPort }/${ dstChannel }/${ denom}`; + } + async _start(): Promise { await this.createJob( BULL_JOB_NAME.CRAWL_IBC_ICS20, From 44363b890f10f7f2bf9b84db8b59bbfeb661dfbb Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 25 Aug 2023 10:17:42 +0700 Subject: [PATCH 38/54] feat: handle ics20 ack --- src/services/ibc/crawl_ibc_ics20.service.ts | 51 ++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 72968c6f5..927306f82 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -41,6 +41,7 @@ export default class CrawlIBCIcs20Service extends BullableService { await knex.transaction(async (trx) => { await this.handleIcs20Send(startHeight, endHeight, trx); await this.handleIcs20Recv(startHeight, endHeight, trx); + await this.handleIcs20Ack(startHeight, endHeight, trx); }); this.logger.info(updateBlockCheckpoint); } @@ -126,6 +127,54 @@ export default class CrawlIBCIcs20Service extends BullableService { } } + async handleIcs20Ack( + startHeight: number, + endHeight: number, + trx: Knex.Transaction + ) { + const ics20Acks = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder.where('type', IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET); + }, + }) + .where('src_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + if (ics20Acks.length > 0) { + const ibcIcs20s = ics20Acks.map((msg) => { + const ackEvents = msg.message.events; + if (ackEvents.length !== 2) { + throw Error(`Ack ibc hasn't emmitted enough events: ${msg.id}`); + } + const [sender, receiver, amount, denom] = getAttributesFrom( + ackEvents[0].attributes, + [ + EventAttribute.ATTRIBUTE_KEY.SENDER, + EventAttribute.ATTRIBUTE_KEY.RECEIVER, + EventAttribute.ATTRIBUTE_KEY.AMOUNT, + EventAttribute.ATTRIBUTE_KEY.DENOM, + ] + ); + return IbcIcs20.fromJson({ + ibc_message_id: msg.id, + sender, + receiver, + amount, + denom, + ack_status: + ackEvents[1].attributes[0].key === + EventAttribute.ATTRIBUTE_KEY.SUCCESS, + }); + }); + await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + } + } + parseDenom( denom: string, isSource: boolean, @@ -136,7 +185,7 @@ export default class CrawlIBCIcs20Service extends BullableService { const tokens2 = denom.split('/').slice(2); return tokens2.join('/'); } - return `${dstPort }/${ dstChannel }/${ denom}`; + return `${dstPort}/${dstChannel}/${denom}`; } async _start(): Promise { From 0cd9a163e8fb8af2b32cd5bad15c1029c95d0334 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 25 Aug 2023 10:39:35 +0700 Subject: [PATCH 39/54] feat: handle ics20 timeout --- src/models/event_attribute.ts | 3 ++ src/services/ibc/crawl_ibc_ics20.service.ts | 48 ++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 2d98fc2b0..de500147c 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -116,6 +116,9 @@ export class EventAttribute extends BaseModel { DST_CHANNEL: 'packet_dst_channel', DENOM: 'denom', SUCCESS: 'success', + REFUND_RECEIVER: 'refund_receiver', + REFUND_DENOM: 'refund_denom', + REFUND_AMOUNT: 'refund_amount', }; static ATTRIBUTE_COMPOSITE_KEY = { diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 927306f82..d8deefbb8 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -42,8 +42,14 @@ export default class CrawlIBCIcs20Service extends BullableService { await this.handleIcs20Send(startHeight, endHeight, trx); await this.handleIcs20Recv(startHeight, endHeight, trx); await this.handleIcs20Ack(startHeight, endHeight, trx); + await this.handleIcs20Timeout(startHeight, endHeight, trx); + updateBlockCheckpoint.height = endHeight; + await BlockCheckpoint.query() + .transacting(trx) + .insert(updateBlockCheckpoint) + .onConflict('job_name') + .merge(); }); - this.logger.info(updateBlockCheckpoint); } async handleIcs20Send( @@ -175,6 +181,46 @@ export default class CrawlIBCIcs20Service extends BullableService { } } + async handleIcs20Timeout( + startHeight: number, + endHeight: number, + trx: Knex.Transaction + ) { + const ics20Timeouts = await IbcMessage.query() + .withGraphFetched('message.events(selectIcs20Event).attributes') + .joinRelated('message.transaction') + .modifiers({ + selectIcs20Event(builder) { + builder.where('type', IbcIcs20.EVENT_TYPE.TIMEOUT); + }, + }) + .where('src_port_id', PORT) + .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.TIMEOUT_PACKET) + .andWhere('message:transaction.height', '>', startHeight) + .andWhere('message:transaction.height', '<=', endHeight) + .orderBy('message.id'); + if (ics20Timeouts.length > 0) { + const ibcIcs20s = ics20Timeouts.map((msg) => { + const timeoutEvent = msg.message.events[0]; + const [receiver, amount, denom] = getAttributesFrom( + timeoutEvent.attributes, + [ + EventAttribute.ATTRIBUTE_KEY.REFUND_RECEIVER, + EventAttribute.ATTRIBUTE_KEY.REFUND_AMOUNT, + EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM, + ] + ); + return IbcIcs20.fromJson({ + ibc_message_id: msg.id, + receiver, + amount, + denom, + }); + }); + await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + } + } + parseDenom( denom: string, isSource: boolean, From c27556dd86fa221a477c59bdc68ccc4cbd49cf42 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 25 Aug 2023 16:05:34 +0700 Subject: [PATCH 40/54] test: test --- src/services/ibc/crawl_ibc_ics20.service.ts | 21 +- .../unit/services/ibc/crawl_ibc_ics20.spec.ts | 519 ++++++++++++++++++ 2 files changed, 531 insertions(+), 9 deletions(-) create mode 100644 test/unit/services/ibc/crawl_ibc_ics20.spec.ts diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index d8deefbb8..38ee3eaf1 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -63,7 +63,8 @@ export default class CrawlIBCIcs20Service extends BullableService { .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.SEND_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); + .orderBy('message.id') + .transacting(trx); if (ics20Sends.length > 0) { const ibcIcs20s = ics20Sends.map((msg) => IbcIcs20.fromJson({ @@ -94,7 +95,8 @@ export default class CrawlIBCIcs20Service extends BullableService { .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); + .orderBy('message.id') + .transacting(trx); if (ics20Recvs.length > 0) { const ibcIcs20s = ics20Recvs.map((msg) => { const recvEvent = msg.message.events.find( @@ -150,20 +152,22 @@ export default class CrawlIBCIcs20Service extends BullableService { .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); + .orderBy('message.id') + .transacting(trx); if (ics20Acks.length > 0) { const ibcIcs20s = ics20Acks.map((msg) => { const ackEvents = msg.message.events; if (ackEvents.length !== 2) { throw Error(`Ack ibc hasn't emmitted enough events: ${msg.id}`); } - const [sender, receiver, amount, denom] = getAttributesFrom( - ackEvents[0].attributes, + const [sender, receiver, amount, denom, success] = getAttributesFrom( + [...ackEvents[0].attributes, ...ackEvents[1].attributes], [ EventAttribute.ATTRIBUTE_KEY.SENDER, EventAttribute.ATTRIBUTE_KEY.RECEIVER, EventAttribute.ATTRIBUTE_KEY.AMOUNT, EventAttribute.ATTRIBUTE_KEY.DENOM, + EventAttribute.ATTRIBUTE_KEY.SUCCESS, ] ); return IbcIcs20.fromJson({ @@ -172,9 +176,7 @@ export default class CrawlIBCIcs20Service extends BullableService { receiver, amount, denom, - ack_status: - ackEvents[1].attributes[0].key === - EventAttribute.ATTRIBUTE_KEY.SUCCESS, + ack_status: success !== undefined, }); }); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -198,7 +200,8 @@ export default class CrawlIBCIcs20Service extends BullableService { .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.TIMEOUT_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) - .orderBy('message.id'); + .orderBy('message.id') + .transacting(trx); if (ics20Timeouts.length > 0) { const ibcIcs20s = ics20Timeouts.map((msg) => { const timeoutEvent = msg.message.events[0]; diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts new file mode 100644 index 000000000..ff4c0d4bf --- /dev/null +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -0,0 +1,519 @@ +import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { ServiceBroker } from 'moleculer'; +import knex from '../../../../src/common/utils/db_connection'; +import { + Block, + Event, + EventAttribute, + IbcIcs20, + IbcMessage, + Transaction, +} from '../../../../src/models'; +import CrawlIbcIcs20 from '../../../../src/services/ibc/crawl_ibc_ics20.service'; +import config from '../../../../config.json' assert { type: 'json' }; +import { getAttributeFrom } from '../../../../src/common/utils/smart_contract'; + +const PORT = config.crawlIbcIcs20.port; +@Describe('Test crawl ibc-ics20 service') +export default class CrawlIbcIcs20Test { + broker = new ServiceBroker({ logger: false }); + + crawlIbcIcs20Serivce = this.broker.createService( + CrawlIbcIcs20 + ) as CrawlIbcIcs20; + + block: Block = Block.fromJson({ + height: 30000, + hash: '4801997745BDD354C8F11CE4A4137237194099E664CD8F83A5FBA9041C43FE9F', + time: '2023-01-12T01:53:57.216Z', + proposer_address: 'auraomd;cvpio3j4eg', + data: {}, + }); + + transaction = { + ...Transaction.fromJson({ + height: this.block.height, + hash: '4A8B0DE950F563553A81360D4782F6EC451F6BEF7AC50E2459D1997FA168997D', + codespace: '', + code: 0, + gas_used: '123035', + gas_wanted: '141106', + gas_limit: '141106', + fee: 353, + timestamp: '2023-01-12T01:53:57.000Z', + index: 0, + data: { + tx_response: { + logs: [], + }, + }, + }), + messages: [ + { + index: 1, + type: '/cosmwasm.wasm.v1.MsgExecuteContract', + sender: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + content: { + msg: '{"add_mint_phase":{"phase_data":{"start_time":"1679976124941000000","end_time":"1679982024941000000","max_supply":2000,"max_nfts_per_address":20,"price":{"amount":"10","denom":"ueaura"},"is_public":false},"token_id": "test"}}', + '@type': '/cosmwasm.wasm.v1.MsgExecuteContract', + funds: [], + sender: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + }, + }, + ], + }; + + @BeforeAll() + async initSuite() { + this.crawlIbcIcs20Serivce.getQueueManager().stopAll(); + await knex.raw( + 'TRUNCATE TABLE block, transaction, ibc_message RESTART IDENTITY CASCADE' + ); + await Block.query().insert(this.block); + await Transaction.query().insertGraph(this.transaction); + } + + @AfterAll() + async tearDown() { + await this.broker.stop(); + } + + @Test('Test handleIcs20Send') + async testHandleIcs20Send() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: PORT, + dst_channel_id: 'cccc', + dst_port_id: 'dddd', + type: IbcMessage.EVENT_TYPE.SEND_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Send( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.sender).toEqual(ibcMessage.data.sender); + expect(result?.receiver).toEqual(ibcMessage.data.receiver); + expect(result?.amount).toEqual(ibcMessage.data.amount); + expect(result?.denom).toEqual(ibcMessage.data.denom); + expect(result?.ack_status).toBeNull(); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20Recv from source chain') + async testHandleIcs20RecvFromSource() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: 'bbbb', + dst_channel_id: 'cccc', + dst_port_id: PORT, + type: IbcMessage.EVENT_TYPE.RECV_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'sender', + value: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + { + key: 'receiver', + value: 'stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl', + }, + { + key: 'denom', + value: 'uatom', + }, + { + key: 'amount', + value: '10000', + }, + { + key: 'memo', + value: '', + }, + { + key: 'success', + value: 'true', + }, + ]; + const event2Attrs = [ + { + key: 'trace_hash', + value: + '40CA5EF447F368B7F2276A689383BE3C427B15395D4BF6639B605D36C0846A20', + }, + { + key: 'denom', + value: + 'ibc/40CA5EF447F368B7F2276A689383BE3C427B15395D4BF6639B605D36C0846A20', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.DENOM_TRACE, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event2Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Recv( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.receiver).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) + ); + expect(result?.sender).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.SENDER) + ); + expect(result?.amount).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) + ); + expect(result?.denom).toEqual( + `${ibcMessage.dst_port_id + }/${ + ibcMessage.dst_channel_id + }/${ + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` + ); + expect(result?.ack_status).toEqual(true); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20Recv from sink chain') + async testHandleIcs20RecvFromSink() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: 'bbbb', + dst_channel_id: 'cccc', + dst_port_id: PORT, + type: IbcMessage.EVENT_TYPE.RECV_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'sender', + value: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + { + key: 'receiver', + value: 'stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl', + }, + { + key: 'denom', + value: 'hhhh/jjjjj/uatom', + }, + { + key: 'amount', + value: '10000', + }, + { + key: 'memo', + value: '', + }, + { + key: 'success', + value: 'true', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Recv( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.receiver).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) + ); + expect(result?.sender).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.SENDER) + ); + expect(result?.amount).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) + ); + expect(result?.denom).toEqual('uatom'); + expect(result?.ack_status).toEqual(true); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20Ack') + async testHandleIcs20Ack() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: PORT, + dst_channel_id: 'cccc', + dst_port_id: 'dddd', + type: IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'sender', + value: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + { + key: 'receiver', + value: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + }, + { + key: 'denom', + value: 'uatom', + }, + { + key: 'amount', + value: '10000', + }, + { + key: 'memo', + value: '', + }, + { + key: 'acknowledgement', + value: 'result:"\\001" ', + }, + ]; + const event2Attrs = [ + { + key: 'error', + value: '\u0001', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event2Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Ack( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.receiver).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) + ); + expect(result?.sender).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.SENDER) + ); + expect(result?.amount).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) + ); + expect(result?.denom).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM) + ); + expect(result?.ack_status).toEqual(false); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20Timeout') + async testHandleIcs20Timeout() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: PORT, + dst_channel_id: 'cccc', + dst_port_id: 'dddd', + type: IbcMessage.EVENT_TYPE.TIMEOUT_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'refund_receiver', + value: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + }, + { + key: 'refund_denom', + value: 'utaura', + }, + { + key: 'refund_amount', + value: '1000000', + }, + { + key: 'memo', + value: '', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.TIMEOUT, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + e.block_height = this.block.height; + e.event_id = 1; + e.index = index; + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Timeout( + this.block.height - 1, + this.block.height, + trx + ); + const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.receiver).toEqual( + getAttributeFrom( + event1Attrs, + EventAttribute.ATTRIBUTE_KEY.REFUND_RECEIVER + ) + ); + expect(result?.sender).toBeNull(); + expect(result?.amount).toEqual( + getAttributeFrom( + event1Attrs, + EventAttribute.ATTRIBUTE_KEY.REFUND_AMOUNT + ) + ); + expect(result?.denom).toEqual( + getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM) + ); + expect(result?.ack_status).toBeNull(); + await trx.rollback(); + }); + } +} From ec38e36dd85a9612e08de242f9e307e22d41978a Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Mon, 28 Aug 2023 09:31:57 +0700 Subject: [PATCH 41/54] fix: code --- test/unit/services/ibc/crawl_ibc_ics20.spec.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index ff4c0d4bf..08ce519fe 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -222,11 +222,9 @@ export default class CrawlIbcIcs20Test { getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) ); expect(result?.denom).toEqual( - `${ibcMessage.dst_port_id - }/${ - ibcMessage.dst_channel_id - }/${ - getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` + `${ibcMessage.dst_port_id}/${ + ibcMessage.dst_channel_id + }/${getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` ); expect(result?.ack_status).toEqual(true); await trx.rollback(); From a61ca2b8bdea28659ce14dd77f2888263e96e87c Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Mon, 28 Aug 2023 09:35:04 +0700 Subject: [PATCH 42/54] fix: code --- .../unit/services/ibc/crawl_ibc_ics20.spec.ts | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index 08ce519fe..853fd7f3e 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -185,9 +185,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event1Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -198,9 +200,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event2Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -290,9 +294,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event1Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -385,9 +391,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event2Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -398,9 +406,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event1Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), @@ -480,9 +490,11 @@ export default class CrawlIbcIcs20Test { block_height: this.block.height, source: 'TX_EVENT', attributes: event1Attrs.map((e, index) => { - e.block_height = this.block.height; - e.event_id = 1; - e.index = index; + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); return e; }), }), From 5c86a9f799b0860d303b8c9998f145342ffe02b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20Th=C3=A0nh=20Phong?= <49814372+phamphong9981@users.noreply.github.com> Date: Mon, 28 Aug 2023 14:01:36 +0700 Subject: [PATCH 43/54] fix: cw721 activity missing from/to ( main ) (#294) * fix: cw721 activity missing from/to * fix: fill from/to cw721 activity * fix: fill from/to cw721 activity * fix: fill from/to cw721 activity * fix: fill from/to cw721 activity * refactor: code * fix: code * refactor: code --------- Co-authored-by: Vu Ngoc Quang --- ...230808072704_update_data_cw721_activity.ts | 55 +++++++++++++++++++ src/models/cw721_tx.ts | 4 +- src/models/event_attribute.ts | 1 + src/models/smart_contract_event.ts | 2 + src/services/cw721/cw721.service.ts | 13 +++++ test/unit/services/cw721/cw721.spec.ts | 37 ++++++++++++- 6 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 migrations/20230808072704_update_data_cw721_activity.ts diff --git a/migrations/20230808072704_update_data_cw721_activity.ts b/migrations/20230808072704_update_data_cw721_activity.ts new file mode 100644 index 000000000..c78964cef --- /dev/null +++ b/migrations/20230808072704_update_data_cw721_activity.ts @@ -0,0 +1,55 @@ +import { Knex } from 'knex'; +import config from '../config.json' assert { type: 'json' }; +import CW721Activity from '../src/models/cw721_tx'; +import { getAttributeFrom } from '../src/common/utils/smart_contract'; +import { EventAttribute } from '../src/models'; +const UPDATE_CW721_ACTIONS = ['mint', 'burn', 'transfer_nft', 'send_nft']; +export async function up(knex: Knex): Promise { + await knex.transaction(async (trx) => { + let prevId = 0; + while (true) { + const handleRecords = await CW721Activity.query() + .withGraphFetched('smart_contract_event.attributes') + .where('cw721_activity.from', null) + .andWhere('cw721_activity.to', null) + .andWhere('cw721_activity.id', '>', prevId) + .whereIn('cw721_activity.action', UPDATE_CW721_ACTIONS) + .whereNotNull('smart_contract_event_id') + .orderBy('cw721_activity.id') + .limit(100) + .transacting(trx); + + if (handleRecords.length > 0) { + const patchQueries: any[] = []; + handleRecords.forEach((record) => { + patchQueries.push( + CW721Activity.query() + .patch({ + from: getAttributeFrom( + record.smart_contract_event.attributes, + EventAttribute.ATTRIBUTE_KEY.SENDER + ), + to: + getAttributeFrom( + record.smart_contract_event.attributes, + EventAttribute.ATTRIBUTE_KEY.OWNER + ) || + getAttributeFrom( + record.smart_contract_event.attributes, + EventAttribute.ATTRIBUTE_KEY.RECIPIENT + ), + }) + .where('id', record.id) + .transacting(trx) + ); + }); + await Promise.all(patchQueries); + prevId = handleRecords[handleRecords.length - 1].id; + } else { + break; + } + } + }); +} + +export async function down(knex: Knex): Promise {} diff --git a/src/models/cw721_tx.ts b/src/models/cw721_tx.ts index 3f20ce128..241949888 100644 --- a/src/models/cw721_tx.ts +++ b/src/models/cw721_tx.ts @@ -9,7 +9,9 @@ import { Event } from './event'; export default class CW721Activity extends BaseModel { static softDelete = false; - id?: number; + smart_contract_event!: SmartContractEvent; + + id!: number; action?: string; diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 2fb47b872..16227d816 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -96,6 +96,7 @@ export class EventAttribute extends BaseModel { GRANTER: 'granter', GRANTEE: 'grantee', FROM: 'from', + MINTER: 'minter', FEE: 'fee', FEE_PAYER: 'fee_payer', }; diff --git a/src/models/smart_contract_event.ts b/src/models/smart_contract_event.ts index c7d592c1d..bdf66e80a 100644 --- a/src/models/smart_contract_event.ts +++ b/src/models/smart_contract_event.ts @@ -10,6 +10,8 @@ import { TransactionMessage } from './transaction_message'; export class SmartContractEvent extends BaseModel { [relation: string]: any; + attributes!: SmartContractEventAttribute; + id!: number; smart_contract_id!: number; diff --git a/src/services/cw721/cw721.service.ts b/src/services/cw721/cw721.service.ts index b924d27c5..5f9b4653a 100644 --- a/src/services/cw721/cw721.service.ts +++ b/src/services/cw721/cw721.service.ts @@ -332,6 +332,19 @@ export default class Cw721HandlerService extends BullableService { cw721_token_id: cw721TokenId, height: cw721Event.height, smart_contract_event_id: cw721Event.smart_contract_event_id, + from: getAttributeFrom( + cw721Event.attributes, + EventAttribute.ATTRIBUTE_KEY.SENDER + ), + to: + getAttributeFrom( + cw721Event.attributes, + EventAttribute.ATTRIBUTE_KEY.OWNER + ) || + getAttributeFrom( + cw721Event.attributes, + EventAttribute.ATTRIBUTE_KEY.RECIPIENT + ), }) ) .onConflict(['smart_contract_event_id']) diff --git a/test/unit/services/cw721/cw721.spec.ts b/test/unit/services/cw721/cw721.spec.ts index 3a670e090..60c95a64f 100644 --- a/test/unit/services/cw721/cw721.spec.ts +++ b/test/unit/services/cw721/cw721.spec.ts @@ -2,8 +2,16 @@ import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; import { ServiceBroker } from 'moleculer'; import { BULL_JOB_NAME } from '../../../../src/common'; import knex from '../../../../src/common/utils/db_connection'; -import { getContractActivities } from '../../../../src/common/utils/smart_contract'; -import { Block, BlockCheckpoint, Transaction } from '../../../../src/models'; +import { + getAttributeFrom, + getContractActivities, +} from '../../../../src/common/utils/smart_contract'; +import { + Block, + BlockCheckpoint, + EventAttribute, + Transaction, +} from '../../../../src/models'; import { Code } from '../../../../src/models/code'; import CW721Contract from '../../../../src/models/cw721_contract'; import CW721Token from '../../../../src/models/cw721_token'; @@ -1296,6 +1304,31 @@ export default class AssetIndexerTest { expect(cw721Activities[0].cw721_token_id).toEqual(1); expect(cw721Activities[1].cw721_token_id).toEqual(0); expect(cw721Activities[2].cw721_token_id).toEqual(2); + expect(cw721Activities[0].from).toEqual(null); + expect(cw721Activities[0].to).toEqual( + getAttributeFrom( + mockActivityMsgs[0].attributes, + EventAttribute.ATTRIBUTE_KEY.OWNER + ) + ); + expect(cw721Activities[1].from).toEqual( + getAttributeFrom( + mockActivityMsgs[1].attributes, + EventAttribute.ATTRIBUTE_KEY.SENDER + ) + ); + expect(cw721Activities[1].to).toEqual( + getAttributeFrom( + mockActivityMsgs[1].attributes, + EventAttribute.ATTRIBUTE_KEY.RECIPIENT + ) + ); + expect(cw721Activities[2].from).toEqual( + getAttributeFrom( + mockActivityMsgs[2].attributes, + EventAttribute.ATTRIBUTE_KEY.SENDER + ) + ); } @Test('test handle multi contract events') From dcec038f79d9afe4be72eb498364ba9d8f46f845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20Th=C3=A0nh=20Phong?= <49814372+phamphong9981@users.noreply.github.com> Date: Mon, 28 Aug 2023 17:02:08 +0700 Subject: [PATCH 44/54] feat: crawl ibc tao ( main ) (#278) * feat: crawl ibc tao * feat: crawl genesis ibc tao * fix: code * fix: code --- ci/config.json.ci | 5 + config.json | 5 + migrations/20230802071931_ibc_tao_model.ts | 36 ++ src/common/constant.ts | 6 + src/models/event.ts | 10 +- src/models/event_attribute.ts | 9 + src/models/ibc_channel.ts | 64 +++ src/models/ibc_client.ts | 40 ++ src/models/ibc_connection.ts | 51 +++ src/models/index.ts | 3 + .../api-gateways/api_gateway.service.ts | 8 +- .../crawl-genesis/crawl_genesis.service.ts | 114 +++++ src/services/ibc/crawl_ibc_tao.service.ts | 259 +++++++++++ test/unit/services/ibc/crawl_ibc_tao.spec.ts | 428 ++++++++++++++++++ 14 files changed, 1035 insertions(+), 3 deletions(-) create mode 100644 migrations/20230802071931_ibc_tao_model.ts create mode 100644 src/models/ibc_channel.ts create mode 100644 src/models/ibc_client.ts create mode 100644 src/models/ibc_connection.ts create mode 100644 src/services/ibc/crawl_ibc_tao.service.ts create mode 100644 test/unit/services/ibc/crawl_ibc_tao.spec.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index bf53142cf..afc6cd564 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -174,5 +174,10 @@ }, "jobRedecodeTx": { "limitRecordGet": 100 + }, + "crawlIbcTao": { + "key": "crawlIbcTao", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100 } } diff --git a/config.json b/config.json index d6766950a..686cbb866 100644 --- a/config.json +++ b/config.json @@ -174,5 +174,10 @@ }, "jobRedecodeTx": { "limitRecordGet": 100 + }, + "crawlIbcTao": { + "key": "crawlIbcTao", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100 } } diff --git a/migrations/20230802071931_ibc_tao_model.ts b/migrations/20230802071931_ibc_tao_model.ts new file mode 100644 index 000000000..a1b2a03b4 --- /dev/null +++ b/migrations/20230802071931_ibc_tao_model.ts @@ -0,0 +1,36 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('ibc_client', (table) => { + table.increments(); + table.string('client_id').notNullable().unique(); + table.string('counterparty_chain_id').notNullable(); + table.jsonb('client_state').notNullable(); + table.jsonb('consensus_state').notNullable(); + table.string('client_type').notNullable(); + }); + await knex.schema.createTable('ibc_connection', (table) => { + table.increments(); + table.integer('ibc_client_id').index(); + table.string('connection_id').notNullable().unique(); + table.string('counterparty_client_id').notNullable(); + table.string('counterparty_connection_id').notNullable(); + table.foreign('ibc_client_id').references('ibc_client.id'); + }); + await knex.schema.createTable('ibc_channel', (table) => { + table.increments(); + table.integer('ibc_connection_id').index(); + table.string('channel_id').notNullable().unique(); + table.string('port_id').notNullable().index(); + table.string('counterparty_port_id').notNullable(); + table.string('counterparty_channel_id').notNullable(); + table.string('state').notNullable(); + table.foreign('ibc_connection_id').references('ibc_connection.id'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists('ibc_client'); + await knex.schema.dropTableIfExists('ibc_connection'); + await knex.schema.dropTableIfExists('ibc_channel'); +} diff --git a/src/common/constant.ts b/src/common/constant.ts index 9e08c3600..465d9b5ad 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -71,6 +71,8 @@ export const BULL_JOB_NAME = { REINDEX_CW721_HISTORY: 'reindex:cw721-history', HANDLE_MIGRATE_CONTRACT: 'handle:migrate-contract', JOB_REDECODE_TX: 'job:redecode-tx', + CRAWL_IBC_TAO: 'crawl:ibc-tao', + CRAWL_GENESIS_IBC_TAO: 'crawl:genesis-ibc-tao', REINDEX_CW20_CONTRACT: 'reindex:cw20-contract', REINDEX_CW20_HISTORY: 'reindex:cw20-history', }; @@ -230,6 +232,10 @@ export const SERVICE = { path: 'v1.ReDecodeTx', }, }, + CrawlIBCTaoService: { + key: 'CrawlIBCTaoService', + name: 'v1.CrawlIBCTaoService', + }, DailyStatisticsService: { key: 'DailyStatisticsService', name: 'v1.DailyStatisticsService', diff --git a/src/models/event.ts b/src/models/event.ts index 3f983faea..da9a05b6b 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -6,7 +6,7 @@ import { EventAttribute } from './event_attribute'; import { TransactionMessage } from './transaction_message'; export class Event extends BaseModel { - [relation: string]: any; + [relation: string]: any | any[]; id!: string; @@ -94,5 +94,13 @@ export class Event extends BaseModel { TX: 'tx', TRANSFER: 'transfer', MIGRATE: 'migrate', + CREATE_CLIENT: 'create_client', + CONNECTION_OPEN_ACK: 'connection_open_ack', + CONNECTION_OPEN_CONFIRM: 'connection_open_confirm', + CHANNEL_OPEN_ACK: 'channel_open_ack', + CHANNEL_OPEN_CONFIRM: 'channel_open_confirm', + CHANNEL_CLOSE_INIT: 'channel_close_init', + CHANNEL_CLOSE_CONFIRM: 'channel_close_confirm', + CHANNEL_CLOSE: 'channel_close', }; } diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 16227d816..48a8d4f7d 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -96,6 +96,15 @@ export class EventAttribute extends BaseModel { GRANTER: 'granter', GRANTEE: 'grantee', FROM: 'from', + CLIENT_ID: 'client_id', + CLIENT_TYPE: 'client_type', + CONNECTION_ID: 'connection_id', + COUNTERPARTY_CLIENT_ID: 'counterparty_client_id', + COUNTERPARTY_CONNECTION_ID: 'counterparty_connection_id', + CHANNEL_ID: 'channel_id', + PORT_ID: 'port_id', + COUNTERPARTY_PORT_ID: 'counterparty_port_id', + COUNTERPARTY_CHANNEL_ID: 'counterparty_channel_id', MINTER: 'minter', FEE: 'fee', FEE_PAYER: 'fee_payer', diff --git a/src/models/ibc_channel.ts b/src/models/ibc_channel.ts new file mode 100644 index 000000000..b1720ccaa --- /dev/null +++ b/src/models/ibc_channel.ts @@ -0,0 +1,64 @@ +/* eslint-disable import/no-cycle */ +import { Model } from 'objection'; +import BaseModel from './base'; +import { IbcConnection } from './ibc_connection'; + +export class IbcChannel extends BaseModel { + id!: number; + + ibc_connection_id!: number; + + channel_id!: string; + + port_id!: string; + + counterparty_port_id!: string; + + counterparty_channel_id!: string; + + state!: string; + + static get tableName() { + return 'ibc_channel'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'ibc_connection_id', + 'channel_id', + 'port_id', + 'counterparty_port_id', + 'counterparty_channel_id', + 'state', + ], + properties: { + ibc_connection_id: { type: 'number' }, + channel_id: { type: 'string' }, + port_id: { type: 'string' }, + counterparty_port_id: { type: 'string' }, + counterparty_channel_id: { type: 'string' }, + state: { type: 'string' }, + }, + }; + } + + static get relationMappings() { + return { + ibc_connection: { + relation: Model.BelongsToOneRelation, + modelClass: IbcConnection, + join: { + from: 'ibc_channel.ibc_connection_id', + to: 'ibc_connection.id', + }, + }, + }; + } + + static STATUS = { + OPEN: 'OPEN', + CLOSE: 'CLOSE', + }; +} diff --git a/src/models/ibc_client.ts b/src/models/ibc_client.ts new file mode 100644 index 000000000..4575e446d --- /dev/null +++ b/src/models/ibc_client.ts @@ -0,0 +1,40 @@ +/* eslint-disable import/no-cycle */ +import BaseModel from './base'; + +export class IbcClient extends BaseModel { + id!: number; + + client_id!: string; + + counterparty_chain_id!: string; + + client_state!: any; + + consensus_state!: any; + + client_type!: string; + + static get tableName() { + return 'ibc_client'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'client_id', + 'counterparty_chain_id', + 'client_state', + 'consensus_state', + 'client_type', + ], + properties: { + client_id: { type: 'string' }, + counterparty_chain_id: { type: 'string' }, + client_state: { type: 'object' }, + consensus_state: { type: 'object' }, + client_type: { type: 'string' }, + }, + }; + } +} diff --git a/src/models/ibc_connection.ts b/src/models/ibc_connection.ts new file mode 100644 index 000000000..df9ed2e24 --- /dev/null +++ b/src/models/ibc_connection.ts @@ -0,0 +1,51 @@ +/* eslint-disable import/no-cycle */ +import { Model } from 'objection'; +import BaseModel from './base'; +import { IbcClient } from './ibc_client'; + +export class IbcConnection extends BaseModel { + id!: number; + + ibc_client_id!: number; + + connection_id!: string; + + counterparty_client_id!: string; + + counterparty_connection_id!: string; + + static get tableName() { + return 'ibc_connection'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'ibc_client_id', + 'connection_id', + 'counterparty_client_id', + 'counterparty_connection_id', + ], + properties: { + ibc_client_id: { type: 'number' }, + connection_id: { type: 'string' }, + counterparty_client_id: { type: 'string' }, + counterparty_connection_id: { type: 'string' }, + }, + }; + } + + static get relationMappings() { + return { + ibc_client: { + relation: Model.BelongsToOneRelation, + modelClass: IbcClient, + join: { + from: 'ibc_connection.ibc_client_id', + to: 'ibc_client.id', + }, + }, + }; + } +} diff --git a/src/models/index.ts b/src/models/index.ts index 9ee83f061..02310cefe 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -25,3 +25,6 @@ export * from './feegrant_history'; export * from './daily_statistics'; export * from './account_statistics'; export * from './cw20_total_holder_stats'; +export * from './ibc_client'; +export * from './ibc_connection'; +export * from './ibc_channel'; diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index 8fd7ddb66..5021b71ab 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -31,8 +31,12 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; path: '/admin', autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services mappingPolicy: 'restrict', // allow action called with exact method - whitelist: ['v2.statistics.syncPrevDateStatsByChainId', 'v1.cw20-admin.*', 'v1.cw721-admin.*'], - } + whitelist: [ + 'v2.statistics.syncPrevDateStatsByChainId', + 'v1.cw20-admin.*', + 'v1.cw721-admin.*', + ], + }, ], // empty cors object will have moleculer to generate handler for preflight request and CORS header which allow all origin cors: {}, diff --git a/src/services/crawl-genesis/crawl_genesis.service.ts b/src/services/crawl-genesis/crawl_genesis.service.ts index 03824afea..9a3465984 100644 --- a/src/services/crawl-genesis/crawl_genesis.service.ts +++ b/src/services/crawl-genesis/crawl_genesis.service.ts @@ -22,6 +22,9 @@ import { SmartContract, Validator, Feegrant, + IbcClient, + IbcConnection, + IbcChannel, } from '../../models'; import BullableService, { QueueHandler } from '../../base/bullable.service'; import { @@ -52,6 +55,7 @@ export default class CrawlGenesisService extends BullableService { BULL_JOB_NAME.CRAWL_GENESIS_CODE, BULL_JOB_NAME.CRAWL_GENESIS_CONTRACT, BULL_JOB_NAME.CRAWL_GENESIS_FEEGRANT, + BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO, ]; public constructor(public broker: ServiceBroker) { @@ -669,6 +673,116 @@ export default class CrawlGenesisService extends BullableService { await this.terminateProcess(); } + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO, + jobName: BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async crawlGenesisIbcTao(_payload: object): Promise { + this.logger.info('Crawl genesis Ibc Tao'); + + const genesisProcess = await this.checkGenesisJobProcess( + BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO + ); + if (genesisProcess !== 0) return; + await knex + .transaction(async (trx) => { + // crawl genesis ibc client + const genClients: any[] = await this.readStreamGenesis( + 'app_state.ibc.client_genesis.clients' + ); + const genClientsConsensus: any[] = await this.readStreamGenesis( + 'app_state.ibc.client_genesis.clients_consensus' + ); + const ibcClients: IbcClient[] = genClients.map((genClient: any) => { + const consensusStates = genClientsConsensus.find( + (clientConsensus) => + clientConsensus.client_id === genClient.client_id + ); + return IbcClient.fromJson({ + client_id: genClient.client_id, + counterparty_chain_id: genClient.client_state.chain_id, + client_state: genClient.client_state, + consensus_state: + consensusStates.consensus_states[ + consensusStates.consensus_states.length - 1 + ], + client_type: genClient.client_id.substring( + 0, + genClient.client_id.lastIndexOf('-') + ), + }); + }); + let newClients: IbcClient[] = []; + if (ibcClients.length > 0) { + newClients = await IbcClient.query() + .insert(ibcClients) + .transacting(trx); + } + this.logger.info('Done client'); + // crawl genesis ibc connections + const genConnections: any[] = await this.readStreamGenesis( + 'app_state.ibc.connection_genesis.connections' + ); + const ibcConnections: IbcConnection[] = genConnections.map( + (genConnection: any) => + IbcConnection.fromJson({ + ibc_client_id: newClients.find( + (client) => client.client_id === genConnection.client_id + )?.id, + connection_id: genConnection.id, + counterparty_client_id: genConnection.counterparty.client_id, + counterparty_connection_id: + genConnection.counterparty.connection_id, + }) + ); + let newConnections: IbcConnection[] = []; + if (ibcConnections.length > 0) { + newConnections = await IbcConnection.query() + .insert(ibcConnections) + .transacting(trx); + } + this.logger.info('Done connections'); + // crawl genesis ibc channels + const genChannels: any[] = await this.readStreamGenesis( + 'app_state.ibc.channel_genesis.channels' + ); + const IbcChannels: IbcChannel[] = genChannels.map((genChannel: any) => + IbcChannel.fromJson({ + ibc_connection_id: newConnections.find( + (connection) => + connection.connection_id === genChannel.connection_hops[0] + )?.id, + channel_id: genChannel.channel_id, + port_id: genChannel.port_id, + counterparty_port_id: genChannel.counterparty.port_id, + counterparty_channel_id: genChannel.counterparty.channel_id, + state: genChannel.state, + }) + ); + if (IbcChannels.length > 0) { + await IbcChannel.query().insert(IbcChannels).transacting(trx); + } + this.logger.info('Done channel'); + await BlockCheckpoint.query() + .insert( + BlockCheckpoint.fromJson({ + job_name: BULL_JOB_NAME.CRAWL_GENESIS_IBC_TAO, + height: 1, + }) + ) + .onConflict('job_name') + .merge() + .returning('id') + .transacting(trx); + }) + .catch((error) => { + this.logger.error(error); + throw error; + }); + await this.terminateProcess(); + } + private async handleIbcDenom(accounts: Account[]): Promise { if (accounts.length === 0) return []; diff --git a/src/services/ibc/crawl_ibc_tao.service.ts b/src/services/ibc/crawl_ibc_tao.service.ts new file mode 100644 index 000000000..4f047f58c --- /dev/null +++ b/src/services/ibc/crawl_ibc_tao.service.ts @@ -0,0 +1,259 @@ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import { Knex } from 'knex'; +import _ from 'lodash'; +import knex from '../../common/utils/db_connection'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import { + BlockCheckpoint, + Event, + EventAttribute, + IbcChannel, + IbcClient, + IbcConnection, +} from '../../models'; +import { getAttributeFrom } from '../../common/utils/smart_contract'; + +@Service({ + name: SERVICE.V1.CrawlIBCTaoService.key, + version: 1, +}) +export default class CrawlIbcTaoService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_IBC_TAO, + jobName: BULL_JOB_NAME.CRAWL_IBC_TAO, + }) + public async crawlIbcTao(): Promise { + const [startHeight, endHeight, updateBlockCheckpoint] = + await BlockCheckpoint.getCheckpoint( + BULL_JOB_NAME.CRAWL_IBC_TAO, + [BULL_JOB_NAME.HANDLE_TRANSACTION], + config.crawlIbcTao.key + ); + this.logger.info( + `Handle IBC/TAO, startHeight: ${startHeight}, endHeight: ${endHeight}` + ); + if (startHeight > endHeight) return; + const events = await Event.query() + .withGraphFetched('attributes') + .joinRelated('message') + .select('event.id', 'event.type', 'message.content') + .whereIn('event.type', [ + Event.EVENT_TYPE.CREATE_CLIENT, + Event.EVENT_TYPE.CONNECTION_OPEN_ACK, + Event.EVENT_TYPE.CONNECTION_OPEN_CONFIRM, + Event.EVENT_TYPE.CHANNEL_OPEN_ACK, + Event.EVENT_TYPE.CHANNEL_OPEN_CONFIRM, + Event.EVENT_TYPE.CHANNEL_CLOSE_INIT, + Event.EVENT_TYPE.CHANNEL_CLOSE_CONFIRM, + Event.EVENT_TYPE.CHANNEL_CLOSE, + ]) + .andWhere('event.block_height', '>', startHeight) + .andWhere('event.block_height', '<=', endHeight) + .orderBy('event.id'); + await knex.transaction(async (trx) => { + await this.handleNewIbcClient( + events.filter((event) => event.type === Event.EVENT_TYPE.CREATE_CLIENT), + trx + ); + await this.handleNewIbcConnection( + events.filter( + (event) => + event.type === Event.EVENT_TYPE.CONNECTION_OPEN_CONFIRM || + event.type === Event.EVENT_TYPE.CONNECTION_OPEN_ACK + ), + trx + ); + await this.handleNewIbcChannel( + events.filter( + (event) => + event.type === Event.EVENT_TYPE.CHANNEL_OPEN_ACK || + event.type === Event.EVENT_TYPE.CHANNEL_OPEN_CONFIRM + ), + trx + ); + await this.handleCloseIbcChannel( + events.filter( + (event) => + event.type === Event.EVENT_TYPE.CHANNEL_CLOSE || + event.type === Event.EVENT_TYPE.CHANNEL_CLOSE_CONFIRM || + event.type === Event.EVENT_TYPE.CHANNEL_CLOSE_INIT + ), + trx + ); + updateBlockCheckpoint.height = endHeight; + await BlockCheckpoint.query() + .transacting(trx) + .insert(updateBlockCheckpoint) + .onConflict('job_name') + .merge(); + }); + } + + async handleNewIbcClient(events: Event[], trx: Knex.Transaction) { + if (events.length > 0) { + const newClients: IbcClient[] = events.map((event) => + IbcClient.fromJson({ + client_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CLIENT_ID + ), + counterparty_chain_id: event.content.client_state.chain_id, + client_state: event.content.client_state, + consensus_state: event.content.consensus_state, + client_type: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CLIENT_TYPE + ), + }) + ); + this.logger.info('New IBC Clients:'); + this.logger.info(newClients); + await IbcClient.query().insert(newClients).transacting(trx); + } + } + + async handleNewIbcConnection(events: Event[], trx: Knex.Transaction) { + if (events.length > 0) { + const ibcClientsByClientId = _.keyBy( + await IbcClient.query() + .whereIn( + 'client_id', + events.map((event) => + getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CLIENT_ID + ) + ) + ) + .transacting(trx), + 'client_id' + ); + const newConnections: IbcConnection[] = events.map((event) => + IbcConnection.fromJson({ + ibc_client_id: + ibcClientsByClientId[ + getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CLIENT_ID + ) + ].id, + connection_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CONNECTION_ID + ), + counterparty_client_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.COUNTERPARTY_CLIENT_ID + ), + counterparty_connection_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.COUNTERPARTY_CONNECTION_ID + ), + }) + ); + this.logger.info('New IBC Connections:'); + this.logger.info(newConnections); + await IbcConnection.query().insert(newConnections).transacting(trx); + } + } + + async handleNewIbcChannel(events: Event[], trx: Knex.Transaction) { + if (events.length > 0) { + const ibcConnectionsByConnetionId = _.keyBy( + await IbcConnection.query() + .whereIn( + 'connection_id', + events.map((event) => + getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CONNECTION_ID + ) + ) + ) + .transacting(trx), + 'connection_id' + ); + const newChannels: IbcChannel[] = events.map((event) => + IbcChannel.fromJson({ + ibc_connection_id: + ibcConnectionsByConnetionId[ + getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CONNECTION_ID + ) + ].id, + channel_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CHANNEL_ID + ), + port_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.PORT_ID + ), + counterparty_port_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.COUNTERPARTY_PORT_ID + ), + counterparty_channel_id: getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.COUNTERPARTY_CHANNEL_ID + ), + state: IbcChannel.STATUS.OPEN, + }) + ); + this.logger.info('New IBC Channels:'); + this.logger.info(newChannels); + await IbcChannel.query().insert(newChannels).transacting(trx); + } + } + + async handleCloseIbcChannel(events: Event[], trx: Knex.Transaction) { + if (events.length > 0) { + const queries = events.map((event) => { + const channelId = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.CHANNEL_ID + ); + if (!channelId) { + throw new Error( + `Event close channel not found channelId: ${event.id}` + ); + } + return IbcChannel.query() + .patch({ + state: IbcChannel.STATUS.CLOSE, + }) + .where({ + channel_id: channelId, + }) + .transacting(trx); + }); + await Promise.all(queries); + } + } + + async _start(): Promise { + await this.createJob( + BULL_JOB_NAME.CRAWL_IBC_TAO, + BULL_JOB_NAME.CRAWL_IBC_TAO, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + every: config.crawlIbcTao.millisecondRepeatJob, + }, + } + ); + return super._start(); + } +} diff --git a/test/unit/services/ibc/crawl_ibc_tao.spec.ts b/test/unit/services/ibc/crawl_ibc_tao.spec.ts new file mode 100644 index 000000000..699a6d02c --- /dev/null +++ b/test/unit/services/ibc/crawl_ibc_tao.spec.ts @@ -0,0 +1,428 @@ +import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { ServiceBroker } from 'moleculer'; +import knex from '../../../../src/common/utils/db_connection'; +import { + Block, + Event, + IbcChannel, + IbcClient, + IbcConnection, + Transaction, +} from '../../../../src/models'; +import CrawlIbcTaoService from '../../../../src/services/ibc/crawl_ibc_tao.service'; + +@Describe('Test crawl ibc service') +export default class CrawlIbcTest { + broker = new ServiceBroker({ logger: false }); + + crawlIbcTaoSerivce = this.broker.createService( + CrawlIbcTaoService + ) as CrawlIbcTaoService; + + block: Block = Block.fromJson({ + height: 1300000, + hash: '4801997745BDD354C8F11CE4A4137237194099E664CD8F83A5FBA9041C43FE9F', + time: '2023-01-12T01:53:57.216Z', + proposer_address: 'auraomd;cvpio3j4eg', + data: {}, + }); + + transaction: Transaction = Transaction.fromJson({ + height: this.block.height, + hash: '4A8B0DE950F563553A81360D4782F6EC451F6BEF7AC50E2459D1997FA168997D', + codespace: '', + code: 0, + gas_used: '123035', + gas_wanted: '141106', + gas_limit: '141106', + fee: 353, + timestamp: '2023-01-12T01:53:57.000Z', + index: 0, + data: { + tx_response: { + logs: [], + }, + }, + }); + + @BeforeAll() + async initSuite() { + this.crawlIbcTaoSerivce.getQueueManager().stopAll(); + await knex.raw('TRUNCATE TABLE block, ibc_client RESTART IDENTITY CASCADE'); + await Block.query().insert(this.block); + } + + @AfterAll() + async tearDown() { + await this.broker.stop(); + } + + @Test('Test handleNewIbcClient') + async testHandleNewIbcClient() { + await knex.transaction(async (trx) => { + const events = Event.fromJson({ + type: Event.EVENT_TYPE.CREATE_CLIENT, + attributes: [ + { + value: '07-tendermint-21', + key: 'client_id', + event_id: '1', + }, + { + value: '07-tendermint', + key: 'client_type', + event_id: '1', + }, + { + value: '3-5086858', + key: 'consensus_height', + event_id: '1', + }, + ], + content: { + '@type': '/ibc.core.client.v1.MsgCreateClient', + signer: 'aura1gypt2w7xg5t9yr76hx6zemwd4xv72jckk03r6t', + client_state: { + '@type': '/ibc.lightclients.tendermint.v1.ClientState', + chain_id: 'axelar-testnet-lisbon-3', + proof_specs: [ + { + leaf_spec: { + hash: 'SHA256', + length: 'VAR_PROTO', + prefix: 'AA==', + prehash_key: 'NO_HASH', + prehash_value: 'SHA256', + }, + max_depth: 0, + min_depth: 0, + inner_spec: { + hash: 'SHA256', + child_size: 33, + child_order: [0, 1], + empty_child: '', + max_prefix_length: 12, + min_prefix_length: 4, + }, + }, + { + leaf_spec: { + hash: 'SHA256', + length: 'VAR_PROTO', + prefix: 'AA==', + prehash_key: 'NO_HASH', + prehash_value: 'SHA256', + }, + max_depth: 0, + min_depth: 0, + inner_spec: { + hash: 'SHA256', + child_size: 32, + child_order: [0, 1], + empty_child: '', + max_prefix_length: 1, + min_prefix_length: 1, + }, + }, + ], + trust_level: { + numerator: '1', + denominator: '3', + }, + upgrade_path: ['upgrade', 'upgradedIBCState'], + frozen_height: { + revision_height: '0', + revision_number: '0', + }, + latest_height: { + revision_height: '5086858', + revision_number: '3', + }, + max_clock_drift: '40000000000', + trusting_period: '403200000000000', + unbonding_period: '604800000000000', + allow_update_after_expiry: true, + allow_update_after_misbehaviour: true, + }, + consensus_state: { + root: { + hash: '/iST1C+vhywD9qgGNqSQvs0NIPlQMqIDwgIJKMIylUI=', + }, + '@type': '/ibc.lightclients.tendermint.v1.ConsensusState', + timestamp: '2022-12-01T07:22:43.523Z', + next_validators_hash: + 'RY9Nf/qtdDMVQK7LMjoVgrS1CkZaEVj02CDlkdgzutM=', + }, + }, + }); + await this.crawlIbcTaoSerivce.handleNewIbcClient([events], trx); + const newClient = await IbcClient.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newClient.client_id).toEqual(events.attributes[0].value); + expect(newClient.client_type).toEqual(events.attributes[1].value); + expect(newClient.client_state).toEqual(events.content.client_state); + expect(newClient.consensus_state).toEqual(events.content.consensus_state); + expect(newClient.counterparty_chain_id).toEqual( + events.content.client_state.chain_id + ); + // await trx.rollback(); + }); + } + + @Test('Test handleNewIbcConnection') + async testHandleNewIbcConnection() { + await knex.transaction(async (trx) => { + const events = Event.fromJson({ + type: Event.EVENT_TYPE.CONNECTION_OPEN_ACK, + attributes: [ + { + value: 'connection-72', + key: 'connection_id', + event_id: '1', + }, + { + value: '07-tendermint-21', + key: 'client_id', + event_id: '1', + }, + { + value: '07-tendermint-45', + key: 'counterparty_client_id', + event_id: '1', + }, + { + value: 'connection-27', + key: 'counterparty_connection_id', + event_id: '1', + }, + ], + content: { + '@type': '/ibc.core.connection.v1.MsgConnectionOpenAck', + signer: 'aura1gypt2w7xg5t9yr76hx6zemwd4xv72jckk03r6t', + version: { + features: ['ORDER_ORDERED', 'ORDER_UNORDERED'], + identifier: '1', + }, + proof_try: + 'CsQICsEIChljb25uZWN0aW9ucy9jb25uZWN0aW9uLTI3EmMKEDA3LXRlbmRlcm1pbnQtNDUSIwoBMRINT1JERVJfT1JERVJFRBIPT1JERVJfVU5PUkRFUkVEGAIiKAoQMDctdGVuZGVybWludC04MBINY29ubmVjdGlvbi03MhoFCgNpYmMaDggBGAEgASoGAAKa8pYEIi4IARIHAgSa8pYEIBohIB/PBZ4uljQRWTUoJWTvlXAf+Sf3geF7LsWKI7+WpaUwIiwIARIoBAia8pYEIEIyNKRwbo7UewNW1Bg6gWp+7QriRoCvyv6ZbKjZHs9wICIsCAESKAYMmvKWBCCeMewQOVwuaeBrU7LaB5kuNW/4JAlZjbrMXHi+1d1mlSAiLAgBEigIGprylgQg3u9esjU846jXfzCL7RGmbCTrgcWgML7vMgDjjXy8LkUgIi4IARIHCiaa8pYEIBohIDGrQ5aJZsN6Ut9GQ3wNVnStZ8Dr7mrIPRFRG/Zv3Z5CIiwIARIoDDia8pYEIHbOF4x7UN1YqA+xoo7KL7w0/rXENvBouFTaVqaJPA7gICIsCAESKA5gmvKWBCA9QpxrAfbqqlM63etC3YtsbTzTJAWBnIGK430UOkRK2CAiLQgBEikQ0AGi8pYEICUsHnwDn1/px2K6DyMm9lISpOrK4TNSLLpbJKbmXp6OICItCAESKRKuA6LylgQgXH14FUNckJ7q6yDzHxVWwkawlgmkdaa9650oE2k2OjsgIi0IARIpFKYFovKWBCAkaa0OlC4pKo4m2xRQeVbx0tieLhfak6Xsu5QVp9cXCCAiLQgBEikWxAqi8pYEIIOT6BYqI5lA+xTBTIRO+dPHH+MnF/7FXoFVz1/cEdVoICItCAESKRj0D6LylgQgxiwkb7P1fNqTdZUSduYl+OfDeXURD69QiFtfjNelu5UgIi0IARIpGqodovKWBCAuUcxazpKF/ptiPz3WEHJuhQNHrYxXUohbInjviGhzCCAiLQgBEikc2F6i8pYEIGlpNHXMBW35F/0aor1DBEL5XBEKxhQigsCTP1pmNykLICIuCAESKh7YlAGi8pYEINa1ZObRgViKtwVE+LVx59w3/oF2pnB0sjlIYyPhwFu2ICIuCAESKiDY7AKi8pYEIPFQOkaEFsZuc5VCL3XLMvndPVA1pCpQVUqq1MAtCRxOICIuCAESKiLq7wWi8pYEIG+vkeRtqUosbXbXctdffMe0oJ0VwyLp+b6J21UuXLBOICIuCAESKibe5Rai8pYEIMdjCIRMYnQf4Nph+jj/sgQbJOO/p/fXqVBXy3hKUr7TICIwCAESCSr+mCai8pYEIBohIKJhx7HKM0nG82d3MAl9cguF5EaDS8VfAQ6E2jwPqc88Ii4IARIqLNiOQaLylgQgZGldPsQJXlzRlduhZIdTRH4SHfo5Xbh/ouka5hktIfcgCv4BCvsBCgNpYmMSIAD8TsE9shScVyP/CHb+hr1siAWPBMHaduu4dWdgj1rBGgkIARgBIAEqAQAiJwgBEgEBGiD5Vt5/BBmvQQnMGXTh8TcjCr0rjLn1J3de7MoaiBcPIiIlCAESIQEvzAtKPUXmsWa73ufs4PS4NzfxMUJ/f7tJz7zRzeuU8CInCAESAQEaIAeJGXGcGsRVvSn5H69yS0J+ru1UFKAa8tkcM2iQ3uOEIiUIARIhAWVN4MYZgTIpdDlaD9rFq8YDvzV9R+cVZEWff23oGt7iIicIARIBARogbDU0NT+iBtS39EPHJu5GTrbal9trUT5GhRdwLYLr+WQ=', + client_state: { + '@type': '/ibc.lightclients.tendermint.v1.ClientState', + chain_id: 'euphoria-2', + proof_specs: [ + { + leaf_spec: { + hash: 'SHA256', + length: 'VAR_PROTO', + prefix: 'AA==', + prehash_key: 'NO_HASH', + prehash_value: 'SHA256', + }, + max_depth: 0, + min_depth: 0, + inner_spec: { + hash: 'SHA256', + child_size: 33, + child_order: [0, 1], + empty_child: '', + max_prefix_length: 12, + min_prefix_length: 4, + }, + }, + { + leaf_spec: { + hash: 'SHA256', + length: 'VAR_PROTO', + prefix: 'AA==', + prehash_key: 'NO_HASH', + prehash_value: 'SHA256', + }, + max_depth: 0, + min_depth: 0, + inner_spec: { + hash: 'SHA256', + child_size: 32, + child_order: [0, 1], + empty_child: '', + max_prefix_length: 1, + min_prefix_length: 1, + }, + }, + ], + trust_level: { + numerator: '1', + denominator: '3', + }, + upgrade_path: ['upgrade', 'upgradedIBCState'], + frozen_height: { + revision_height: '0', + revision_number: '0', + }, + latest_height: { + revision_height: '5744576', + revision_number: '2', + }, + max_clock_drift: '20000000000', + trusting_period: '115200000000000', + unbonding_period: '172800000000000', + allow_update_after_expiry: true, + allow_update_after_misbehaviour: true, + }, + proof_client: + 'CqMJCqAJCiRjbGllbnRzLzA3LXRlbmRlcm1pbnQtNDUvY2xpZW50U3RhdGUSsgEKKy9pYmMubGlnaHRjbGllbnRzLnRlbmRlcm1pbnQudjEuQ2xpZW50U3RhdGUSggEKCmV1cGhvcmlhLTISBAgBEAMaBAiAhAciBAiAxgoqAggUMgA6BwgCEMDP3gJCGQoJCAEYASABKgEAEgwKAgABECEYBCAMMAFCGQoJCAEYASABKgEAEgwKAgABECAYASABMAFKB3VwZ3JhZGVKEHVwZ3JhZGVkSUJDU3RhdGVQAVgBGg4IARgBIAEqBgACovKWBCIuCAESBwIEovKWBCAaISBAlbewzvl2PjgUtyznqYRFOiNhxJp+KAq2YmJaxmAAsiIsCAESKAQGovKWBCDjWwUrygy8PnWb8yoN4Xwomockb9eYporo/dYJRHBDziAiLAgBEigGDqLylgQgjCxek5E7g4E/jZ/z9ayx2Rz8Wlwd5SourZeBm+l7ORkgIiwIARIoCByi8pYEIBzZYxppGpUUOli49o63SLSJv24Pv0bfhHrQL4/b7xR9ICIuCAESBwo0ovKWBCAaISCDdwKGH9QJ5iwCx763LCpf7/QE2iVdkRd52M3o9zDyOSIuCAESBwxGovKWBCAaISDhzb/9iqmZ90/la2ecG8xfrP1g49sNRKF7eHRoZ41hQyIsCAESKA5wovKWBCBbVdBjKBpebgwZFVLIitdMRdO0N0BkfjDLFp9EJMqrNCAiLwgBEggQ0AGi8pYEIBohIJfTNDSJ0SRz/JI1ivIeLCdm3ejk0yl2A7EwNe9AfAYZIi0IARIpEq4DovKWBCBcfXgVQ1yQnurrIPMfFVbCRrCWCaR1pr3rnSgTaTY6OyAiLQgBEikUpgWi8pYEICRprQ6ULikqjibbFFB5VvHS2J4uF9qTpey7lBWn1xcIICItCAESKRbECqLylgQgg5PoFiojmUD7FMFMhE7508cf4ycX/sVegVXPX9wR1WggIi0IARIpGPQPovKWBCDGLCRvs/V82pN1lRJ25iX458N5dREPr1CIW1+M16W7lSAiLQgBEikaqh2i8pYEIC5RzFrOkoX+m2I/PdYQcm6FA0etjFdSiFsieO+IaHMIICItCAESKRzYXqLylgQgaWk0dcwFbfkX/RqivUMEQvlcEQrGFCKCwJM/WmY3KQsgIi4IARIqHtiUAaLylgQg1rVk5tGBWIq3BUT4tXHn3Df+gXamcHSyOUhjI+HAW7YgIi4IARIqINjsAqLylgQg8VA6RoQWxm5zlUIvdcsy+d09UDWkKlBVSqrUwC0JHE4gIi4IARIqIurvBaLylgQgb6+R5G2pSixtdtdy1198x7SgnRXDIun5vonbVS5csE4gIi4IARIqJt7lFqLylgQgx2MIhExidB/g2mH6OP+yBBsk47+n99epUFfLeEpSvtMgIjAIARIJKv6YJqLylgQgGiEgomHHscozScbzZ3cwCX1yC4XkRoNLxV8BDoTaPA+pzzwiLggBEios2I5BovKWBCBkaV0+xAleXNGV26Fkh1NEfhId+jlduH+i6RrmGS0h9yAK/gEK+wEKA2liYxIgAPxOwT2yFJxXI/8Idv6GvWyIBY8Ewdp267h1Z2CPWsEaCQgBGAEgASoBACInCAESAQEaIPlW3n8EGa9BCcwZdOHxNyMKvSuMufUnd17syhqIFw8iIiUIARIhAS/MC0o9ReaxZrve5+zg9Lg3N/ExQn9/u0nPvNHN65TwIicIARIBARogB4kZcZwaxFW9Kfkfr3JLQn6u7VQUoBry2RwzaJDe44QiJQgBEiEBZU3gxhmBMil0OVoP2sWrxgO/NX1H5xVkRZ9/bega3uIiJwgBEgEBGiBsNTQ1P6IG1Lf0Q8cm7kZOttqX22tRPkaFF3Atguv5ZA==', + proof_height: { + revision_height: '4381842', + revision_number: '0', + }, + connection_id: 'connection-72', + proof_consensus: + 'CtUICtIICjJjbGllbnRzLzA3LXRlbmRlcm1pbnQtNDUvY29uc2Vuc3VzU3RhdGVzLzItNTc0NDU3NhKGAQouL2liYy5saWdodGNsaWVudHMudGVuZGVybWludC52MS5Db25zZW5zdXNTdGF0ZRJUCgwIn4D+pQYQ9PKBuAMSIgogtPl77yi9LvzW1gkeO+teMVcM1E6qcv7Xp3okSVvtGAcaIMe9/GKdHdD4dPFEauon+h3QEdH1oZ6jP4DEH2ZWn3ROGg4IARgBIAEqBgACovKWBCIsCAESKAQGovKWBCAHjrwjqZJaxGGLEXaH55ia064+0SLWxo3XLQqnH3PnDSAiLAgBEigGDqLylgQgWMaksHVViO8rzGDk6rbntj3jGE+rHofgIGu7zk8ojukgIi4IARIHCBii8pYEIBohIJgUvjsUDyjaulIls+YyOVYO793IQ59F4DtEoCEKLZTfIiwIARIoCjSi8pYEIEzSRrihNkes+Q5IuNa5gpyhkva9yby1DMC1p8VrYlUUICIuCAESBwxGovKWBCAaISDhzb/9iqmZ90/la2ecG8xfrP1g49sNRKF7eHRoZ41hQyIsCAESKA5wovKWBCBbVdBjKBpebgwZFVLIitdMRdO0N0BkfjDLFp9EJMqrNCAiLwgBEggQ0AGi8pYEIBohIJfTNDSJ0SRz/JI1ivIeLCdm3ejk0yl2A7EwNe9AfAYZIi0IARIpEq4DovKWBCBcfXgVQ1yQnurrIPMfFVbCRrCWCaR1pr3rnSgTaTY6OyAiLQgBEikUpgWi8pYEICRprQ6ULikqjibbFFB5VvHS2J4uF9qTpey7lBWn1xcIICItCAESKRbECqLylgQgg5PoFiojmUD7FMFMhE7508cf4ycX/sVegVXPX9wR1WggIi0IARIpGPQPovKWBCDGLCRvs/V82pN1lRJ25iX458N5dREPr1CIW1+M16W7lSAiLQgBEikaqh2i8pYEIC5RzFrOkoX+m2I/PdYQcm6FA0etjFdSiFsieO+IaHMIICItCAESKRzYXqLylgQgaWk0dcwFbfkX/RqivUMEQvlcEQrGFCKCwJM/WmY3KQsgIi4IARIqHtiUAaLylgQg1rVk5tGBWIq3BUT4tXHn3Df+gXamcHSyOUhjI+HAW7YgIi4IARIqINjsAqLylgQg8VA6RoQWxm5zlUIvdcsy+d09UDWkKlBVSqrUwC0JHE4gIi4IARIqIurvBaLylgQgb6+R5G2pSixtdtdy1198x7SgnRXDIun5vonbVS5csE4gIi4IARIqJt7lFqLylgQgx2MIhExidB/g2mH6OP+yBBsk47+n99epUFfLeEpSvtMgIjAIARIJKv6YJqLylgQgGiEgomHHscozScbzZ3cwCX1yC4XkRoNLxV8BDoTaPA+pzzwiLggBEios2I5BovKWBCBkaV0+xAleXNGV26Fkh1NEfhId+jlduH+i6RrmGS0h9yAK/gEK+wEKA2liYxIgAPxOwT2yFJxXI/8Idv6GvWyIBY8Ewdp267h1Z2CPWsEaCQgBGAEgASoBACInCAESAQEaIPlW3n8EGa9BCcwZdOHxNyMKvSuMufUnd17syhqIFw8iIiUIARIhAS/MC0o9ReaxZrve5+zg9Lg3N/ExQn9/u0nPvNHN65TwIicIARIBARogB4kZcZwaxFW9Kfkfr3JLQn6u7VQUoBry2RwzaJDe44QiJQgBEiEBZU3gxhmBMil0OVoP2sWrxgO/NX1H5xVkRZ9/bega3uIiJwgBEgEBGiBsNTQ1P6IG1Lf0Q8cm7kZOttqX22tRPkaFF3Atguv5ZA==', + consensus_height: { + revision_height: '5744576', + revision_number: '2', + }, + counterparty_connection_id: 'connection-27', + }, + }); + await this.crawlIbcTaoSerivce.handleNewIbcConnection([events], trx); + const newConnection = await IbcConnection.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newConnection.connection_id).toEqual(events.attributes[0].value); + expect(newConnection.counterparty_client_id).toEqual( + events.attributes[2].value + ); + expect(newConnection.counterparty_connection_id).toEqual( + events.attributes[3].value + ); + }); + } + + @Test('Test handleNewIbcChannel') + async testHandleNewIbcChannel() { + await knex.transaction(async (trx) => { + const events = Event.fromJson({ + type: Event.EVENT_TYPE.CHANNEL_OPEN_ACK, + attributes: [ + { + value: + 'wasm.aura1s42mq5xz5et3fhs0cxvf8ds6vmy5u6d27u23ydq28r2hgmednw5s3u7kjf', + key: 'port_id', + event_id: '1', + }, + { + value: 'channel-79', + key: 'channel_id', + event_id: '1', + }, + { + value: + 'wasm.nois1xwde9rzqk5u36fke0r9ddmtwvh43n4fv53c5vc462wz8xlnqjhls6d90xc', + key: 'counterparty_port_id', + event_id: '1', + }, + { + value: 'channel-44', + key: 'counterparty_channel_id', + event_id: '1', + }, + { + value: 'connection-72', + key: 'connection_id', + event_id: '1', + }, + ], + content: { + '@type': '/ibc.core.channel.v1.MsgChannelOpenAck', + signer: 'aura1teguu4gyk002q74rdw8xk6wxz663d3e0da44vv', + port_id: + 'wasm.aura1s42mq5xz5et3fhs0cxvf8ds6vmy5u6d27u23ydq28r2hgmednw5s3u7kjf', + proof_try: + 'CqsJCqgJCmpjaGFubmVsRW5kcy9wb3J0cy93YXNtLm5vaXMxeHdkZTlyenFrNXUzNmZrZTByOWRkbXR3dmg0M240ZnY1M2M1dmM0NjJ3ejh4bG5xamhsczZkOTB4Yy9jaGFubmVscy9jaGFubmVsLTQ0EnAIAhABGlIKRHdhc20uYXVyYTFzNDJtcTV4ejVldDNmaHMwY3h2ZjhkczZ2bXk1dTZkMjd1MjN5ZHEyOHIyaGdtZWRudzVzM3U3a2pmEgpjaGFubmVsLTc5Ig1jb25uZWN0aW9uLTIwKgdub2lzLXY3Gg4IARgBIAEqBgACqNbwAyIsCAESKAIEqNbwAyApVTv4ZHdG03r0dv2FLRgljarFXhPCzLxe5U794Dxg7iAiLAgBEigEBqjW8AMgbTQCakVAjVARrysKAAPM1e1tiTHofZa81TN6VdrZryMgIiwIARIoBgqo1vADIH+PzOA8CN5w3mj/yRWJffIKzoh1l9E7sKZ5XNBpk36XICIuCAESBwgQqNbwAyAaISDJYlIFbAyC1KK1ZZBsZZFdBLoWBTAPcvdp1IOv5E9HLSIuCAESBwowqNbwAyAaISCEaKaAA8yFOHGODccYN+9HuImlZxYWqoTxIN/J8kBiayIsCAESKAxUqNbwAyD3SAJRP1mO4LVWbHvRyD2webWn9TkNbkJZy7jIPzhrMiAiLwgBEggO1AGo1vADIBohICKuXv3N+wd3GBXhJZ27wd4v6vIL8XZUP8OoZE/aOUysIi0IARIpEO4CqNbwAyBgaG7cgNsElUcgrf9Mp2Gmzo0/AsmRHPw5FTwX9Kr0MSAiLQgBEikSqASo1vADIFQDyXDfKzS3OL1ZkYfELpPLhFtz8T5WII7GUvojwrwsICItCAESKRScBqjW8AMgfo0r4iAG6Q26x4Y6zuXHZJc5TgywPhYCp408vX7zDBQgIi0IARIpFtIMqNbwAyDqu2q/0ILlZYXdrphZJmwZtcf8/aHxsqR3dX84m9TTeiAiLQgBEikYgBqo1vADIIPtczfmGBBRreb3CDJB7Wm0ebRH+HR88bBCcXaUupjcICIvCAESCBrAJ6jW8AMgGiEgcpa1SruIq6LeCV4xG7rX2arPRTP/iEbXQ8D5WnWUzwAiLwgBEggc1mGo1vADIBohIDXxpaT6Ugv6U6oOS5S0EDChcoYZW0+my6CUTsXNzvvGIi4IARIqHoqRAajW8AMgBRWQCNEouEeVb1Jgy9Tt+qq891z0wHVNBzVj20RLaGggIi4IARIqItjHAqjW8AMgUvOkgI+dIkIugdSRweG9eleBh/pswvTETxdw6FY4uTwgIjAIARIJJPDCDajW8AMgGiEgyhYnMC8oHxxof8aeyKInF9dJIbxw7e5YkEFV7dhRRWMiLggBEiom8rYSqNbwAyD0JrFmOhwOvx0p9bY869JVWAT2YOnKOMAZ2aBBy0VO/CAiLggBEioosvQZqNbwAyCz9+zU1Wu3buyW7azM+yVxZPfqMxKFHWdwLoOIS5JhYyAiMAgBEgkq9oM/qNbwAyAaISAPNNwvlr7Cghq7eoYfeWqJEKPOMwjXnB66fWb3O1uckQr+AQr7AQoDaWJjEiDfSyEEYUNHoexjzxEoS8ca//0GRWkbX/BhVhlfyDT5zBoJCAEYASABKgEAIicIARIBARog+VbefwQZr0EJzBl04fE3Iwq9K4y59Sd3XuzKGogXDyIiJQgBEiEBL8wLSj1F5rFmu97n7OD0uDc38TFCf3+7Sc+80c3rlPAiJwgBEgEBGiDFznslp7K5oVZJG04oLeQXNU3ws7wFeEH4qB+XS4Q9zyIlCAESIQEtpn507xvLNnr4cRXICIMm+qvT86qeswagi/7S7aB1riInCAESAQEaILASAaS4z/TvmnniGlkXMUOeTYKHWs4RWU6MlCMFBTA3', + channel_id: 'channel-79', + proof_height: { + revision_height: '4068757', + revision_number: '0', + }, + counterparty_version: 'nois-v7', + counterparty_channel_id: 'channel-44', + }, + }); + await this.crawlIbcTaoSerivce.handleNewIbcChannel([events], trx); + const newChannel = await IbcChannel.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newChannel.channel_id).toEqual(events.attributes[1].value); + expect(newChannel.counterparty_channel_id).toEqual( + events.attributes[3].value + ); + expect(newChannel.counterparty_port_id).toEqual( + events.attributes[2].value + ); + expect(newChannel.port_id).toEqual(events.attributes[0].value); + expect(newChannel.state).toEqual(IbcChannel.STATUS.OPEN); + }); + } + + @Test('Test handleCloseIbcChannel') + async testHandleCloseIbcChannel() { + await knex.transaction(async (trx) => { + const events = Event.fromJson({ + type: Event.EVENT_TYPE.CHANNEL_CLOSE_CONFIRM, + attributes: [ + { + value: + 'wasm.aura1s42mq5xz5et3fhs0cxvf8ds6vmy5u6d27u23ydq28r2hgmednw5s3u7kjf', + key: 'port_id', + event_id: '1', + }, + { + value: 'channel-79', + key: 'channel_id', + event_id: '1', + }, + { + value: + 'wasm.nois1xwde9rzqk5u36fke0r9ddmtwvh43n4fv53c5vc462wz8xlnqjhls6d90xc', + key: 'counterparty_port_id', + event_id: '1', + }, + { + value: 'channel-44', + key: 'counterparty_channel_id', + event_id: '1', + }, + { + value: 'connection-72', + key: 'connection_id', + event_id: '1', + }, + ], + content: { + '@type': '/ibc.core.channel.v1.MsgChannelOpenAck', + signer: 'aura1teguu4gyk002q74rdw8xk6wxz663d3e0da44vv', + port_id: + 'wasm.aura1s42mq5xz5et3fhs0cxvf8ds6vmy5u6d27u23ydq28r2hgmednw5s3u7kjf', + proof_try: + 'CqsJCqgJCmpjaGFubmVsRW5kcy9wb3J0cy93YXNtLm5vaXMxeHdkZTlyenFrNXUzNmZrZTByOWRkbXR3dmg0M240ZnY1M2M1dmM0NjJ3ejh4bG5xamhsczZkOTB4Yy9jaGFubmVscy9jaGFubmVsLTQ0EnAIAhABGlIKRHdhc20uYXVyYTFzNDJtcTV4ejVldDNmaHMwY3h2ZjhkczZ2bXk1dTZkMjd1MjN5ZHEyOHIyaGdtZWRudzVzM3U3a2pmEgpjaGFubmVsLTc5Ig1jb25uZWN0aW9uLTIwKgdub2lzLXY3Gg4IARgBIAEqBgACqNbwAyIsCAESKAIEqNbwAyApVTv4ZHdG03r0dv2FLRgljarFXhPCzLxe5U794Dxg7iAiLAgBEigEBqjW8AMgbTQCakVAjVARrysKAAPM1e1tiTHofZa81TN6VdrZryMgIiwIARIoBgqo1vADIH+PzOA8CN5w3mj/yRWJffIKzoh1l9E7sKZ5XNBpk36XICIuCAESBwgQqNbwAyAaISDJYlIFbAyC1KK1ZZBsZZFdBLoWBTAPcvdp1IOv5E9HLSIuCAESBwowqNbwAyAaISCEaKaAA8yFOHGODccYN+9HuImlZxYWqoTxIN/J8kBiayIsCAESKAxUqNbwAyD3SAJRP1mO4LVWbHvRyD2webWn9TkNbkJZy7jIPzhrMiAiLwgBEggO1AGo1vADIBohICKuXv3N+wd3GBXhJZ27wd4v6vIL8XZUP8OoZE/aOUysIi0IARIpEO4CqNbwAyBgaG7cgNsElUcgrf9Mp2Gmzo0/AsmRHPw5FTwX9Kr0MSAiLQgBEikSqASo1vADIFQDyXDfKzS3OL1ZkYfELpPLhFtz8T5WII7GUvojwrwsICItCAESKRScBqjW8AMgfo0r4iAG6Q26x4Y6zuXHZJc5TgywPhYCp408vX7zDBQgIi0IARIpFtIMqNbwAyDqu2q/0ILlZYXdrphZJmwZtcf8/aHxsqR3dX84m9TTeiAiLQgBEikYgBqo1vADIIPtczfmGBBRreb3CDJB7Wm0ebRH+HR88bBCcXaUupjcICIvCAESCBrAJ6jW8AMgGiEgcpa1SruIq6LeCV4xG7rX2arPRTP/iEbXQ8D5WnWUzwAiLwgBEggc1mGo1vADIBohIDXxpaT6Ugv6U6oOS5S0EDChcoYZW0+my6CUTsXNzvvGIi4IARIqHoqRAajW8AMgBRWQCNEouEeVb1Jgy9Tt+qq891z0wHVNBzVj20RLaGggIi4IARIqItjHAqjW8AMgUvOkgI+dIkIugdSRweG9eleBh/pswvTETxdw6FY4uTwgIjAIARIJJPDCDajW8AMgGiEgyhYnMC8oHxxof8aeyKInF9dJIbxw7e5YkEFV7dhRRWMiLggBEiom8rYSqNbwAyD0JrFmOhwOvx0p9bY869JVWAT2YOnKOMAZ2aBBy0VO/CAiLggBEioosvQZqNbwAyCz9+zU1Wu3buyW7azM+yVxZPfqMxKFHWdwLoOIS5JhYyAiMAgBEgkq9oM/qNbwAyAaISAPNNwvlr7Cghq7eoYfeWqJEKPOMwjXnB66fWb3O1uckQr+AQr7AQoDaWJjEiDfSyEEYUNHoexjzxEoS8ca//0GRWkbX/BhVhlfyDT5zBoJCAEYASABKgEAIicIARIBARog+VbefwQZr0EJzBl04fE3Iwq9K4y59Sd3XuzKGogXDyIiJQgBEiEBL8wLSj1F5rFmu97n7OD0uDc38TFCf3+7Sc+80c3rlPAiJwgBEgEBGiDFznslp7K5oVZJG04oLeQXNU3ws7wFeEH4qB+XS4Q9zyIlCAESIQEtpn507xvLNnr4cRXICIMm+qvT86qeswagi/7S7aB1riInCAESAQEaILASAaS4z/TvmnniGlkXMUOeTYKHWs4RWU6MlCMFBTA3', + channel_id: 'channel-79', + proof_height: { + revision_height: '4068757', + revision_number: '0', + }, + counterparty_version: 'nois-v7', + counterparty_channel_id: 'channel-44', + }, + }); + await this.crawlIbcTaoSerivce.handleCloseIbcChannel([events], trx); + const newChannel = await IbcChannel.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newChannel.state).toEqual(IbcChannel.STATUS.CLOSE); + }); + } +} From 7b8d852065ea5410103a83c6fc06b8ece569c3e8 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Tue, 29 Aug 2023 09:17:18 +0700 Subject: [PATCH 45/54] refactor: code --- src/common/utils/utils.ts | 11 -------- src/models/event.ts | 7 +++++ src/services/ibc/crawl_ibc_ics20.service.ts | 31 +++++++++++---------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/common/utils/utils.ts b/src/common/utils/utils.ts index da5a16690..8563cf71f 100644 --- a/src/common/utils/utils.ts +++ b/src/common/utils/utils.ts @@ -210,14 +210,3 @@ export default class Utils { return [response, heightRange]; } } - -// get Attribute value by specified key from array of attributes -export function getAttributesFrom( - listAttributes: any, - attributesType: string[] -) { - return attributesType.map( - (attributeType) => - listAttributes?.find((attr: any) => attr.key === attributeType)?.value - ); -} diff --git a/src/models/event.ts b/src/models/event.ts index 13dfe8e3e..c0f7a384d 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -77,6 +77,13 @@ export class Event extends BaseModel { }; } + getAttributesFrom(attributesType: string[]) { + return attributesType.map( + (attributeType) => + this.attributes?.find((attr: any) => attr.key === attributeType)?.value + ); + } + static EVENT_TYPE = { STORE_CODE: 'store_code', SUBMIT_PROPOSAL: 'submit_proposal', diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 38ee3eaf1..2b391edea 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -11,7 +11,6 @@ import { IbcIcs20, IbcMessage, } from '../../models'; -import { getAttributesFrom } from '../../common/utils/utils'; const PORT = config.crawlIbcIcs20.port; @Service({ @@ -106,13 +105,16 @@ export default class CrawlIBCIcs20Service extends BullableService { throw Error(`Recv ibc hasn't emmitted events: ${msg.id}`); } const [sender, receiver, amount, originalDenom, ackStatus] = - getAttributesFrom(recvEvent.attributes, [ + recvEvent.getAttributesFrom([ EventAttribute.ATTRIBUTE_KEY.SENDER, EventAttribute.ATTRIBUTE_KEY.RECEIVER, EventAttribute.ATTRIBUTE_KEY.AMOUNT, EventAttribute.ATTRIBUTE_KEY.DENOM, EventAttribute.ATTRIBUTE_KEY.SUCCESS, ]); + if (originalDenom === undefined) { + throw Error(`Recv ibc hasn't emit denom: ${ msg.id}`); + } const denomTraceEvent = msg.message.events.find( (e) => e.type === IbcIcs20.EVENT_TYPE.DENOM_TRACE ); @@ -160,16 +162,18 @@ export default class CrawlIBCIcs20Service extends BullableService { if (ackEvents.length !== 2) { throw Error(`Ack ibc hasn't emmitted enough events: ${msg.id}`); } - const [sender, receiver, amount, denom, success] = getAttributesFrom( - [...ackEvents[0].attributes, ...ackEvents[1].attributes], - [ + const [sender, receiver, amount, denom, success] = [ + ...ackEvents[0].getAttributesFrom([ EventAttribute.ATTRIBUTE_KEY.SENDER, EventAttribute.ATTRIBUTE_KEY.RECEIVER, EventAttribute.ATTRIBUTE_KEY.AMOUNT, EventAttribute.ATTRIBUTE_KEY.DENOM, + ]), + ...ackEvents[1].getAttributesFrom([ EventAttribute.ATTRIBUTE_KEY.SUCCESS, - ] - ); + ]), + ]; + return IbcIcs20.fromJson({ ibc_message_id: msg.id, sender, @@ -205,14 +209,11 @@ export default class CrawlIBCIcs20Service extends BullableService { if (ics20Timeouts.length > 0) { const ibcIcs20s = ics20Timeouts.map((msg) => { const timeoutEvent = msg.message.events[0]; - const [receiver, amount, denom] = getAttributesFrom( - timeoutEvent.attributes, - [ - EventAttribute.ATTRIBUTE_KEY.REFUND_RECEIVER, - EventAttribute.ATTRIBUTE_KEY.REFUND_AMOUNT, - EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM, - ] - ); + const [receiver, amount, denom] = timeoutEvent.getAttributesFrom([ + EventAttribute.ATTRIBUTE_KEY.REFUND_RECEIVER, + EventAttribute.ATTRIBUTE_KEY.REFUND_AMOUNT, + EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM, + ]); return IbcIcs20.fromJson({ ibc_message_id: msg.id, receiver, From a5f4afc4cd4a122adeb6ee96838f31356a29ae19 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Tue, 29 Aug 2023 11:26:01 +0700 Subject: [PATCH 46/54] fix: test --- test/unit/services/ibc/crawl_ibc_ics20.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index 853fd7f3e..4fc4aeee8 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -390,7 +390,7 @@ export default class CrawlIbcIcs20Test { type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, block_height: this.block.height, source: 'TX_EVENT', - attributes: event2Attrs.map((e, index) => { + attributes: event1Attrs.map((e, index) => { Object.assign(e, { block_height: this.block.height, event_id: 1, @@ -405,7 +405,7 @@ export default class CrawlIbcIcs20Test { type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, block_height: this.block.height, source: 'TX_EVENT', - attributes: event1Attrs.map((e, index) => { + attributes: event2Attrs.map((e, index) => { Object.assign(e, { block_height: this.block.height, event_id: 1, From 0c7e6030e1656008e4e74611ca532bc6d20b3c5e Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Tue, 29 Aug 2023 14:15:09 +0700 Subject: [PATCH 47/54] refactor: code --- migrations/20230823070516_ics20_model.ts | 2 +- src/models/ibc_ics20.ts | 2 +- src/models/ibc_message.ts | 5 ++++ src/services/ibc/crawl_ibc_ics20.service.ts | 15 +++++----- .../unit/services/ibc/crawl_ibc_ics20.spec.ts | 30 +++++++++++++------ 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/migrations/20230823070516_ics20_model.ts b/migrations/20230823070516_ics20_model.ts index ed3937b9e..c8428453b 100644 --- a/migrations/20230823070516_ics20_model.ts +++ b/migrations/20230823070516_ics20_model.ts @@ -8,7 +8,7 @@ export async function up(knex: Knex): Promise { table.string('receiver').index().notNullable(); table.decimal('amount', 80, 0).notNullable(); table.string('denom').notNullable().index(); - table.boolean('ack_status'); + table.boolean('status').defaultTo(true); table.foreign('ibc_message_id').references('ibc_message.id'); }); } diff --git a/src/models/ibc_ics20.ts b/src/models/ibc_ics20.ts index f33eb829b..540fde04a 100644 --- a/src/models/ibc_ics20.ts +++ b/src/models/ibc_ics20.ts @@ -16,7 +16,7 @@ export class IbcIcs20 extends BaseModel { denom!: string; - ack_status!: boolean; + status!: boolean; ibc_message!: IbcMessage; diff --git a/src/models/ibc_message.ts b/src/models/ibc_message.ts index 6f96ded28..79b73b232 100644 --- a/src/models/ibc_message.ts +++ b/src/models/ibc_message.ts @@ -3,6 +3,7 @@ import { Model } from 'objection'; import BaseModel from './base'; import { IbcChannel } from './ibc_channel'; import { TransactionMessage } from './transaction_message'; +import config from '../../config.json' assert { type: 'json' }; export class IbcMessage extends BaseModel { id!: number; @@ -92,4 +93,8 @@ export class IbcMessage extends BaseModel { ACKNOWLEDGE_PACKET: 'acknowledge_packet', TIMEOUT_PACKET: 'timeout_packet', }; + + static PORTS = { + ICS20: config.crawlIbcIcs20.port, + }; } diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 2b391edea..b4ba7913d 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -12,7 +12,6 @@ import { IbcMessage, } from '../../models'; -const PORT = config.crawlIbcIcs20.port; @Service({ name: SERVICE.V1.CrawlIBCIcs20Service.key, version: 1, @@ -58,7 +57,7 @@ export default class CrawlIBCIcs20Service extends BullableService { ) { const ics20Sends = await IbcMessage.query() .joinRelated('message.transaction') - .where('src_port_id', PORT) + .where('src_port_id', IbcMessage.PORTS.ICS20) .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.SEND_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) @@ -90,7 +89,7 @@ export default class CrawlIBCIcs20Service extends BullableService { .orWhere('type', IbcIcs20.EVENT_TYPE.DENOM_TRACE); }, }) - .where('dst_port_id', PORT) + .where('dst_port_id', IbcMessage.PORTS.ICS20) .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.RECV_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) @@ -113,7 +112,7 @@ export default class CrawlIBCIcs20Service extends BullableService { EventAttribute.ATTRIBUTE_KEY.SUCCESS, ]); if (originalDenom === undefined) { - throw Error(`Recv ibc hasn't emit denom: ${ msg.id}`); + throw Error(`Recv ibc hasn't emit denom: ${msg.id}`); } const denomTraceEvent = msg.message.events.find( (e) => e.type === IbcIcs20.EVENT_TYPE.DENOM_TRACE @@ -130,7 +129,7 @@ export default class CrawlIBCIcs20Service extends BullableService { receiver, amount, denom, - ack_status: ackStatus === 'true', + status: ackStatus === 'true', }); }); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -150,7 +149,7 @@ export default class CrawlIBCIcs20Service extends BullableService { builder.where('type', IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET); }, }) - .where('src_port_id', PORT) + .where('src_port_id', IbcMessage.PORTS.ICS20) .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) @@ -180,7 +179,7 @@ export default class CrawlIBCIcs20Service extends BullableService { receiver, amount, denom, - ack_status: success !== undefined, + status: success !== undefined, }); }); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -200,7 +199,7 @@ export default class CrawlIBCIcs20Service extends BullableService { builder.where('type', IbcIcs20.EVENT_TYPE.TIMEOUT); }, }) - .where('src_port_id', PORT) + .where('src_port_id', IbcMessage.PORTS.ICS20) .andWhere('ibc_message.type', IbcMessage.EVENT_TYPE.TIMEOUT_PACKET) .andWhere('message:transaction.height', '>', startHeight) .andWhere('message:transaction.height', '<=', endHeight) diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index 4fc4aeee8..dc85151e1 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -98,18 +98,21 @@ export default class CrawlIbcIcs20Test { sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', }, }); - await IbcMessage.query().insert(ibcMessage).transacting(trx); + const message = await IbcMessage.query() + .insert(ibcMessage) + .transacting(trx); await this.crawlIbcIcs20Serivce.handleIcs20Send( this.block.height - 1, this.block.height, trx ); const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.ibc_message_id).toEqual(message.id); expect(result?.sender).toEqual(ibcMessage.data.sender); expect(result?.receiver).toEqual(ibcMessage.data.receiver); expect(result?.amount).toEqual(ibcMessage.data.amount); expect(result?.denom).toEqual(ibcMessage.data.denom); - expect(result?.ack_status).toBeNull(); + expect(result?.status).toEqual(true); await trx.rollback(); }); } @@ -134,7 +137,9 @@ export default class CrawlIbcIcs20Test { sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', }, }); - await IbcMessage.query().insert(ibcMessage).transacting(trx); + const ibcMsg = await IbcMessage.query() + .insert(ibcMessage) + .transacting(trx); const event1Attrs = [ { key: 'module', @@ -216,6 +221,7 @@ export default class CrawlIbcIcs20Test { trx ); const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.ibc_message_id).toEqual(ibcMsg.id); expect(result?.receiver).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) ); @@ -230,7 +236,7 @@ export default class CrawlIbcIcs20Test { ibcMessage.dst_channel_id }/${getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` ); - expect(result?.ack_status).toEqual(true); + expect(result?.status).toEqual(true); await trx.rollback(); }); } @@ -255,7 +261,9 @@ export default class CrawlIbcIcs20Test { sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', }, }); - await IbcMessage.query().insert(ibcMessage).transacting(trx); + const ibcMsg = await IbcMessage.query() + .insert(ibcMessage) + .transacting(trx); const event1Attrs = [ { key: 'module', @@ -310,6 +318,7 @@ export default class CrawlIbcIcs20Test { trx ); const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.ibc_message_id).toEqual(ibcMsg.id); expect(result?.receiver).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) ); @@ -320,7 +329,7 @@ export default class CrawlIbcIcs20Test { getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) ); expect(result?.denom).toEqual('uatom'); - expect(result?.ack_status).toEqual(true); + expect(result?.status).toEqual(true); await trx.rollback(); }); } @@ -434,7 +443,7 @@ export default class CrawlIbcIcs20Test { expect(result?.denom).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM) ); - expect(result?.ack_status).toEqual(false); + expect(result?.status).toEqual(false); await trx.rollback(); }); } @@ -459,7 +468,9 @@ export default class CrawlIbcIcs20Test { sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', }, }); - await IbcMessage.query().insert(ibcMessage).transacting(trx); + const ibcMsg = await IbcMessage.query() + .insert(ibcMessage) + .transacting(trx); const event1Attrs = [ { key: 'module', @@ -506,6 +517,7 @@ export default class CrawlIbcIcs20Test { trx ); const result = await IbcIcs20.query().first().transacting(trx); + expect(result?.ibc_message_id).toEqual(ibcMsg.id); expect(result?.receiver).toEqual( getAttributeFrom( event1Attrs, @@ -522,7 +534,7 @@ export default class CrawlIbcIcs20Test { expect(result?.denom).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM) ); - expect(result?.ack_status).toBeNull(); + expect(result?.status).toBe(true); await trx.rollback(); }); } From e37e21088f7cdb172aadaa8bdc0877cd126469b7 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Tue, 29 Aug 2023 14:27:26 +0700 Subject: [PATCH 48/54] fix: merge code --- src/common/utils/utils.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/common/utils/utils.ts b/src/common/utils/utils.ts index da5a16690..8563cf71f 100644 --- a/src/common/utils/utils.ts +++ b/src/common/utils/utils.ts @@ -210,14 +210,3 @@ export default class Utils { return [response, heightRange]; } } - -// get Attribute value by specified key from array of attributes -export function getAttributesFrom( - listAttributes: any, - attributesType: string[] -) { - return attributesType.map( - (attributeType) => - listAttributes?.find((attr: any) => attr.key === attributeType)?.value - ); -} From 2da7bd41de25110454a68bb108b030be964492a4 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Tue, 5 Sep 2023 15:11:30 +0700 Subject: [PATCH 49/54] refactor: review code --- migrations/20230823070516_ics20_model.ts | 1 + src/models/ibc_ics20.ts | 14 +++++++++++++- src/services/ibc/crawl_ibc_ics20.service.ts | 14 ++++++++++---- test/unit/services/ibc/crawl_ibc_ics20.spec.ts | 5 +++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/migrations/20230823070516_ics20_model.ts b/migrations/20230823070516_ics20_model.ts index c8428453b..6f2a523ce 100644 --- a/migrations/20230823070516_ics20_model.ts +++ b/migrations/20230823070516_ics20_model.ts @@ -9,6 +9,7 @@ export async function up(knex: Knex): Promise { table.decimal('amount', 80, 0).notNullable(); table.string('denom').notNullable().index(); table.boolean('status').defaultTo(true); + table.string('channel_id').notNullable().index(); table.foreign('ibc_message_id').references('ibc_message.id'); }); } diff --git a/src/models/ibc_ics20.ts b/src/models/ibc_ics20.ts index 540fde04a..b2a1e97f8 100644 --- a/src/models/ibc_ics20.ts +++ b/src/models/ibc_ics20.ts @@ -2,6 +2,7 @@ import { Model } from 'objection'; import BaseModel from './base'; import { IbcMessage } from './ibc_message'; +import { IbcChannel } from './ibc_channel'; export class IbcIcs20 extends BaseModel { id!: number; @@ -18,6 +19,8 @@ export class IbcIcs20 extends BaseModel { status!: boolean; + channel_id!: string; + ibc_message!: IbcMessage; static get tableName() { @@ -27,12 +30,13 @@ export class IbcIcs20 extends BaseModel { static get jsonSchema() { return { type: 'object', - required: ['receiver', 'amount', 'denom', 'ibc_message_id'], + required: ['receiver', 'amount', 'denom', 'ibc_message_id', 'channel_id'], properties: { receiver: { type: 'string' }, denom: { type: 'string' }, ibc_message_id: { type: 'number' }, amount: { type: 'string' }, + channel_id: { type: 'string' }, }, }; } @@ -47,6 +51,14 @@ export class IbcIcs20 extends BaseModel { to: 'ibc_message.id', }, }, + channel: { + relation: Model.BelongsToOneRelation, + modelClass: IbcChannel, + join: { + from: 'ibc_ics20.channel_id', + to: 'ibc_channel.channel_id', + }, + }, }; } diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index b4ba7913d..082be84ae 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -68,6 +68,7 @@ export default class CrawlIBCIcs20Service extends BullableService { IbcIcs20.fromJson({ ibc_message_id: msg.id, ...msg.data, + channel_id: msg.src_channel_id, }) ); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -114,12 +115,13 @@ export default class CrawlIBCIcs20Service extends BullableService { if (originalDenom === undefined) { throw Error(`Recv ibc hasn't emit denom: ${msg.id}`); } - const denomTraceEvent = msg.message.events.find( - (e) => e.type === IbcIcs20.EVENT_TYPE.DENOM_TRACE - ); + const isSource = + msg.message.events.find( + (e) => e.type === IbcIcs20.EVENT_TYPE.DENOM_TRACE + ) === undefined; const denom = this.parseDenom( originalDenom, - denomTraceEvent === undefined, + isSource, msg.dst_port_id, msg.dst_channel_id ); @@ -130,6 +132,7 @@ export default class CrawlIBCIcs20Service extends BullableService { amount, denom, status: ackStatus === 'true', + channel_id: msg.dst_channel_id, }); }); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -180,6 +183,7 @@ export default class CrawlIBCIcs20Service extends BullableService { amount, denom, status: success !== undefined, + channel_id: msg.src_channel_id, }); }); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -218,6 +222,8 @@ export default class CrawlIBCIcs20Service extends BullableService { receiver, amount, denom, + channel_id: msg.src_channel_id, + status: false, }); }); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index dc85151e1..59243360f 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -113,6 +113,7 @@ export default class CrawlIbcIcs20Test { expect(result?.amount).toEqual(ibcMessage.data.amount); expect(result?.denom).toEqual(ibcMessage.data.denom); expect(result?.status).toEqual(true); + expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); await trx.rollback(); }); } @@ -237,6 +238,7 @@ export default class CrawlIbcIcs20Test { }/${getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` ); expect(result?.status).toEqual(true); + expect(result?.channel_id).toEqual(ibcMessage.dst_channel_id); await trx.rollback(); }); } @@ -330,6 +332,7 @@ export default class CrawlIbcIcs20Test { ); expect(result?.denom).toEqual('uatom'); expect(result?.status).toEqual(true); + expect(result?.channel_id).toEqual(ibcMessage.dst_channel_id); await trx.rollback(); }); } @@ -444,6 +447,7 @@ export default class CrawlIbcIcs20Test { getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM) ); expect(result?.status).toEqual(false); + expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); await trx.rollback(); }); } @@ -535,6 +539,7 @@ export default class CrawlIbcIcs20Test { getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM) ); expect(result?.status).toBe(true); + expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); await trx.rollback(); }); } From 8c075f2d6b3dc7108f0393df0c23b2c07fdbe181 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Tue, 5 Sep 2023 15:15:21 +0700 Subject: [PATCH 50/54] refactor: review code --- test/unit/services/ibc/crawl_ibc_ics20.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index 59243360f..fbc76b340 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -538,7 +538,7 @@ export default class CrawlIbcIcs20Test { expect(result?.denom).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM) ); - expect(result?.status).toBe(true); + expect(result?.status).toBe(false); expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); await trx.rollback(); }); From e19387adc82f7ffd5af13290aba67664b61f3080 Mon Sep 17 00:00:00 2001 From: Tuan Phan Anh <38557844+fibonacci998@users.noreply.github.com> Date: Fri, 8 Sep 2023 09:16:44 +0700 Subject: [PATCH 51/54] Fix/lint json file (#359) * fix: display json on multiple lines * fix: update launch.json to debug jest in 1 file; use moleculer.config.ts when debug service --- .vscode/launch.json | 18 +- .../crawl-transaction/tx.fixture.json | 257 +- .../crawl-transaction/tx_authz.fixture.json | 2686 ++++++++++++++++- .../handle-vote/tx_multi_vote.fixture.json | 166 +- .../services/handle-vote/tx_vote.fixture.json | 134 +- .../handle-vote/tx_vote_authz.fixture.json | 136 +- .../tx_vote_change_option_no.fixture.json | 134 +- .../tx_vote_change_option_yes.fixture.json | 134 +- 8 files changed, 3657 insertions(+), 8 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 51733a2df..db2d91d23 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,7 +14,12 @@ "sourceMaps": true, "runtimeArgs": ["-r", "ts-node/register"], "cwd": "${workspaceRoot}", - "args": ["--env", "src/services/**/*.service.ts"] + "args": [ + "--env", + "src/services/**/*.service.ts", + "--config", + "src/moleculer.config.ts" + ] }, { "type": "node", @@ -26,6 +31,17 @@ "cwd": "${workspaceRoot}", "runtimeArgs": ["--inspect-brk"] }, + { + "type": "node", + "request": "launch", + "name": "Jest current file", + "runtimeExecutable": "ts-node", + "program": "${workspaceRoot}/node_modules/jest-cli/bin/jest.js", + "args": [ + "${fileBasename}" + ], + "internalConsoleOptions": "neverOpen" + }, { "name": "Attach by Process ID", "processId": "${command:PickProcess}", diff --git a/test/unit/services/crawl-transaction/tx.fixture.json b/test/unit/services/crawl-transaction/tx.fixture.json index 0627d975c..be840b8e9 100644 --- a/test/unit/services/crawl-transaction/tx.fixture.json +++ b/test/unit/services/crawl-transaction/tx.fixture.json @@ -1 +1,256 @@ -{"txs":[{"hash":"5F38B0C3E9FAB4423C37FB6306AC06D983AF50013BC7BCFBD9F684D6BFB0AF23","height":"423136","index":0,"tx_result":{"code":0,"data":"CjkKNy9jb3Ntb3MuZGlzdHJpYnV0aW9uLnYxYmV0YTEuTXNnV2l0aGRyYXdEZWxlZ2F0b3JSZXdhcmQKPQo7L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd1ZhbGlkYXRvckNvbW1pc3Npb24=","log":"[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"},{\"key\":\"amount\",\"value\":\"340104uaura\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"340104uaura\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"module\",\"value\":\"distribution\"},{\"key\":\"sender\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"340104uaura\"}]},{\"type\":\"withdraw_rewards\",\"attributes\":[{\"key\":\"amount\",\"value\":\"340104uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper12pku6jshmeekleg5sfhdt50a4sxjlrhumumtwz\"}]}]},{\"msg_index\":1,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"},{\"key\":\"amount\",\"value\":\"223509uaura\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"223509uaura\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"module\",\"value\":\"distribution\"},{\"key\":\"sender\",\"value\":\"auravaloper12pku6jshmeekleg5sfhdt50a4sxjlrhumumtwz\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"223509uaura\"}]},{\"type\":\"withdraw_commission\",\"attributes\":[{\"key\":\"amount\",\"value\":\"223509uaura\"}]}]}]","info":"","gas_wanted":"200000","gas_used":"142624","events":[{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true},{"key":"YW1vdW50","value":"MjAwdWF1cmE=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==","index":true},{"key":"YW1vdW50","value":"MjAwdWF1cmE=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true},{"key":"YW1vdW50","value":"MjAwdWF1cmE=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true}]},{"type":"tx","attributes":[{"key":"ZmVl","value":"MjAwdWF1cmE=","index":true},{"key":"ZmVlX3BheWVy","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true}]},{"type":"tx","attributes":[{"key":"YWNjX3NlcQ==","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdS8yNDQ5","index":true}]},{"type":"tx","attributes":[{"key":"c2lnbmF0dXJl","value":"QjFRczdlZ2NMU0laWkgzMkErNzF2a0FzRjBTeG42emtsNHFuSnh2OGVmb2dtNVJydHJZTFY4cDFJdVJTSnp5eVAxTDVKZUJEQ1JjaHFERGlrQmJ6L3c9PQ==","index":true}]},{"type":"message","attributes":[{"key":"YWN0aW9u","value":"L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd0RlbGVnYXRvclJld2FyZA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MzQwMTA0dWF1cmE=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true},{"key":"YW1vdW50","value":"MzQwMTA0dWF1cmE=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MzQwMTA0dWF1cmE=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MzQwMTA0dWF1cmE=","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxMnBrdTZqc2htZWVrbGVnNXNmaGR0NTBhNHN4amxyaHVtdW10d3o=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"ZGlzdHJpYnV0aW9u","index":true},{"key":"c2VuZGVy","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true}]},{"type":"message","attributes":[{"key":"YWN0aW9u","value":"L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd1ZhbGlkYXRvckNvbW1pc3Npb24=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MjIzNTA5dWF1cmE=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true},{"key":"YW1vdW50","value":"MjIzNTA5dWF1cmE=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MjIzNTA5dWF1cmE=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true}]},{"type":"withdraw_commission","attributes":[{"key":"YW1vdW50","value":"MjIzNTA5dWF1cmE=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"ZGlzdHJpYnV0aW9u","index":true},{"key":"c2VuZGVy","value":"YXVyYXZhbG9wZXIxMnBrdTZqc2htZWVrbGVnNXNmaGR0NTBhNHN4amxyaHVtdW10d3o=","index":true}]}],"codespace":""},"tx":"CpQCCpwBCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEmEKK2F1cmExMnBrdTZqc2htZWVrbGVnNXNmaGR0NTBhNHN4amxyaHVxdzJya3USMmF1cmF2YWxvcGVyMTJwa3U2anNobWVla2xlZzVzZmhkdDUwYTRzeGpscmh1bXVtdHd6CnMKOy9jb3Ntb3MuZGlzdHJpYnV0aW9uLnYxYmV0YTEuTXNnV2l0aGRyYXdWYWxpZGF0b3JDb21taXNzaW9uEjQKMmF1cmF2YWxvcGVyMTJwa3U2anNobWVla2xlZzVzZmhkdDUwYTRzeGpscmh1bXVtdHd6EmcKUQpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOxEYuQv4wegGfSTUuqovipYdmXvu6PJBro71MKqDe8/RIECgIIARiRExISCgwKBXVhdXJhEgMyMDAQwJoMGkAHVCzt6BwtIhlkffYD7vW+QCwXRLGfrOSXiqcnG/x5+iCblGu2tgtXynUi5FInPLI/Uvkl4EMJFyGoMOKQFvP/"}],"total_count":"1"} \ No newline at end of file +{ + "txs": [ + { + "hash": "5F38B0C3E9FAB4423C37FB6306AC06D983AF50013BC7BCFBD9F684D6BFB0AF23", + "height": "423136", + "index": 0, + "tx_result": { + "code": 0, + "data": "CjkKNy9jb3Ntb3MuZGlzdHJpYnV0aW9uLnYxYmV0YTEuTXNnV2l0aGRyYXdEZWxlZ2F0b3JSZXdhcmQKPQo7L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd1ZhbGlkYXRvckNvbW1pc3Npb24=", + "log": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"},{\"key\":\"amount\",\"value\":\"340104uaura\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"340104uaura\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"module\",\"value\":\"distribution\"},{\"key\":\"sender\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"340104uaura\"}]},{\"type\":\"withdraw_rewards\",\"attributes\":[{\"key\":\"amount\",\"value\":\"340104uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper12pku6jshmeekleg5sfhdt50a4sxjlrhumumtwz\"}]}]},{\"msg_index\":1,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"},{\"key\":\"amount\",\"value\":\"223509uaura\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"223509uaura\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"module\",\"value\":\"distribution\"},{\"key\":\"sender\",\"value\":\"auravaloper12pku6jshmeekleg5sfhdt50a4sxjlrhumumtwz\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"aura12pku6jshmeekleg5sfhdt50a4sxjlrhuqw2rku\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"223509uaura\"}]},{\"type\":\"withdraw_commission\",\"attributes\":[{\"key\":\"amount\",\"value\":\"223509uaura\"}]}]}]", + "info": "", + "gas_wanted": "200000", + "gas_used": "142624", + "events": [ + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjAwdWF1cmE=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjAwdWF1cmE=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjAwdWF1cmE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { "key": "ZmVl", "value": "MjAwdWF1cmE=", "index": true }, + { + "key": "ZmVlX3BheWVy", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "YWNjX3NlcQ==", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdS8yNDQ5", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "c2lnbmF0dXJl", + "value": "QjFRczdlZ2NMU0laWkgzMkErNzF2a0FzRjBTeG42emtsNHFuSnh2OGVmb2dtNVJydHJZTFY4cDFJdVJTSnp5eVAxTDVKZUJEQ1JjaHFERGlrQmJ6L3c9PQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd0RlbGVnYXRvclJld2FyZA==", + "index": true + } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQwMTA0dWF1cmE=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQwMTA0dWF1cmE=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQwMTA0dWF1cmE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MzQwMTA0dWF1cmE=", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxMnBrdTZqc2htZWVrbGVnNXNmaGR0NTBhNHN4amxyaHVtdW10d3o=", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "ZGlzdHJpYnV0aW9u", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd1ZhbGlkYXRvckNvbW1pc3Npb24=", + "index": true + } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjIzNTA5dWF1cmE=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjIzNTA5dWF1cmE=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTEycGt1NmpzaG1lZWtsZWc1c2ZoZHQ1MGE0c3hqbHJodXF3MnJrdQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjIzNTA5dWF1cmE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + } + ] + }, + { + "type": "withdraw_commission", + "attributes": [ + { "key": "YW1vdW50", "value": "MjIzNTA5dWF1cmE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "ZGlzdHJpYnV0aW9u", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYXZhbG9wZXIxMnBrdTZqc2htZWVrbGVnNXNmaGR0NTBhNHN4amxyaHVtdW10d3o=", + "index": true + } + ] + } + ], + "codespace": "" + }, + "tx": "CpQCCpwBCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEmEKK2F1cmExMnBrdTZqc2htZWVrbGVnNXNmaGR0NTBhNHN4amxyaHVxdzJya3USMmF1cmF2YWxvcGVyMTJwa3U2anNobWVla2xlZzVzZmhkdDUwYTRzeGpscmh1bXVtdHd6CnMKOy9jb3Ntb3MuZGlzdHJpYnV0aW9uLnYxYmV0YTEuTXNnV2l0aGRyYXdWYWxpZGF0b3JDb21taXNzaW9uEjQKMmF1cmF2YWxvcGVyMTJwa3U2anNobWVla2xlZzVzZmhkdDUwYTRzeGpscmh1bXVtdHd6EmcKUQpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOxEYuQv4wegGfSTUuqovipYdmXvu6PJBro71MKqDe8/RIECgIIARiRExISCgwKBXVhdXJhEgMyMDAQwJoMGkAHVCzt6BwtIhlkffYD7vW+QCwXRLGfrOSXiqcnG/x5+iCblGu2tgtXynUi5FInPLI/Uvkl4EMJFyGoMOKQFvP/" + } + ], + "total_count": "1" +} diff --git a/test/unit/services/crawl-transaction/tx_authz.fixture.json b/test/unit/services/crawl-transaction/tx_authz.fixture.json index 24dd9fcb1..98adb2f4d 100644 --- a/test/unit/services/crawl-transaction/tx_authz.fixture.json +++ b/test/unit/services/crawl-transaction/tx_authz.fixture.json @@ -1 +1,2685 @@ -{"txs":[{"hash":"14B177CFD3AC22F6AF1B46EF24C376B757B2379023E9EE075CB81A5E2FF18FAC","height":"452049","index":0,"tx_result":{"code":0,"data":"Ck0KHS9jb3Ntb3MuYXV0aHoudjFiZXRhMS5Nc2dFeGVjEiwKAAoACgAKAAoACgAKAAoACgAKAAoACgAKAAoACgAKAAoACgAKAAoACgAKAA==","log":"[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw\"},{\"key\":\"amount\",\"value\":\"10896uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"8757uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"receiver\",\"value\":\"aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6\"},{\"key\":\"amount\",\"value\":\"288uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"231uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"receiver\",\"value\":\"aura1q3truhus7zwhazzuaczygc5fy3u4a2frknavq2\"},{\"key\":\"amount\",\"value\":\"12357uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"9931uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"receiver\",\"value\":\"aura1hkx39tezwcc5zzdjwtffz0qupaezsckt23fake\"},{\"key\":\"amount\",\"value\":\"37632uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"30244uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"receiver\",\"value\":\"aura1st5n8x4t7uluwzuarrkzh5p7dc6g22ezgx9f8r\"},{\"key\":\"amount\",\"value\":\"639057uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"513596uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"receiver\",\"value\":\"aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7\"},{\"key\":\"amount\",\"value\":\"34653uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"27849uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"receiver\",\"value\":\"aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5\"},{\"key\":\"amount\",\"value\":\"16116uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"12952uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"receiver\",\"value\":\"aura1ffqj2q8asq5ae2twx2h76cvjp2m8rtrzkkluxa\"},{\"key\":\"amount\",\"value\":\"22364uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"17974uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"receiver\",\"value\":\"aura1njpy9vx7jra4522eegpg5qsyvr74j0n4xfanwc\"},{\"key\":\"amount\",\"value\":\"1238uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"995uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"receiver\",\"value\":\"aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz\"},{\"key\":\"amount\",\"value\":\"1009uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"811uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"receiver\",\"value\":\"aura1rzkf5z9rhy6yyn5xuvakjvy4fvn8mzv533vefx\"},{\"key\":\"amount\",\"value\":\"286193uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"230007uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"receiver\",\"value\":\"aura19au3a9lvw2fpa3lfh2h023n5uv60wunnrkx3uk\"},{\"key\":\"amount\",\"value\":\"707934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"568951uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"receiver\",\"value\":\"aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj\"},{\"key\":\"amount\",\"value\":\"1456uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"1170uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"receiver\",\"value\":\"aura12a75dfy8s4ac76356p8a8ht5hsutp7xjcqrzu7\"},{\"key\":\"amount\",\"value\":\"2076997uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"1669237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"receiver\",\"value\":\"aura1mdhdvqclrxdx78cdx6yl5v4cvqteze2py933em\"},{\"key\":\"amount\",\"value\":\"101237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"81362uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"receiver\",\"value\":\"aura143679htpup9yky3wlm6yhm3feul9fuh70uza6r\"},{\"key\":\"amount\",\"value\":\"93532uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"75169uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"receiver\",\"value\":\"aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq\"},{\"key\":\"amount\",\"value\":\"24804uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"19934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"receiver\",\"value\":\"aura1nqhugyn20xwrm7y5rcw5lq4sg0qculvrjgsthm\"},{\"key\":\"amount\",\"value\":\"314414uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"252688uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"receiver\",\"value\":\"aura1f0vypc9887x7s2z9cn63jxewajcs6p742d6pld\"},{\"key\":\"amount\",\"value\":\"601484uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"483399uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"receiver\",\"value\":\"aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz\"},{\"key\":\"amount\",\"value\":\"43215uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"34731uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"receiver\",\"value\":\"aura1633hgqh7uwsj43kh0d40x5f07xmwszdpkf82df\"},{\"key\":\"amount\",\"value\":\"10265uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"8249uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"receiver\",\"value\":\"aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p\"},{\"key\":\"amount\",\"value\":\"51778uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"41612uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"10896uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"spender\",\"value\":\"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw\"},{\"key\":\"amount\",\"value\":\"8757uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"288uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"spender\",\"value\":\"aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6\"},{\"key\":\"amount\",\"value\":\"231uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"12357uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"spender\",\"value\":\"aura1q3truhus7zwhazzuaczygc5fy3u4a2frknavq2\"},{\"key\":\"amount\",\"value\":\"9931uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"37632uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"spender\",\"value\":\"aura1hkx39tezwcc5zzdjwtffz0qupaezsckt23fake\"},{\"key\":\"amount\",\"value\":\"30244uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"639057uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"spender\",\"value\":\"aura1st5n8x4t7uluwzuarrkzh5p7dc6g22ezgx9f8r\"},{\"key\":\"amount\",\"value\":\"513596uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"34653uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"spender\",\"value\":\"aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7\"},{\"key\":\"amount\",\"value\":\"27849uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"16116uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"spender\",\"value\":\"aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5\"},{\"key\":\"amount\",\"value\":\"12952uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"22364uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"spender\",\"value\":\"aura1ffqj2q8asq5ae2twx2h76cvjp2m8rtrzkkluxa\"},{\"key\":\"amount\",\"value\":\"17974uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1238uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"spender\",\"value\":\"aura1njpy9vx7jra4522eegpg5qsyvr74j0n4xfanwc\"},{\"key\":\"amount\",\"value\":\"995uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1009uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"spender\",\"value\":\"aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz\"},{\"key\":\"amount\",\"value\":\"811uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"286193uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"spender\",\"value\":\"aura1rzkf5z9rhy6yyn5xuvakjvy4fvn8mzv533vefx\"},{\"key\":\"amount\",\"value\":\"230007uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"707934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"spender\",\"value\":\"aura19au3a9lvw2fpa3lfh2h023n5uv60wunnrkx3uk\"},{\"key\":\"amount\",\"value\":\"568951uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1456uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"spender\",\"value\":\"aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj\"},{\"key\":\"amount\",\"value\":\"1170uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"2076997uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"spender\",\"value\":\"aura12a75dfy8s4ac76356p8a8ht5hsutp7xjcqrzu7\"},{\"key\":\"amount\",\"value\":\"1669237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"101237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"spender\",\"value\":\"aura1mdhdvqclrxdx78cdx6yl5v4cvqteze2py933em\"},{\"key\":\"amount\",\"value\":\"81362uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"93532uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"spender\",\"value\":\"aura143679htpup9yky3wlm6yhm3feul9fuh70uza6r\"},{\"key\":\"amount\",\"value\":\"75169uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"24804uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"spender\",\"value\":\"aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq\"},{\"key\":\"amount\",\"value\":\"19934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"314414uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"spender\",\"value\":\"aura1nqhugyn20xwrm7y5rcw5lq4sg0qculvrjgsthm\"},{\"key\":\"amount\",\"value\":\"252688uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"601484uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"spender\",\"value\":\"aura1f0vypc9887x7s2z9cn63jxewajcs6p742d6pld\"},{\"key\":\"amount\",\"value\":\"483399uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"43215uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"spender\",\"value\":\"aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz\"},{\"key\":\"amount\",\"value\":\"34731uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"10265uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"spender\",\"value\":\"aura1633hgqh7uwsj43kh0d40x5f07xmwszdpkf82df\"},{\"key\":\"amount\",\"value\":\"8249uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"51778uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"},{\"key\":\"spender\",\"value\":\"aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p\"},{\"key\":\"amount\",\"value\":\"41612uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"delegate\",\"attributes\":[{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"8757uaura\"},{\"key\":\"new_shares\",\"value\":\"8757.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"231uaura\"},{\"key\":\"new_shares\",\"value\":\"231.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"9931uaura\"},{\"key\":\"new_shares\",\"value\":\"9931.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"30244uaura\"},{\"key\":\"new_shares\",\"value\":\"30244.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"513596uaura\"},{\"key\":\"new_shares\",\"value\":\"513596.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"27849uaura\"},{\"key\":\"new_shares\",\"value\":\"27849.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"12952uaura\"},{\"key\":\"new_shares\",\"value\":\"12952.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"17974uaura\"},{\"key\":\"new_shares\",\"value\":\"17974.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"995uaura\"},{\"key\":\"new_shares\",\"value\":\"995.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"811uaura\"},{\"key\":\"new_shares\",\"value\":\"811.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"230007uaura\"},{\"key\":\"new_shares\",\"value\":\"230007.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"568951uaura\"},{\"key\":\"new_shares\",\"value\":\"568951.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"1170uaura\"},{\"key\":\"new_shares\",\"value\":\"1170.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"1669237uaura\"},{\"key\":\"new_shares\",\"value\":\"1669237.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"81362uaura\"},{\"key\":\"new_shares\",\"value\":\"81362.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"75169uaura\"},{\"key\":\"new_shares\",\"value\":\"75169.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"19934uaura\"},{\"key\":\"new_shares\",\"value\":\"19934.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"252688uaura\"},{\"key\":\"new_shares\",\"value\":\"252688.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"483399uaura\"},{\"key\":\"new_shares\",\"value\":\"483399.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"34731uaura\"},{\"key\":\"new_shares\",\"value\":\"34731.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"8249uaura\"},{\"key\":\"new_shares\",\"value\":\"8249.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"41612uaura\"},{\"key\":\"new_shares\",\"value\":\"41612.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.authz.v1beta1.MsgExec\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1q3truhus7zwhazzuaczygc5fy3u4a2frknavq2\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1hkx39tezwcc5zzdjwtffz0qupaezsckt23fake\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1st5n8x4t7uluwzuarrkzh5p7dc6g22ezgx9f8r\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1ffqj2q8asq5ae2twx2h76cvjp2m8rtrzkkluxa\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1njpy9vx7jra4522eegpg5qsyvr74j0n4xfanwc\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1rzkf5z9rhy6yyn5xuvakjvy4fvn8mzv533vefx\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura19au3a9lvw2fpa3lfh2h023n5uv60wunnrkx3uk\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura12a75dfy8s4ac76356p8a8ht5hsutp7xjcqrzu7\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1mdhdvqclrxdx78cdx6yl5v4cvqteze2py933em\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura143679htpup9yky3wlm6yhm3feul9fuh70uza6r\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1nqhugyn20xwrm7y5rcw5lq4sg0qculvrjgsthm\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1f0vypc9887x7s2z9cn63jxewajcs6p742d6pld\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1633hgqh7uwsj43kh0d40x5f07xmwszdpkf82df\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"10896uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"recipient\",\"value\":\"aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"288uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"recipient\",\"value\":\"aura1q3truhus7zwhazzuaczygc5fy3u4a2frknavq2\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"12357uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"recipient\",\"value\":\"aura1hkx39tezwcc5zzdjwtffz0qupaezsckt23fake\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"37632uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"recipient\",\"value\":\"aura1st5n8x4t7uluwzuarrkzh5p7dc6g22ezgx9f8r\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"639057uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"recipient\",\"value\":\"aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"34653uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"recipient\",\"value\":\"aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"16116uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"recipient\",\"value\":\"aura1ffqj2q8asq5ae2twx2h76cvjp2m8rtrzkkluxa\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"22364uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"recipient\",\"value\":\"aura1njpy9vx7jra4522eegpg5qsyvr74j0n4xfanwc\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1238uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"recipient\",\"value\":\"aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1009uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"recipient\",\"value\":\"aura1rzkf5z9rhy6yyn5xuvakjvy4fvn8mzv533vefx\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"286193uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"recipient\",\"value\":\"aura19au3a9lvw2fpa3lfh2h023n5uv60wunnrkx3uk\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"707934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"recipient\",\"value\":\"aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1456uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"recipient\",\"value\":\"aura12a75dfy8s4ac76356p8a8ht5hsutp7xjcqrzu7\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"2076997uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"recipient\",\"value\":\"aura1mdhdvqclrxdx78cdx6yl5v4cvqteze2py933em\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"101237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"recipient\",\"value\":\"aura143679htpup9yky3wlm6yhm3feul9fuh70uza6r\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"93532uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"recipient\",\"value\":\"aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"24804uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"recipient\",\"value\":\"aura1nqhugyn20xwrm7y5rcw5lq4sg0qculvrjgsthm\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"314414uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"recipient\",\"value\":\"aura1f0vypc9887x7s2z9cn63jxewajcs6p742d6pld\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"601484uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"recipient\",\"value\":\"aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"43215uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"recipient\",\"value\":\"aura1633hgqh7uwsj43kh0d40x5f07xmwszdpkf82df\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"10265uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"recipient\",\"value\":\"aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"51778uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"withdraw_rewards\",\"attributes\":[{\"key\":\"amount\",\"value\":\"10896uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"amount\",\"value\":\"288uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"amount\",\"value\":\"12357uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"amount\",\"value\":\"37632uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"amount\",\"value\":\"639057uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"amount\",\"value\":\"34653uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"amount\",\"value\":\"16116uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"amount\",\"value\":\"22364uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"amount\",\"value\":\"1238uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"amount\",\"value\":\"1009uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"amount\",\"value\":\"286193uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"amount\",\"value\":\"707934uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"amount\",\"value\":\"1456uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"amount\",\"value\":\"2076997uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"amount\",\"value\":\"101237uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"amount\",\"value\":\"93532uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"amount\",\"value\":\"24804uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"amount\",\"value\":\"314414uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"amount\",\"value\":\"601484uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"amount\",\"value\":\"43215uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"amount\",\"value\":\"10265uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"amount\",\"value\":\"51778uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]}]}]","info":"","gas_wanted":"3175474","gas_used":"2888215","events":[{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ng==","index":true},{"key":"YW1vdW50","value":"MTU4OHVhdXJh","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==","index":true},{"key":"YW1vdW50","value":"MTU4OHVhdXJh","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ng==","index":true},{"key":"YW1vdW50","value":"MTU4OHVhdXJh","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ng==","index":true}]},{"type":"tx","attributes":[{"key":"ZmVl","value":"MTU4OHVhdXJh","index":true},{"key":"ZmVlX3BheWVy","value":"YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ng==","index":true}]},{"type":"tx","attributes":[{"key":"YWNjX3NlcQ==","value":"YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ni81NDA1","index":true}]},{"type":"tx","attributes":[{"key":"c2lnbmF0dXJl","value":"ZGNpTmxuaUxPMUlTd3Zkc2txek8xMkpCeFZNVER0RmtBQnp5WEVKeUJVZ0pQcGYxd094NGRaWnRJYXRreVpobUFoT0RtN2wzeUxKQjJhbm9HcUpTcnc9PQ==","index":true}]},{"type":"message","attributes":[{"key":"YWN0aW9u","value":"L2Nvc21vcy5hdXRoei52MWJldGExLk1zZ0V4ZWM=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTA4OTZ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3Rmdw==","index":true},{"key":"YW1vdW50","value":"MTA4OTZ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3Rmdw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTA4OTZ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MTA4OTZ1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3Rmdw==","index":true},{"key":"YW1vdW50","value":"ODc1N3VhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"ODc1N3VhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"ODc1N3VhdXJh","index":true},{"key":"bmV3X3NoYXJlcw==","value":"ODc1Ny4wMDAwMDAwMDAwMDAwMDAwMDA=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3Rmdw==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"Mjg4dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==","index":true},{"key":"YW1vdW50","value":"Mjg4dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"Mjg4dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"Mjg4dWF1cmE=","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==","index":true},{"key":"YW1vdW50","value":"MjMxdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MjMxdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MjMxdWF1cmE=","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MjMxLjAwMDAwMDAwMDAwMDAwMDAwMA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MQ==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTIzNTd1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFxM3RydWh1czd6d2hhenp1YWN6eWdjNWZ5M3U0YTJmcmtuYXZxMg==","index":true},{"key":"YW1vdW50","value":"MTIzNTd1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFxM3RydWh1czd6d2hhenp1YWN6eWdjNWZ5M3U0YTJmcmtuYXZxMg==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTIzNTd1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MTIzNTd1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFxM3RydWh1czd6d2hhenp1YWN6eWdjNWZ5M3U0YTJmcmtuYXZxMg==","index":true},{"key":"YW1vdW50","value":"OTkzMXVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"OTkzMXVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"OTkzMXVhdXJh","index":true},{"key":"bmV3X3NoYXJlcw==","value":"OTkzMS4wMDAwMDAwMDAwMDAwMDAwMDA=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFxM3RydWh1czd6d2hhenp1YWN6eWdjNWZ5M3U0YTJmcmtuYXZxMg==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mg==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"Mzc2MzJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFoa3gzOXRlendjYzV6emRqd3RmZnowcXVwYWV6c2NrdDIzZmFrZQ==","index":true},{"key":"YW1vdW50","value":"Mzc2MzJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFoa3gzOXRlendjYzV6emRqd3RmZnowcXVwYWV6c2NrdDIzZmFrZQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"Mzc2MzJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"Mzc2MzJ1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFoa3gzOXRlendjYzV6emRqd3RmZnowcXVwYWV6c2NrdDIzZmFrZQ==","index":true},{"key":"YW1vdW50","value":"MzAyNDR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MzAyNDR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MzAyNDR1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MzAyNDQuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFoa3gzOXRlendjYzV6emRqd3RmZnowcXVwYWV6c2NrdDIzZmFrZQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Mw==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NjM5MDU3dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFzdDVuOHg0dDd1bHV3enVhcnJremg1cDdkYzZnMjJlemd4OWY4cg==","index":true},{"key":"YW1vdW50","value":"NjM5MDU3dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFzdDVuOHg0dDd1bHV3enVhcnJremg1cDdkYzZnMjJlemd4OWY4cg==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NjM5MDU3dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"NjM5MDU3dWF1cmE=","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFzdDVuOHg0dDd1bHV3enVhcnJremg1cDdkYzZnMjJlemd4OWY4cg==","index":true},{"key":"YW1vdW50","value":"NTEzNTk2dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"NTEzNTk2dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"NTEzNTk2dWF1cmE=","index":true},{"key":"bmV3X3NoYXJlcw==","value":"NTEzNTk2LjAwMDAwMDAwMDAwMDAwMDAwMA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFzdDVuOHg0dDd1bHV3enVhcnJremg1cDdkYzZnMjJlemd4OWY4cg==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MzQ2NTN1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==","index":true},{"key":"YW1vdW50","value":"MzQ2NTN1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MzQ2NTN1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MzQ2NTN1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==","index":true},{"key":"YW1vdW50","value":"Mjc4NDl1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"Mjc4NDl1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"Mjc4NDl1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"Mjc4NDkuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"NQ==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTYxMTZ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==","index":true},{"key":"YW1vdW50","value":"MTYxMTZ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTYxMTZ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MTYxMTZ1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==","index":true},{"key":"YW1vdW50","value":"MTI5NTJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MTI5NTJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MTI5NTJ1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MTI5NTIuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Ng==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MjIzNjR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmZnFqMnE4YXNxNWFlMnR3eDJoNzZjdmpwMm04cnRyemtrbHV4YQ==","index":true},{"key":"YW1vdW50","value":"MjIzNjR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFmZnFqMnE4YXNxNWFlMnR3eDJoNzZjdmpwMm04cnRyemtrbHV4YQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MjIzNjR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MjIzNjR1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFmZnFqMnE4YXNxNWFlMnR3eDJoNzZjdmpwMm04cnRyemtrbHV4YQ==","index":true},{"key":"YW1vdW50","value":"MTc5NzR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MTc5NzR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MTc5NzR1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MTc5NzQuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFmZnFqMnE4YXNxNWFlMnR3eDJoNzZjdmpwMm04cnRyemtrbHV4YQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"Nw==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTIzOHVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFuanB5OXZ4N2pyYTQ1MjJlZWdwZzVxc3l2cjc0ajBuNHhmYW53Yw==","index":true},{"key":"YW1vdW50","value":"MTIzOHVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFuanB5OXZ4N2pyYTQ1MjJlZWdwZzVxc3l2cjc0ajBuNHhmYW53Yw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTIzOHVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MTIzOHVhdXJh","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFuanB5OXZ4N2pyYTQ1MjJlZWdwZzVxc3l2cjc0ajBuNHhmYW53Yw==","index":true},{"key":"YW1vdW50","value":"OTk1dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"OTk1dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"OTk1dWF1cmE=","index":true},{"key":"bmV3X3NoYXJlcw==","value":"OTk1LjAwMDAwMDAwMDAwMDAwMDAwMA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFuanB5OXZ4N2pyYTQ1MjJlZWdwZzVxc3l2cjc0ajBuNHhmYW53Yw==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTAwOXVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==","index":true},{"key":"YW1vdW50","value":"MTAwOXVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTAwOXVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MTAwOXVhdXJh","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==","index":true},{"key":"YW1vdW50","value":"ODExdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"ODExdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"ODExdWF1cmE=","index":true},{"key":"bmV3X3NoYXJlcw==","value":"ODExLjAwMDAwMDAwMDAwMDAwMDAwMA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"OQ==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"Mjg2MTkzdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFyemtmNXo5cmh5Nnl5bjV4dXZha2p2eTRmdm44bXp2NTMzdmVmeA==","index":true},{"key":"YW1vdW50","value":"Mjg2MTkzdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFyemtmNXo5cmh5Nnl5bjV4dXZha2p2eTRmdm44bXp2NTMzdmVmeA==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"Mjg2MTkzdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"Mjg2MTkzdWF1cmE=","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFyemtmNXo5cmh5Nnl5bjV4dXZha2p2eTRmdm44bXp2NTMzdmVmeA==","index":true},{"key":"YW1vdW50","value":"MjMwMDA3dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MjMwMDA3dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MjMwMDA3dWF1cmE=","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MjMwMDA3LjAwMDAwMDAwMDAwMDAwMDAwMA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFyemtmNXo5cmh5Nnl5bjV4dXZha2p2eTRmdm44bXp2NTMzdmVmeA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTA=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NzA3OTM0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTE5YXUzYTlsdncyZnBhM2xmaDJoMDIzbjV1djYwd3VubnJreDN1aw==","index":true},{"key":"YW1vdW50","value":"NzA3OTM0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTE5YXUzYTlsdncyZnBhM2xmaDJoMDIzbjV1djYwd3VubnJreDN1aw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NzA3OTM0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"NzA3OTM0dWF1cmE=","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTE5YXUzYTlsdncyZnBhM2xmaDJoMDIzbjV1djYwd3VubnJreDN1aw==","index":true},{"key":"YW1vdW50","value":"NTY4OTUxdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"NTY4OTUxdWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"NTY4OTUxdWF1cmE=","index":true},{"key":"bmV3X3NoYXJlcw==","value":"NTY4OTUxLjAwMDAwMDAwMDAwMDAwMDAwMA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTE5YXUzYTlsdncyZnBhM2xmaDJoMDIzbjV1djYwd3VubnJreDN1aw==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTE=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTQ1NnVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==","index":true},{"key":"YW1vdW50","value":"MTQ1NnVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTQ1NnVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MTQ1NnVhdXJh","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==","index":true},{"key":"YW1vdW50","value":"MTE3MHVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MTE3MHVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MTE3MHVhdXJh","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MTE3MC4wMDAwMDAwMDAwMDAwMDAwMDA=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTI=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MjA3Njk5N3VhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTEyYTc1ZGZ5OHM0YWM3NjM1NnA4YThodDVoc3V0cDd4amNxcnp1Nw==","index":true},{"key":"YW1vdW50","value":"MjA3Njk5N3VhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTEyYTc1ZGZ5OHM0YWM3NjM1NnA4YThodDVoc3V0cDd4amNxcnp1Nw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MjA3Njk5N3VhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MjA3Njk5N3VhdXJh","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTEyYTc1ZGZ5OHM0YWM3NjM1NnA4YThodDVoc3V0cDd4amNxcnp1Nw==","index":true},{"key":"YW1vdW50","value":"MTY2OTIzN3VhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MTY2OTIzN3VhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MTY2OTIzN3VhdXJh","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MTY2OTIzNy4wMDAwMDAwMDAwMDAwMDAwMDA=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTEyYTc1ZGZ5OHM0YWM3NjM1NnA4YThodDVoc3V0cDd4amNxcnp1Nw==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTM=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTAxMjM3dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbQ==","index":true},{"key":"YW1vdW50","value":"MTAxMjM3dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTAxMjM3dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MTAxMjM3dWF1cmE=","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbQ==","index":true},{"key":"YW1vdW50","value":"ODEzNjJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"ODEzNjJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"ODEzNjJ1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"ODEzNjIuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTQ=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"OTM1MzJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTE0MzY3OWh0cHVwOXlreTN3bG02eWhtM2ZldWw5ZnVoNzB1emE2cg==","index":true},{"key":"YW1vdW50","value":"OTM1MzJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTE0MzY3OWh0cHVwOXlreTN3bG02eWhtM2ZldWw5ZnVoNzB1emE2cg==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"OTM1MzJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"OTM1MzJ1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTE0MzY3OWh0cHVwOXlreTN3bG02eWhtM2ZldWw5ZnVoNzB1emE2cg==","index":true},{"key":"YW1vdW50","value":"NzUxNjl1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"NzUxNjl1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"NzUxNjl1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"NzUxNjkuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTE0MzY3OWh0cHVwOXlreTN3bG02eWhtM2ZldWw5ZnVoNzB1emE2cg==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTU=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MjQ4MDR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==","index":true},{"key":"YW1vdW50","value":"MjQ4MDR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MjQ4MDR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MjQ4MDR1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==","index":true},{"key":"YW1vdW50","value":"MTk5MzR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MTk5MzR1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MTk5MzR1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MTk5MzQuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTY=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MzE0NDE0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobQ==","index":true},{"key":"YW1vdW50","value":"MzE0NDE0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobQ==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MzE0NDE0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MzE0NDE0dWF1cmE=","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobQ==","index":true},{"key":"YW1vdW50","value":"MjUyNjg4dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MjUyNjg4dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MjUyNjg4dWF1cmE=","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MjUyNjg4LjAwMDAwMDAwMDAwMDAwMDAwMA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTc=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NjAxNDg0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZA==","index":true},{"key":"YW1vdW50","value":"NjAxNDg0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZA==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NjAxNDg0dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"NjAxNDg0dWF1cmE=","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZA==","index":true},{"key":"YW1vdW50","value":"NDgzMzk5dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"NDgzMzk5dWF1cmE=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"NDgzMzk5dWF1cmE=","index":true},{"key":"bmV3X3NoYXJlcw==","value":"NDgzMzk5LjAwMDAwMDAwMDAwMDAwMDAwMA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTg=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NDMyMTV1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==","index":true},{"key":"YW1vdW50","value":"NDMyMTV1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NDMyMTV1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"NDMyMTV1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==","index":true},{"key":"YW1vdW50","value":"MzQ3MzF1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"MzQ3MzF1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"MzQ3MzF1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"MzQ3MzEuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MTk=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTAyNjV1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTE2MzNoZ3FoN3V3c2o0M2toMGQ0MHg1ZjA3eG13c3pkcGtmODJkZg==","index":true},{"key":"YW1vdW50","value":"MTAyNjV1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTE2MzNoZ3FoN3V3c2o0M2toMGQ0MHg1ZjA3eG13c3pkcGtmODJkZg==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"MTAyNjV1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"MTAyNjV1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTE2MzNoZ3FoN3V3c2o0M2toMGQ0MHg1ZjA3eG13c3pkcGtmODJkZg==","index":true},{"key":"YW1vdW50","value":"ODI0OXVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"ODI0OXVhdXJh","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"ODI0OXVhdXJh","index":true},{"key":"bmV3X3NoYXJlcw==","value":"ODI0OS4wMDAwMDAwMDAwMDAwMDAwMDA=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTE2MzNoZ3FoN3V3c2o0M2toMGQ0MHg1ZjA3eG13c3pkcGtmODJkZg==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjA=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NTE3Nzh1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==","index":true},{"key":"YW1vdW50","value":"NTE3Nzh1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YW1vdW50","value":"NTE3Nzh1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]},{"type":"withdraw_rewards","attributes":[{"key":"YW1vdW50","value":"NTE3Nzh1YXVyYQ==","index":true},{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==","index":true},{"key":"YW1vdW50","value":"NDE2MTJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==","index":true},{"key":"YW1vdW50","value":"NDE2MTJ1YXVyYQ==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]},{"type":"delegate","attributes":[{"key":"dmFsaWRhdG9y","value":"YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=","index":true},{"key":"YW1vdW50","value":"NDE2MTJ1YXVyYQ==","index":true},{"key":"bmV3X3NoYXJlcw==","value":"NDE2MTIuMDAwMDAwMDAwMDAwMDAwMDAw","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"c3Rha2luZw==","index":true},{"key":"c2VuZGVy","value":"YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==","index":true},{"key":"YXV0aHpfbXNnX2luZGV4","value":"MjE=","index":true}]}],"codespace":""},"tx":"CtEbCp4bCh0vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnRXhlYxL8GgorYXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3NhKXAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScAorYXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3RmdxIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDQoFdWF1cmESBDg3NTcSlgEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEm8KK2F1cmExcTkzeGt3dGZ2N251dDBlcWpqd3MzNzd3a2prOTcyNjV6c3hseDYSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGgwKBXVhdXJhEgMyMzESlwEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnAKK2F1cmExcTN0cnVodXM3endoYXp6dWFjenlnYzVmeTN1NGEyZnJrbmF2cTISMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg0KBXVhdXJhEgQ5OTMxEpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCithdXJhMWhreDM5dGV6d2NjNXp6ZGp3dGZmejBxdXBhZXpzY2t0MjNmYWtlEjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoOCgV1YXVyYRIFMzAyNDQSmQEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnIKK2F1cmExc3Q1bjh4NHQ3dWx1d3p1YXJya3poNXA3ZGM2ZzIyZXpneDlmOHISMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg8KBXVhdXJhEgY1MTM1OTYSmAEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnEKK2F1cmExcnFsbDJkNHd5eWx2bDAzaHQ2bWhnbHN3ajQ2Z2tjcjNrc3ZrbTcSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg4KBXVhdXJhEgUyNzg0ORKYAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScQorYXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNRIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDgoFdWF1cmESBTEyOTUyEpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCithdXJhMWZmcWoycThhc3E1YWUydHd4Mmg3NmN2anAybThydHJ6a2tsdXhhEjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoOCgV1YXVyYRIFMTc5NzQSlgEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEm8KK2F1cmExbmpweTl2eDdqcmE0NTIyZWVncGc1cXN5dnI3NGowbjR4ZmFud2MSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGgwKBXVhdXJhEgM5OTUSlgEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEm8KK2F1cmExZWZxNXE0dXpuNTgzbmg1bWF1emM1Y21nbXM1M3c5bDZ2czVkeHoSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGgwKBXVhdXJhEgM4MTESmQEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnIKK2F1cmExcnprZjV6OXJoeTZ5eW41eHV2YWtqdnk0ZnZuOG16djUzM3ZlZngSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg8KBXVhdXJhEgYyMzAwMDcSmQEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnIKK2F1cmExOWF1M2E5bHZ3MmZwYTNsZmgyaDAyM241dXY2MHd1bm5ya3gzdWsSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg8KBXVhdXJhEgY1Njg5NTESlwEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnAKK2F1cmExdWp2MmdtZndyd3pqNTA0bnRnZ3FsZDBxNWV1YWZwNzZ2Z3g1bGoSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg0KBXVhdXJhEgQxMTcwEpoBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJzCithdXJhMTJhNzVkZnk4czRhYzc2MzU2cDhhOGh0NWhzdXRwN3hqY3FyenU3EjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoQCgV1YXVyYRIHMTY2OTIzNxKYAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScQorYXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbRIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDgoFdWF1cmESBTgxMzYyEpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCithdXJhMTQzNjc5aHRwdXA5eWt5M3dsbTZ5aG0zZmV1bDlmdWg3MHV6YTZyEjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoOCgV1YXVyYRIFNzUxNjkSmAEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnEKK2F1cmExZnd0a3FlNHlwNjUyc3ZyajVsemR1OWxueWt5c2g5NDdtc2M0eHESMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg4KBXVhdXJhEgUxOTkzNBKZAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScgorYXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobRIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDwoFdWF1cmESBjI1MjY4OBKZAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScgorYXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZBIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDwoFdWF1cmESBjQ4MzM5ORKYAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScQorYXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dsehIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDgoFdWF1cmESBTM0NzMxEpcBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJwCithdXJhMTYzM2hncWg3dXdzajQza2gwZDQweDVmMDd4bXdzemRwa2Y4MmRmEjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoNCgV1YXVyYRIEODI0ORKYAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScQorYXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cBIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDgoFdWF1cmESBTQxNjEyEi5SRVN0YWtlZCBieSAgQXV0b1N0YWtlIPCfm6HvuI8gU2xhc2ggUHJvdGVjdGVkEmkKUQpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOaXY9F10j5uP8HchWBOLJwIsI6JlzBPmY7Z5rEutB14xIECgIIARidKhIUCg0KBXVhdXJhEgQxNTg4ELLowQEaQHXIjZZ4iztSEsL3bJKsztdiQcVTEw7RZAAc8lxCcgVICT6X9cDseHWWbSGrZMmYZgITg5u5d8iyQdmp6BqiUq8="}],"total_count":"1"} \ No newline at end of file +{ + "txs": [ + { + "hash": "14B177CFD3AC22F6AF1B46EF24C376B757B2379023E9EE075CB81A5E2FF18FAC", + "height": "452049", + "index": 0, + "tx_result": { + "code": 0, + "data": "Ck0KHS9jb3Ntb3MuYXV0aHoudjFiZXRhMS5Nc2dFeGVjEiwKAAoACgAKAAoACgAKAAoACgAKAAoACgAKAAoACgAKAAoACgAKAAoACgAKAA==", + "log": "[{\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw\"},{\"key\":\"amount\",\"value\":\"10896uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"8757uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"receiver\",\"value\":\"aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6\"},{\"key\":\"amount\",\"value\":\"288uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"231uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"receiver\",\"value\":\"aura1q3truhus7zwhazzuaczygc5fy3u4a2frknavq2\"},{\"key\":\"amount\",\"value\":\"12357uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"9931uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"receiver\",\"value\":\"aura1hkx39tezwcc5zzdjwtffz0qupaezsckt23fake\"},{\"key\":\"amount\",\"value\":\"37632uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"30244uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"receiver\",\"value\":\"aura1st5n8x4t7uluwzuarrkzh5p7dc6g22ezgx9f8r\"},{\"key\":\"amount\",\"value\":\"639057uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"513596uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"receiver\",\"value\":\"aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7\"},{\"key\":\"amount\",\"value\":\"34653uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"27849uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"receiver\",\"value\":\"aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5\"},{\"key\":\"amount\",\"value\":\"16116uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"12952uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"receiver\",\"value\":\"aura1ffqj2q8asq5ae2twx2h76cvjp2m8rtrzkkluxa\"},{\"key\":\"amount\",\"value\":\"22364uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"17974uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"receiver\",\"value\":\"aura1njpy9vx7jra4522eegpg5qsyvr74j0n4xfanwc\"},{\"key\":\"amount\",\"value\":\"1238uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"995uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"receiver\",\"value\":\"aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz\"},{\"key\":\"amount\",\"value\":\"1009uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"811uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"receiver\",\"value\":\"aura1rzkf5z9rhy6yyn5xuvakjvy4fvn8mzv533vefx\"},{\"key\":\"amount\",\"value\":\"286193uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"230007uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"receiver\",\"value\":\"aura19au3a9lvw2fpa3lfh2h023n5uv60wunnrkx3uk\"},{\"key\":\"amount\",\"value\":\"707934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"568951uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"receiver\",\"value\":\"aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj\"},{\"key\":\"amount\",\"value\":\"1456uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"1170uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"receiver\",\"value\":\"aura12a75dfy8s4ac76356p8a8ht5hsutp7xjcqrzu7\"},{\"key\":\"amount\",\"value\":\"2076997uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"1669237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"receiver\",\"value\":\"aura1mdhdvqclrxdx78cdx6yl5v4cvqteze2py933em\"},{\"key\":\"amount\",\"value\":\"101237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"81362uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"receiver\",\"value\":\"aura143679htpup9yky3wlm6yhm3feul9fuh70uza6r\"},{\"key\":\"amount\",\"value\":\"93532uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"75169uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"receiver\",\"value\":\"aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq\"},{\"key\":\"amount\",\"value\":\"24804uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"19934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"receiver\",\"value\":\"aura1nqhugyn20xwrm7y5rcw5lq4sg0qculvrjgsthm\"},{\"key\":\"amount\",\"value\":\"314414uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"252688uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"receiver\",\"value\":\"aura1f0vypc9887x7s2z9cn63jxewajcs6p742d6pld\"},{\"key\":\"amount\",\"value\":\"601484uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"483399uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"receiver\",\"value\":\"aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz\"},{\"key\":\"amount\",\"value\":\"43215uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"34731uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"receiver\",\"value\":\"aura1633hgqh7uwsj43kh0d40x5f07xmwszdpkf82df\"},{\"key\":\"amount\",\"value\":\"10265uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"8249uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"receiver\",\"value\":\"aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p\"},{\"key\":\"amount\",\"value\":\"51778uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"},{\"key\":\"receiver\",\"value\":\"aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw\"},{\"key\":\"amount\",\"value\":\"41612uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"10896uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"spender\",\"value\":\"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw\"},{\"key\":\"amount\",\"value\":\"8757uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"288uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"spender\",\"value\":\"aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6\"},{\"key\":\"amount\",\"value\":\"231uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"12357uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"spender\",\"value\":\"aura1q3truhus7zwhazzuaczygc5fy3u4a2frknavq2\"},{\"key\":\"amount\",\"value\":\"9931uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"37632uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"spender\",\"value\":\"aura1hkx39tezwcc5zzdjwtffz0qupaezsckt23fake\"},{\"key\":\"amount\",\"value\":\"30244uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"639057uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"spender\",\"value\":\"aura1st5n8x4t7uluwzuarrkzh5p7dc6g22ezgx9f8r\"},{\"key\":\"amount\",\"value\":\"513596uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"34653uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"spender\",\"value\":\"aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7\"},{\"key\":\"amount\",\"value\":\"27849uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"16116uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"spender\",\"value\":\"aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5\"},{\"key\":\"amount\",\"value\":\"12952uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"22364uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"spender\",\"value\":\"aura1ffqj2q8asq5ae2twx2h76cvjp2m8rtrzkkluxa\"},{\"key\":\"amount\",\"value\":\"17974uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1238uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"spender\",\"value\":\"aura1njpy9vx7jra4522eegpg5qsyvr74j0n4xfanwc\"},{\"key\":\"amount\",\"value\":\"995uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1009uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"spender\",\"value\":\"aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz\"},{\"key\":\"amount\",\"value\":\"811uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"286193uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"spender\",\"value\":\"aura1rzkf5z9rhy6yyn5xuvakjvy4fvn8mzv533vefx\"},{\"key\":\"amount\",\"value\":\"230007uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"707934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"spender\",\"value\":\"aura19au3a9lvw2fpa3lfh2h023n5uv60wunnrkx3uk\"},{\"key\":\"amount\",\"value\":\"568951uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1456uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"spender\",\"value\":\"aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj\"},{\"key\":\"amount\",\"value\":\"1170uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"2076997uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"spender\",\"value\":\"aura12a75dfy8s4ac76356p8a8ht5hsutp7xjcqrzu7\"},{\"key\":\"amount\",\"value\":\"1669237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"101237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"spender\",\"value\":\"aura1mdhdvqclrxdx78cdx6yl5v4cvqteze2py933em\"},{\"key\":\"amount\",\"value\":\"81362uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"93532uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"spender\",\"value\":\"aura143679htpup9yky3wlm6yhm3feul9fuh70uza6r\"},{\"key\":\"amount\",\"value\":\"75169uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"24804uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"spender\",\"value\":\"aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq\"},{\"key\":\"amount\",\"value\":\"19934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"314414uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"spender\",\"value\":\"aura1nqhugyn20xwrm7y5rcw5lq4sg0qculvrjgsthm\"},{\"key\":\"amount\",\"value\":\"252688uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"601484uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"spender\",\"value\":\"aura1f0vypc9887x7s2z9cn63jxewajcs6p742d6pld\"},{\"key\":\"amount\",\"value\":\"483399uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"43215uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"spender\",\"value\":\"aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz\"},{\"key\":\"amount\",\"value\":\"34731uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"10265uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"spender\",\"value\":\"aura1633hgqh7uwsj43kh0d40x5f07xmwszdpkf82df\"},{\"key\":\"amount\",\"value\":\"8249uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"spender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"51778uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"},{\"key\":\"spender\",\"value\":\"aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p\"},{\"key\":\"amount\",\"value\":\"41612uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"delegate\",\"attributes\":[{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"8757uaura\"},{\"key\":\"new_shares\",\"value\":\"8757.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"231uaura\"},{\"key\":\"new_shares\",\"value\":\"231.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"9931uaura\"},{\"key\":\"new_shares\",\"value\":\"9931.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"30244uaura\"},{\"key\":\"new_shares\",\"value\":\"30244.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"513596uaura\"},{\"key\":\"new_shares\",\"value\":\"513596.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"27849uaura\"},{\"key\":\"new_shares\",\"value\":\"27849.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"12952uaura\"},{\"key\":\"new_shares\",\"value\":\"12952.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"17974uaura\"},{\"key\":\"new_shares\",\"value\":\"17974.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"995uaura\"},{\"key\":\"new_shares\",\"value\":\"995.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"811uaura\"},{\"key\":\"new_shares\",\"value\":\"811.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"230007uaura\"},{\"key\":\"new_shares\",\"value\":\"230007.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"568951uaura\"},{\"key\":\"new_shares\",\"value\":\"568951.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"1170uaura\"},{\"key\":\"new_shares\",\"value\":\"1170.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"1669237uaura\"},{\"key\":\"new_shares\",\"value\":\"1669237.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"81362uaura\"},{\"key\":\"new_shares\",\"value\":\"81362.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"75169uaura\"},{\"key\":\"new_shares\",\"value\":\"75169.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"19934uaura\"},{\"key\":\"new_shares\",\"value\":\"19934.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"252688uaura\"},{\"key\":\"new_shares\",\"value\":\"252688.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"483399uaura\"},{\"key\":\"new_shares\",\"value\":\"483399.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"34731uaura\"},{\"key\":\"new_shares\",\"value\":\"34731.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"8249uaura\"},{\"key\":\"new_shares\",\"value\":\"8249.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"amount\",\"value\":\"41612uaura\"},{\"key\":\"new_shares\",\"value\":\"41612.000000000000000000\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.authz.v1beta1.MsgExec\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1q3truhus7zwhazzuaczygc5fy3u4a2frknavq2\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1hkx39tezwcc5zzdjwtffz0qupaezsckt23fake\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1st5n8x4t7uluwzuarrkzh5p7dc6g22ezgx9f8r\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1ffqj2q8asq5ae2twx2h76cvjp2m8rtrzkkluxa\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1njpy9vx7jra4522eegpg5qsyvr74j0n4xfanwc\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1rzkf5z9rhy6yyn5xuvakjvy4fvn8mzv533vefx\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura19au3a9lvw2fpa3lfh2h023n5uv60wunnrkx3uk\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura12a75dfy8s4ac76356p8a8ht5hsutp7xjcqrzu7\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1mdhdvqclrxdx78cdx6yl5v4cvqteze2py933em\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura143679htpup9yky3wlm6yhm3feul9fuh70uza6r\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1nqhugyn20xwrm7y5rcw5lq4sg0qculvrjgsthm\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1f0vypc9887x7s2z9cn63jxewajcs6p742d6pld\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1633hgqh7uwsj43kh0d40x5f07xmwszdpkf82df\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"10896uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"recipient\",\"value\":\"aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"288uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"recipient\",\"value\":\"aura1q3truhus7zwhazzuaczygc5fy3u4a2frknavq2\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"12357uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"recipient\",\"value\":\"aura1hkx39tezwcc5zzdjwtffz0qupaezsckt23fake\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"37632uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"recipient\",\"value\":\"aura1st5n8x4t7uluwzuarrkzh5p7dc6g22ezgx9f8r\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"639057uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"recipient\",\"value\":\"aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"34653uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"recipient\",\"value\":\"aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"16116uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"recipient\",\"value\":\"aura1ffqj2q8asq5ae2twx2h76cvjp2m8rtrzkkluxa\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"22364uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"recipient\",\"value\":\"aura1njpy9vx7jra4522eegpg5qsyvr74j0n4xfanwc\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1238uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"recipient\",\"value\":\"aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1009uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"recipient\",\"value\":\"aura1rzkf5z9rhy6yyn5xuvakjvy4fvn8mzv533vefx\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"286193uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"recipient\",\"value\":\"aura19au3a9lvw2fpa3lfh2h023n5uv60wunnrkx3uk\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"707934uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"recipient\",\"value\":\"aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"1456uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"recipient\",\"value\":\"aura12a75dfy8s4ac76356p8a8ht5hsutp7xjcqrzu7\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"2076997uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"recipient\",\"value\":\"aura1mdhdvqclrxdx78cdx6yl5v4cvqteze2py933em\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"101237uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"recipient\",\"value\":\"aura143679htpup9yky3wlm6yhm3feul9fuh70uza6r\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"93532uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"recipient\",\"value\":\"aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"24804uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"recipient\",\"value\":\"aura1nqhugyn20xwrm7y5rcw5lq4sg0qculvrjgsthm\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"314414uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"recipient\",\"value\":\"aura1f0vypc9887x7s2z9cn63jxewajcs6p742d6pld\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"601484uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"recipient\",\"value\":\"aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"43215uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"recipient\",\"value\":\"aura1633hgqh7uwsj43kh0d40x5f07xmwszdpkf82df\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"10265uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"recipient\",\"value\":\"aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p\"},{\"key\":\"sender\",\"value\":\"aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx\"},{\"key\":\"amount\",\"value\":\"51778uaura\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]},{\"type\":\"withdraw_rewards\",\"attributes\":[{\"key\":\"amount\",\"value\":\"10896uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"},{\"key\":\"amount\",\"value\":\"288uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"1\"},{\"key\":\"amount\",\"value\":\"12357uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"2\"},{\"key\":\"amount\",\"value\":\"37632uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"3\"},{\"key\":\"amount\",\"value\":\"639057uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"4\"},{\"key\":\"amount\",\"value\":\"34653uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"5\"},{\"key\":\"amount\",\"value\":\"16116uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"6\"},{\"key\":\"amount\",\"value\":\"22364uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"7\"},{\"key\":\"amount\",\"value\":\"1238uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"8\"},{\"key\":\"amount\",\"value\":\"1009uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"9\"},{\"key\":\"amount\",\"value\":\"286193uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"10\"},{\"key\":\"amount\",\"value\":\"707934uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"11\"},{\"key\":\"amount\",\"value\":\"1456uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"12\"},{\"key\":\"amount\",\"value\":\"2076997uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"13\"},{\"key\":\"amount\",\"value\":\"101237uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"14\"},{\"key\":\"amount\",\"value\":\"93532uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"15\"},{\"key\":\"amount\",\"value\":\"24804uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"16\"},{\"key\":\"amount\",\"value\":\"314414uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"17\"},{\"key\":\"amount\",\"value\":\"601484uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"18\"},{\"key\":\"amount\",\"value\":\"43215uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"19\"},{\"key\":\"amount\",\"value\":\"10265uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"20\"},{\"key\":\"amount\",\"value\":\"51778uaura\"},{\"key\":\"validator\",\"value\":\"auravaloper1z8qlzq3ag2mzccg4xr2n805swr5rl3ugdh3euf\"},{\"key\":\"authz_msg_index\",\"value\":\"21\"}]}]}]", + "info": "", + "gas_wanted": "3175474", + "gas_used": "2888215", + "events": [ + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ng==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU4OHVhdXJh", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU4OHVhdXJh", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ng==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU4OHVhdXJh", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ng==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { "key": "ZmVl", "value": "MTU4OHVhdXJh", "index": true }, + { + "key": "ZmVlX3BheWVy", + "value": "YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ng==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "YWNjX3NlcQ==", + "value": "YXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3Ni81NDA1", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "c2lnbmF0dXJl", + "value": "ZGNpTmxuaUxPMUlTd3Zkc2txek8xMkpCeFZNVER0RmtBQnp5WEVKeUJVZ0pQcGYxd094NGRaWnRJYXRreVpobUFoT0RtN2wzeUxKQjJhbm9HcUpTcnc9PQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5hdXRoei52MWJldGExLk1zZ0V4ZWM=", + "index": true + } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTA4OTZ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3Rmdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTA4OTZ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3Rmdw==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTA4OTZ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MTA4OTZ1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3Rmdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "ODc1N3VhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "ODc1N3VhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "ODc1N3VhdXJh", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "ODc1Ny4wMDAwMDAwMDAwMDAwMDAwMDA=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3Rmdw==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjg4dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjg4dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjg4dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "Mjg4dWF1cmE=", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjMxdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjMxdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MjMxdWF1cmE=", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MjMxLjAwMDAwMDAwMDAwMDAwMDAwMA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MQ==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTIzNTd1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFxM3RydWh1czd6d2hhenp1YWN6eWdjNWZ5M3U0YTJmcmtuYXZxMg==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTIzNTd1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFxM3RydWh1czd6d2hhenp1YWN6eWdjNWZ5M3U0YTJmcmtuYXZxMg==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTIzNTd1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MTIzNTd1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFxM3RydWh1czd6d2hhenp1YWN6eWdjNWZ5M3U0YTJmcmtuYXZxMg==", + "index": true + }, + { "key": "YW1vdW50", "value": "OTkzMXVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "OTkzMXVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "OTkzMXVhdXJh", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "OTkzMS4wMDAwMDAwMDAwMDAwMDAwMDA=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFxM3RydWh1czd6d2hhenp1YWN6eWdjNWZ5M3U0YTJmcmtuYXZxMg==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mg==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mzc2MzJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFoa3gzOXRlendjYzV6emRqd3RmZnowcXVwYWV6c2NrdDIzZmFrZQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mzc2MzJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFoa3gzOXRlendjYzV6emRqd3RmZnowcXVwYWV6c2NrdDIzZmFrZQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mzc2MzJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "Mzc2MzJ1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFoa3gzOXRlendjYzV6emRqd3RmZnowcXVwYWV6c2NrdDIzZmFrZQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzAyNDR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzAyNDR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MzAyNDR1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MzAyNDQuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFoa3gzOXRlendjYzV6emRqd3RmZnowcXVwYWV6c2NrdDIzZmFrZQ==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Mw==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NjM5MDU3dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFzdDVuOHg0dDd1bHV3enVhcnJremg1cDdkYzZnMjJlemd4OWY4cg==", + "index": true + }, + { "key": "YW1vdW50", "value": "NjM5MDU3dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFzdDVuOHg0dDd1bHV3enVhcnJremg1cDdkYzZnMjJlemd4OWY4cg==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NjM5MDU3dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "NjM5MDU3dWF1cmE=", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFzdDVuOHg0dDd1bHV3enVhcnJremg1cDdkYzZnMjJlemd4OWY4cg==", + "index": true + }, + { "key": "YW1vdW50", "value": "NTEzNTk2dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "NTEzNTk2dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "NTEzNTk2dWF1cmE=", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "NTEzNTk2LjAwMDAwMDAwMDAwMDAwMDAwMA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFzdDVuOHg0dDd1bHV3enVhcnJremg1cDdkYzZnMjJlemd4OWY4cg==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NA==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQ2NTN1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQ2NTN1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQ2NTN1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MzQ2NTN1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjc4NDl1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjc4NDl1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjc4NDl1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "Mjc4NDkuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "NQ==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTYxMTZ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTYxMTZ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTYxMTZ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MTYxMTZ1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTI5NTJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTI5NTJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MTI5NTJ1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MTI5NTIuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Ng==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjIzNjR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmZnFqMnE4YXNxNWFlMnR3eDJoNzZjdmpwMm04cnRyemtrbHV4YQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjIzNjR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFmZnFqMnE4YXNxNWFlMnR3eDJoNzZjdmpwMm04cnRyemtrbHV4YQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjIzNjR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MjIzNjR1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFmZnFqMnE4YXNxNWFlMnR3eDJoNzZjdmpwMm04cnRyemtrbHV4YQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTc5NzR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTc5NzR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MTc5NzR1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MTc5NzQuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFmZnFqMnE4YXNxNWFlMnR3eDJoNzZjdmpwMm04cnRyemtrbHV4YQ==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "Nw==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTIzOHVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFuanB5OXZ4N2pyYTQ1MjJlZWdwZzVxc3l2cjc0ajBuNHhmYW53Yw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTIzOHVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFuanB5OXZ4N2pyYTQ1MjJlZWdwZzVxc3l2cjc0ajBuNHhmYW53Yw==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTIzOHVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MTIzOHVhdXJh", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFuanB5OXZ4N2pyYTQ1MjJlZWdwZzVxc3l2cjc0ajBuNHhmYW53Yw==", + "index": true + }, + { "key": "YW1vdW50", "value": "OTk1dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "OTk1dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "OTk1dWF1cmE=", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "OTk1LjAwMDAwMDAwMDAwMDAwMDAwMA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFuanB5OXZ4N2pyYTQ1MjJlZWdwZzVxc3l2cjc0ajBuNHhmYW53Yw==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OA==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAwOXVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAwOXVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAwOXVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MTAwOXVhdXJh", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==", + "index": true + }, + { "key": "YW1vdW50", "value": "ODExdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "ODExdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "ODExdWF1cmE=", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "ODExLjAwMDAwMDAwMDAwMDAwMDAwMA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "OQ==", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjg2MTkzdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFyemtmNXo5cmh5Nnl5bjV4dXZha2p2eTRmdm44bXp2NTMzdmVmeA==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjg2MTkzdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFyemtmNXo5cmh5Nnl5bjV4dXZha2p2eTRmdm44bXp2NTMzdmVmeA==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "Mjg2MTkzdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "Mjg2MTkzdWF1cmE=", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFyemtmNXo5cmh5Nnl5bjV4dXZha2p2eTRmdm44bXp2NTMzdmVmeA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjMwMDA3dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjMwMDA3dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MjMwMDA3dWF1cmE=", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MjMwMDA3LjAwMDAwMDAwMDAwMDAwMDAwMA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFyemtmNXo5cmh5Nnl5bjV4dXZha2p2eTRmdm44bXp2NTMzdmVmeA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTA=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NzA3OTM0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE5YXUzYTlsdncyZnBhM2xmaDJoMDIzbjV1djYwd3VubnJreDN1aw==", + "index": true + }, + { "key": "YW1vdW50", "value": "NzA3OTM0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE5YXUzYTlsdncyZnBhM2xmaDJoMDIzbjV1djYwd3VubnJreDN1aw==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NzA3OTM0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "NzA3OTM0dWF1cmE=", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE5YXUzYTlsdncyZnBhM2xmaDJoMDIzbjV1djYwd3VubnJreDN1aw==", + "index": true + }, + { "key": "YW1vdW50", "value": "NTY4OTUxdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "NTY4OTUxdWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "NTY4OTUxdWF1cmE=", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "NTY4OTUxLjAwMDAwMDAwMDAwMDAwMDAwMA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE5YXUzYTlsdncyZnBhM2xmaDJoMDIzbjV1djYwd3VubnJreDN1aw==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTE=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTQ1NnVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTQ1NnVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTQ1NnVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MTQ1NnVhdXJh", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTE3MHVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTE3MHVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MTE3MHVhdXJh", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MTE3MC4wMDAwMDAwMDAwMDAwMDAwMDA=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTI=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjA3Njk5N3VhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTEyYTc1ZGZ5OHM0YWM3NjM1NnA4YThodDVoc3V0cDd4amNxcnp1Nw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjA3Njk5N3VhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTEyYTc1ZGZ5OHM0YWM3NjM1NnA4YThodDVoc3V0cDd4amNxcnp1Nw==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjA3Njk5N3VhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MjA3Njk5N3VhdXJh", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTEyYTc1ZGZ5OHM0YWM3NjM1NnA4YThodDVoc3V0cDd4amNxcnp1Nw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTY2OTIzN3VhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTY2OTIzN3VhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MTY2OTIzN3VhdXJh", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MTY2OTIzNy4wMDAwMDAwMDAwMDAwMDAwMDA=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTEyYTc1ZGZ5OHM0YWM3NjM1NnA4YThodDVoc3V0cDd4amNxcnp1Nw==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTM=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAxMjM3dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAxMjM3dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAxMjM3dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MTAxMjM3dWF1cmE=", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "ODEzNjJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "ODEzNjJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "ODEzNjJ1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "ODEzNjIuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbQ==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTQ=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "OTM1MzJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE0MzY3OWh0cHVwOXlreTN3bG02eWhtM2ZldWw5ZnVoNzB1emE2cg==", + "index": true + }, + { "key": "YW1vdW50", "value": "OTM1MzJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE0MzY3OWh0cHVwOXlreTN3bG02eWhtM2ZldWw5ZnVoNzB1emE2cg==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "OTM1MzJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "OTM1MzJ1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE0MzY3OWh0cHVwOXlreTN3bG02eWhtM2ZldWw5ZnVoNzB1emE2cg==", + "index": true + }, + { "key": "YW1vdW50", "value": "NzUxNjl1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "NzUxNjl1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "NzUxNjl1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "NzUxNjkuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE0MzY3OWh0cHVwOXlreTN3bG02eWhtM2ZldWw5ZnVoNzB1emE2cg==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTU=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjQ4MDR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjQ4MDR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjQ4MDR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MjQ4MDR1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTk5MzR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTk5MzR1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MTk5MzR1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MTk5MzQuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTY=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzE0NDE0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzE0NDE0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzE0NDE0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MzE0NDE0dWF1cmE=", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjUyNjg4dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjUyNjg4dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MjUyNjg4dWF1cmE=", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MjUyNjg4LjAwMDAwMDAwMDAwMDAwMDAwMA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobQ==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTc=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NjAxNDg0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NjAxNDg0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZA==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NjAxNDg0dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "NjAxNDg0dWF1cmE=", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NDgzMzk5dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "NDgzMzk5dWF1cmE=", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "NDgzMzk5dWF1cmE=", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "NDgzMzk5LjAwMDAwMDAwMDAwMDAwMDAwMA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTg=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NDMyMTV1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==", + "index": true + }, + { "key": "YW1vdW50", "value": "NDMyMTV1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NDMyMTV1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "NDMyMTV1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQ3MzF1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQ3MzF1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "MzQ3MzF1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "MzQ3MzEuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MTk=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAyNjV1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE2MzNoZ3FoN3V3c2o0M2toMGQ0MHg1ZjA3eG13c3pkcGtmODJkZg==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAyNjV1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE2MzNoZ3FoN3V3c2o0M2toMGQ0MHg1ZjA3eG13c3pkcGtmODJkZg==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTAyNjV1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "MTAyNjV1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE2MzNoZ3FoN3V3c2o0M2toMGQ0MHg1ZjA3eG13c3pkcGtmODJkZg==", + "index": true + }, + { "key": "YW1vdW50", "value": "ODI0OXVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "ODI0OXVhdXJh", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "ODI0OXVhdXJh", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "ODI0OS4wMDAwMDAwMDAwMDAwMDAwMDA=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE2MzNoZ3FoN3V3c2o0M2toMGQ0MHg1ZjA3eG13c3pkcGtmODJkZg==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjA=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NTE3Nzh1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NTE3Nzh1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NTE3Nzh1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + }, + { + "type": "withdraw_rewards", + "attributes": [ + { "key": "YW1vdW50", "value": "NTE3Nzh1YXVyYQ==", "index": true }, + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + }, + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==", + "index": true + }, + { "key": "YW1vdW50", "value": "NDE2MTJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "NDE2MTJ1YXVyYQ==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + }, + { + "type": "delegate", + "attributes": [ + { + "key": "dmFsaWRhdG9y", + "value": "YXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWY=", + "index": true + }, + { "key": "YW1vdW50", "value": "NDE2MTJ1YXVyYQ==", "index": true }, + { + "key": "bmV3X3NoYXJlcw==", + "value": "NDE2MTIuMDAwMDAwMDAwMDAwMDAwMDAw", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "c3Rha2luZw==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MjE=", "index": true } + ] + } + ], + "codespace": "" + }, + "tx": "CtEbCp4bCh0vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnRXhlYxL8GgorYXVyYTE5NHl0bjUweWhoNjdyZGhhOGFraHM3YzZ6dWxuejRuMmpwN3B3NhKXAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScAorYXVyYTFxcXE0Y2VjbTZ5dmFlcDQ2NzI5MDk2dXJxcTMwazNrcDJtY3RmdxIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDQoFdWF1cmESBDg3NTcSlgEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEm8KK2F1cmExcTkzeGt3dGZ2N251dDBlcWpqd3MzNzd3a2prOTcyNjV6c3hseDYSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGgwKBXVhdXJhEgMyMzESlwEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnAKK2F1cmExcTN0cnVodXM3endoYXp6dWFjenlnYzVmeTN1NGEyZnJrbmF2cTISMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg0KBXVhdXJhEgQ5OTMxEpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCithdXJhMWhreDM5dGV6d2NjNXp6ZGp3dGZmejBxdXBhZXpzY2t0MjNmYWtlEjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoOCgV1YXVyYRIFMzAyNDQSmQEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnIKK2F1cmExc3Q1bjh4NHQ3dWx1d3p1YXJya3poNXA3ZGM2ZzIyZXpneDlmOHISMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg8KBXVhdXJhEgY1MTM1OTYSmAEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnEKK2F1cmExcnFsbDJkNHd5eWx2bDAzaHQ2bWhnbHN3ajQ2Z2tjcjNrc3ZrbTcSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg4KBXVhdXJhEgUyNzg0ORKYAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScQorYXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNRIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDgoFdWF1cmESBTEyOTUyEpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCithdXJhMWZmcWoycThhc3E1YWUydHd4Mmg3NmN2anAybThydHJ6a2tsdXhhEjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoOCgV1YXVyYRIFMTc5NzQSlgEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEm8KK2F1cmExbmpweTl2eDdqcmE0NTIyZWVncGc1cXN5dnI3NGowbjR4ZmFud2MSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGgwKBXVhdXJhEgM5OTUSlgEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEm8KK2F1cmExZWZxNXE0dXpuNTgzbmg1bWF1emM1Y21nbXM1M3c5bDZ2czVkeHoSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGgwKBXVhdXJhEgM4MTESmQEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnIKK2F1cmExcnprZjV6OXJoeTZ5eW41eHV2YWtqdnk0ZnZuOG16djUzM3ZlZngSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg8KBXVhdXJhEgYyMzAwMDcSmQEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnIKK2F1cmExOWF1M2E5bHZ3MmZwYTNsZmgyaDAyM241dXY2MHd1bm5ya3gzdWsSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg8KBXVhdXJhEgY1Njg5NTESlwEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnAKK2F1cmExdWp2MmdtZndyd3pqNTA0bnRnZ3FsZDBxNWV1YWZwNzZ2Z3g1bGoSMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg0KBXVhdXJhEgQxMTcwEpoBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJzCithdXJhMTJhNzVkZnk4czRhYzc2MzU2cDhhOGh0NWhzdXRwN3hqY3FyenU3EjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoQCgV1YXVyYRIHMTY2OTIzNxKYAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScQorYXVyYTFtZGhkdnFjbHJ4ZHg3OGNkeDZ5bDV2NGN2cXRlemUycHk5MzNlbRIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDgoFdWF1cmESBTgxMzYyEpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCithdXJhMTQzNjc5aHRwdXA5eWt5M3dsbTZ5aG0zZmV1bDlmdWg3MHV6YTZyEjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoOCgV1YXVyYRIFNzUxNjkSmAEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEnEKK2F1cmExZnd0a3FlNHlwNjUyc3ZyajVsemR1OWxueWt5c2g5NDdtc2M0eHESMmF1cmF2YWxvcGVyMXo4cWx6cTNhZzJtemNjZzR4cjJuODA1c3dyNXJsM3VnZGgzZXVmGg4KBXVhdXJhEgUxOTkzNBKZAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScgorYXVyYTFucWh1Z3luMjB4d3JtN3k1cmN3NWxxNHNnMHFjdWx2cmpnc3RobRIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDwoFdWF1cmESBjI1MjY4OBKZAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScgorYXVyYTFmMHZ5cGM5ODg3eDdzMno5Y242M2p4ZXdhamNzNnA3NDJkNnBsZBIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDwoFdWF1cmESBjQ4MzM5ORKYAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScQorYXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dsehIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDgoFdWF1cmESBTM0NzMxEpcBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJwCithdXJhMTYzM2hncWg3dXdzajQza2gwZDQweDVmMDd4bXdzemRwa2Y4MmRmEjJhdXJhdmFsb3BlcjF6OHFsenEzYWcybXpjY2c0eHIybjgwNXN3cjVybDN1Z2RoM2V1ZhoNCgV1YXVyYRIEODI0ORKYAQojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUScQorYXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cBIyYXVyYXZhbG9wZXIxejhxbHpxM2FnMm16Y2NnNHhyMm44MDVzd3I1cmwzdWdkaDNldWYaDgoFdWF1cmESBTQxNjEyEi5SRVN0YWtlZCBieSAgQXV0b1N0YWtlIPCfm6HvuI8gU2xhc2ggUHJvdGVjdGVkEmkKUQpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOaXY9F10j5uP8HchWBOLJwIsI6JlzBPmY7Z5rEutB14xIECgIIARidKhIUCg0KBXVhdXJhEgQxNTg4ELLowQEaQHXIjZZ4iztSEsL3bJKsztdiQcVTEw7RZAAc8lxCcgVICT6X9cDseHWWbSGrZMmYZgITg5u5d8iyQdmp6BqiUq8=" + } + ], + "total_count": "1" +} diff --git a/test/unit/services/handle-vote/tx_multi_vote.fixture.json b/test/unit/services/handle-vote/tx_multi_vote.fixture.json index 2fd959b18..33ca812ea 100644 --- a/test/unit/services/handle-vote/tx_multi_vote.fixture.json +++ b/test/unit/services/handle-vote/tx_multi_vote.fixture.json @@ -1 +1,165 @@ -{ "txs": [ { "hash": "CD012FB920AB8AB432325EA6B0C16A09D731CB235AC999E64E524B7DCFDFFF74", "height": "4279260", "index": 0, "tx_result": { "code": 0, "data": "Ch0KGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZQodChsvY29zbW9zLmdvdi52MWJldGExLk1zZ1ZvdGU=", "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura17dv9s7hujzkruzmezeg4l39zfuks7mjfscddcm\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":4,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"428\"}]}]},{\"msg_index\":1,\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura17dv9s7hujzkruzmezeg4l39zfuks7mjfscddcm\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":3,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"428\"}]}]}]", "info": "", "gas_wanted": "1500000", "gas_used": "70704", "events": [ { "type": "coin_spent", "attributes": [ { "key": "c3BlbmRlcg==", "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", "index": true }, { "key": "YW1vdW50", "value": "MjUwMDB1dGF1cmE=", "index": true } ] }, { "type": "coin_received", "attributes": [ { "key": "cmVjZWl2ZXI=", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "YW1vdW50", "value": "MjUwMDB1dGF1cmE=", "index": true } ] }, { "type": "transfer", "attributes": [ { "key": "cmVjaXBpZW50", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", "index": true }, { "key": "YW1vdW50", "value": "MjUwMDB1dGF1cmE=", "index": true } ] }, { "type": "message", "attributes": [ { "key": "c2VuZGVy", "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "ZmVl", "value": "MjUwMDB1dGF1cmE=", "index": true }, { "key": "ZmVlX3BheWVy", "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "YWNjX3NlcQ==", "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbS8zMA==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "c2lnbmF0dXJl", "value": "bXNob3d0b0E4cE85RHg1SEw0Y1ZMaWNZcjJqWVhxdWNNdFQ1bVdSZWlsby9MaGVwTFljcWZQcFRibnpiZ0lhS0ltQlVrM3ZVMTdOcjRZOVN6endJQlE9PQ==", "index": true } ] }, { "type": "message", "attributes": [ { "key": "YWN0aW9u", "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", "index": true } ] }, { "type": "proposal_vote", "attributes": [ { "key": "b3B0aW9u", "value": "eyJvcHRpb24iOjQsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", "index": true }, { "key": "cHJvcG9zYWxfaWQ=", "value": "NDI4", "index": true } ] }, { "type": "message", "attributes": [ { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", "index": true } ] }, { "type": "message", "attributes": [ { "key": "YWN0aW9u", "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", "index": true } ] }, { "type": "proposal_vote", "attributes": [ { "key": "b3B0aW9u", "value": "eyJvcHRpb24iOjMsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", "index": true }, { "key": "cHJvcG9zYWxfaWQ=", "value": "NDI4", "index": true } ] }, { "type": "message", "attributes": [ { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", "index": true } ] } ], "codespace": "" }, "tx": "CrcBClEKGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZRIyCKwDEithdXJhMTdkdjlzN2h1anprcnV6bWV6ZWc0bDM5emZ1a3M3bWpmc2NkZGNtGAQKUQobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjIIrAMSK2F1cmExN2R2OXM3aHVqemtydXptZXplZzRsMzl6ZnVrczdtamZzY2RkY20YAxIPdGVzdCBncmFudCB2b3RlEmkKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQPrbKd06+uu+OVWbtUu70PrFiJtb8AhBDjJfjVwNN64BRIECgIIARgeEhUKDwoGdXRhdXJhEgUyNTAwMBDgxlsaQJrIaMLaAPKTvQ8eRy+HFS4nGK9o2F6rnDLU+ZlkXopaPy4XqS2HKnz6U25824CGiiJgVJN71Neza+GPUs88CAU=" } ], "total_count": "1" } \ No newline at end of file +{ + "txs": [ + { + "hash": "CD012FB920AB8AB432325EA6B0C16A09D731CB235AC999E64E524B7DCFDFFF74", + "height": "4279260", + "index": 0, + "tx_result": { + "code": 0, + "data": "Ch0KGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZQodChsvY29zbW9zLmdvdi52MWJldGExLk1zZ1ZvdGU=", + "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura17dv9s7hujzkruzmezeg4l39zfuks7mjfscddcm\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":4,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"428\"}]}]},{\"msg_index\":1,\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura17dv9s7hujzkruzmezeg4l39zfuks7mjfscddcm\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":3,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"428\"}]}]}]", + "info": "", + "gas_wanted": "1500000", + "gas_used": "70704", + "events": [ + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjUwMDB1dGF1cmE=", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjUwMDB1dGF1cmE=", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjUwMDB1dGF1cmE=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { "key": "ZmVl", "value": "MjUwMDB1dGF1cmE=", "index": true }, + { + "key": "ZmVlX3BheWVy", + "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "YWNjX3NlcQ==", + "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbS8zMA==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "c2lnbmF0dXJl", + "value": "bXNob3d0b0E4cE85RHg1SEw0Y1ZMaWNZcjJqWVhxdWNNdFQ1bVdSZWlsby9MaGVwTFljcWZQcFRibnpiZ0lhS0ltQlVrM3ZVMTdOcjRZOVN6endJQlE9PQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", + "index": true + } + ] + }, + { + "type": "proposal_vote", + "attributes": [ + { + "key": "b3B0aW9u", + "value": "eyJvcHRpb24iOjQsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "index": true + }, + { "key": "cHJvcG9zYWxfaWQ=", "value": "NDI4", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", + "index": true + } + ] + }, + { + "type": "proposal_vote", + "attributes": [ + { + "key": "b3B0aW9u", + "value": "eyJvcHRpb24iOjMsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "index": true + }, + { "key": "cHJvcG9zYWxfaWQ=", "value": "NDI4", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE3ZHY5czdodWp6a3J1em1lemVnNGwzOXpmdWtzN21qZnNjZGRjbQ==", + "index": true + } + ] + } + ], + "codespace": "" + }, + "tx": "CrcBClEKGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZRIyCKwDEithdXJhMTdkdjlzN2h1anprcnV6bWV6ZWc0bDM5emZ1a3M3bWpmc2NkZGNtGAQKUQobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjIIrAMSK2F1cmExN2R2OXM3aHVqemtydXptZXplZzRsMzl6ZnVrczdtamZzY2RkY20YAxIPdGVzdCBncmFudCB2b3RlEmkKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQPrbKd06+uu+OVWbtUu70PrFiJtb8AhBDjJfjVwNN64BRIECgIIARgeEhUKDwoGdXRhdXJhEgUyNTAwMBDgxlsaQJrIaMLaAPKTvQ8eRy+HFS4nGK9o2F6rnDLU+ZlkXopaPy4XqS2HKnz6U25824CGiiJgVJN71Neza+GPUs88CAU=" + } + ], + "total_count": "1" +} diff --git a/test/unit/services/handle-vote/tx_vote.fixture.json b/test/unit/services/handle-vote/tx_vote.fixture.json index 31d8c417c..0bf78337f 100644 --- a/test/unit/services/handle-vote/tx_vote.fixture.json +++ b/test/unit/services/handle-vote/tx_vote.fixture.json @@ -1 +1,133 @@ -{ "txs": [ { "hash": "176C4E8978E337E6FC1C08383C4BFEA3C5871D526BD0AC1AF84D16EA876373E6", "height": "4279260", "index": 0, "tx_result": { "code": 0, "data": "Ch0KGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZQ==", "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura1x974kw0xsasgr574h9vpaggg8dhn7yxgffvxu8\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":1,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"18\"}]}]}]", "info": "", "gas_wanted": "200000", "gas_used": "64257", "events": [ { "type": "coin_spent", "attributes": [ { "key": "c3BlbmRlcg==", "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", "index": true }, { "key": "YW1vdW50", "value": "MjAwdWVhdXJh", "index": true } ] }, { "type": "coin_received", "attributes": [ { "key": "cmVjZWl2ZXI=", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "YW1vdW50", "value": "MjAwdWVhdXJh", "index": true } ] }, { "type": "transfer", "attributes": [ { "key": "cmVjaXBpZW50", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", "index": true }, { "key": "YW1vdW50", "value": "MjAwdWVhdXJh", "index": true } ] }, { "type": "message", "attributes": [ { "key": "c2VuZGVy", "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "ZmVl", "value": "MjAwdWVhdXJh", "index": true }, { "key": "ZmVlX3BheWVy", "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "YWNjX3NlcQ==", "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OC8zMzcyMQ==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "c2lnbmF0dXJl", "value": "Q3VqejVJQkZVQVAzQk9RYVdLNVBoR3llVEZiYTdDYnpwRnFpRDkzYlJndyt4ZnZGTTFPUUpPNUJ0QndSRFMrb3RFeXlvNmhFbUp5cVlmdEtrbFFKTXc9PQ==", "index": true } ] }, { "type": "message", "attributes": [ { "key": "YWN0aW9u", "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", "index": true } ] }, { "type": "proposal_vote", "attributes": [ { "key": "b3B0aW9u", "value": "eyJvcHRpb24iOjEsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", "index": true }, { "key": "cHJvcG9zYWxfaWQ=", "value": "MTg=", "index": true } ] }, { "type": "message", "attributes": [ { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", "index": true } ] } ], "codespace": "" }, "tx": "ClIKUAobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjEIEhIrYXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OBgBEmkKUgpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQJ9fRtHBujmvhXDUaEWGVfRBQ1UupdIi0Ay8Zygq6ncBBIECgIIARi5hwISEwoNCgZ1ZWF1cmESAzIwMBDAmgwaQAro8+SARVAD9wTkGliuT4RsnkxW2uwm86Raog/d20YMPsX7xTNTkCTuQbQcEQ0vqLRMsqOoRJicqmH7SpJUCTM=" } ], "total_count": "1" } \ No newline at end of file +{ + "txs": [ + { + "hash": "176C4E8978E337E6FC1C08383C4BFEA3C5871D526BD0AC1AF84D16EA876373E6", + "height": "4279260", + "index": 0, + "tx_result": { + "code": 0, + "data": "Ch0KGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZQ==", + "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura1x974kw0xsasgr574h9vpaggg8dhn7yxgffvxu8\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":1,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"18\"}]}]}]", + "info": "", + "gas_wanted": "200000", + "gas_used": "64257", + "events": [ + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjAwdWVhdXJh", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjAwdWVhdXJh", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjAwdWVhdXJh", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { "key": "ZmVl", "value": "MjAwdWVhdXJh", "index": true }, + { + "key": "ZmVlX3BheWVy", + "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "YWNjX3NlcQ==", + "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OC8zMzcyMQ==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "c2lnbmF0dXJl", + "value": "Q3VqejVJQkZVQVAzQk9RYVdLNVBoR3llVEZiYTdDYnpwRnFpRDkzYlJndyt4ZnZGTTFPUUpPNUJ0QndSRFMrb3RFeXlvNmhFbUp5cVlmdEtrbFFKTXc9PQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", + "index": true + } + ] + }, + { + "type": "proposal_vote", + "attributes": [ + { + "key": "b3B0aW9u", + "value": "eyJvcHRpb24iOjEsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "index": true + }, + { "key": "cHJvcG9zYWxfaWQ=", "value": "MTg=", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OA==", + "index": true + } + ] + } + ], + "codespace": "" + }, + "tx": "ClIKUAobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjEIEhIrYXVyYTF4OTc0a3cweHNhc2dyNTc0aDl2cGFnZ2c4ZGhuN3l4Z2Zmdnh1OBgBEmkKUgpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQJ9fRtHBujmvhXDUaEWGVfRBQ1UupdIi0Ay8Zygq6ncBBIECgIIARi5hwISEwoNCgZ1ZWF1cmESAzIwMBDAmgwaQAro8+SARVAD9wTkGliuT4RsnkxW2uwm86Raog/d20YMPsX7xTNTkCTuQbQcEQ0vqLRMsqOoRJicqmH7SpJUCTM=" + } + ], + "total_count": "1" +} diff --git a/test/unit/services/handle-vote/tx_vote_authz.fixture.json b/test/unit/services/handle-vote/tx_vote_authz.fixture.json index 48f58e4b3..403794f1f 100644 --- a/test/unit/services/handle-vote/tx_vote_authz.fixture.json +++ b/test/unit/services/handle-vote/tx_vote_authz.fixture.json @@ -1 +1,135 @@ -{ "txs": [ { "hash": "A9E1E587ABB572DDA680EB9B31C01A1E7A13ADD34A97CE2E9125C246C92E6A57", "height": "4279350", "index": 0, "tx_result": { "code": 0, "data": "CiMKHS9jb3Ntb3MuYXV0aHoudjFiZXRhMS5Nc2dFeGVjEgIKAA==", "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.authz.v1beta1.MsgExec\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura1dyw4kvnfzj9geh75jlw4z2tgmj5g9f354wteel\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":1,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"2\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"}]}]}]", "info": "", "gas_wanted": "106176", "gas_used": "72438", "events": [ { "type": "coin_spent", "attributes": [ { "key": "c3BlbmRlcg==", "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdw==", "index": true }, { "key": "YW1vdW50", "value": "MjY1NXVhdXJh", "index": true } ] }, { "type": "coin_received", "attributes": [ { "key": "cmVjZWl2ZXI=", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "YW1vdW50", "value": "MjY1NXVhdXJh", "index": true } ] }, { "type": "transfer", "attributes": [ { "key": "cmVjaXBpZW50", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdw==", "index": true }, { "key": "YW1vdW50", "value": "MjY1NXVhdXJh", "index": true } ] }, { "type": "message", "attributes": [ { "key": "c2VuZGVy", "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdw==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "ZmVl", "value": "MjY1NXVhdXJh", "index": true }, { "key": "ZmVlX3BheWVy", "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdw==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "YWNjX3NlcQ==", "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdy8w", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "c2lnbmF0dXJl", "value": "OVkxTFcrOFNWRWlUMmRFNUdkaVI1eW9IVUpILzE3eE42TG5VOHdRelJveG1Zb25CcERtL2hsMEVlTm01TEdGYWU4cm9ySVFIOEZmK2tkYStQWHlSbnc9PQ==", "index": true } ] }, { "type": "message", "attributes": [ { "key": "YWN0aW9u", "value": "L2Nvc21vcy5hdXRoei52MWJldGExLk1zZ0V4ZWM=", "index": true } ] }, { "type": "proposal_vote", "attributes": [ { "key": "b3B0aW9u", "value": "eyJvcHRpb24iOjEsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", "index": true }, { "key": "cHJvcG9zYWxfaWQ=", "value": "Mg==", "index": true }, { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } ] }, { "type": "message", "attributes": [ { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTFkeXc0a3ZuZnpqOWdlaDc1amx3NHoydGdtajVnOWYzNTR3dGVlbA==", "index": true }, { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } ] } ], "codespace": "" }, "tx": "CqMBCqABCh0vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnRXhlYxJ/CithdXJhMTVnY3h2cGVxNGpoaDdzNjQ3Zm1wbTgydjlxZjl1eW1obWNzbjB3ElAKGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZRIxCAISK2F1cmExZHl3NGt2bmZ6ajlnZWg3NWpsdzR6MnRnbWo1ZzlmMzU0d3RlZWwYARJlCk4KRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDnXHDFeTDT9uiqnEJdHr95TPUbVETEDGXzxtpsOc9zKwSBAoCCAESEwoNCgV1YXVyYRIEMjY1NRDAvQYaQPWNS1vvElRIk9nRORnYkecqB1CR/9e8Tei51PMEM0aMZmKJwaQ5v4ZdBHjZuSxhWnvK6KyEB/BX/pHWvj18kZ8=" } ], "total_count": "1" } \ No newline at end of file +{ + "txs": [ + { + "hash": "A9E1E587ABB572DDA680EB9B31C01A1E7A13ADD34A97CE2E9125C246C92E6A57", + "height": "4279350", + "index": 0, + "tx_result": { + "code": 0, + "data": "CiMKHS9jb3Ntb3MuYXV0aHoudjFiZXRhMS5Nc2dFeGVjEgIKAA==", + "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.authz.v1beta1.MsgExec\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura1dyw4kvnfzj9geh75jlw4z2tgmj5g9f354wteel\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":1,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"2\"},{\"key\":\"authz_msg_index\",\"value\":\"0\"}]}]}]", + "info": "", + "gas_wanted": "106176", + "gas_used": "72438", + "events": [ + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjY1NXVhdXJh", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjY1NXVhdXJh", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdw==", + "index": true + }, + { "key": "YW1vdW50", "value": "MjY1NXVhdXJh", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdw==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { "key": "ZmVl", "value": "MjY1NXVhdXJh", "index": true }, + { + "key": "ZmVlX3BheWVy", + "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdw==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "YWNjX3NlcQ==", + "value": "YXVyYTE1Z2N4dnBlcTRqaGg3czY0N2ZtcG04MnY5cWY5dXltaG1jc24wdy8w", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "c2lnbmF0dXJl", + "value": "OVkxTFcrOFNWRWlUMmRFNUdkaVI1eW9IVUpILzE3eE42TG5VOHdRelJveG1Zb25CcERtL2hsMEVlTm01TEdGYWU4cm9ySVFIOEZmK2tkYStQWHlSbnc9PQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5hdXRoei52MWJldGExLk1zZ0V4ZWM=", + "index": true + } + ] + }, + { + "type": "proposal_vote", + "attributes": [ + { + "key": "b3B0aW9u", + "value": "eyJvcHRpb24iOjEsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "index": true + }, + { "key": "cHJvcG9zYWxfaWQ=", "value": "Mg==", "index": true }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTFkeXc0a3ZuZnpqOWdlaDc1amx3NHoydGdtajVnOWYzNTR3dGVlbA==", + "index": true + }, + { "key": "YXV0aHpfbXNnX2luZGV4", "value": "MA==", "index": true } + ] + } + ], + "codespace": "" + }, + "tx": "CqMBCqABCh0vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnRXhlYxJ/CithdXJhMTVnY3h2cGVxNGpoaDdzNjQ3Zm1wbTgydjlxZjl1eW1obWNzbjB3ElAKGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZRIxCAISK2F1cmExZHl3NGt2bmZ6ajlnZWg3NWpsdzR6MnRnbWo1ZzlmMzU0d3RlZWwYARJlCk4KRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDnXHDFeTDT9uiqnEJdHr95TPUbVETEDGXzxtpsOc9zKwSBAoCCAESEwoNCgV1YXVyYRIEMjY1NRDAvQYaQPWNS1vvElRIk9nRORnYkecqB1CR/9e8Tei51PMEM0aMZmKJwaQ5v4ZdBHjZuSxhWnvK6KyEB/BX/pHWvj18kZ8=" + } + ], + "total_count": "1" +} diff --git a/test/unit/services/handle-vote/tx_vote_change_option_no.fixture.json b/test/unit/services/handle-vote/tx_vote_change_option_no.fixture.json index 7af2ead27..30e14bb73 100644 --- a/test/unit/services/handle-vote/tx_vote_change_option_no.fixture.json +++ b/test/unit/services/handle-vote/tx_vote_change_option_no.fixture.json @@ -1 +1,133 @@ -{ "txs": [ { "hash": "7064D3CB43CE760112CD86CD9FCE8AAEF7AD9C65D2F1204EDC25CDF6AAD71478", "height": "6794619", "index": 0, "tx_result": { "code": 0, "data": "Ch0KGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZQ==", "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura145wvhwnjl8nlqpl990w4s6wa7yw88s6njzkjxv\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":3,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"427\"}]}]}]", "info": "", "gas_wanted": "63219", "gas_used": "62990", "events": [ { "type": "coin_spent", "attributes": [ { "key": "c3BlbmRlcg==", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true }, { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } ] }, { "type": "coin_received", "attributes": [ { "key": "cmVjZWl2ZXI=", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } ] }, { "type": "transfer", "attributes": [ { "key": "cmVjaXBpZW50", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true }, { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } ] }, { "type": "message", "attributes": [ { "key": "c2VuZGVy", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "ZmVl", "value": "MTU5dXRhdXJh", "index": true }, { "key": "ZmVlX3BheWVy", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "YWNjX3NlcQ==", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4di85", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "c2lnbmF0dXJl", "value": "MThkOEEweDlNZmI1VDJBdWl5SUlWVU9DWW40M2FrQ1NsM2ducVpRZVpEVVhFOHJiMWQ3eEtvM0FnZjRGam1sTGFrSy9SQmFxYWh1OXZacjNWZ0Zjc2c9PQ==", "index": true } ] }, { "type": "message", "attributes": [ { "key": "YWN0aW9u", "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", "index": true } ] }, { "type": "proposal_vote", "attributes": [ { "key": "b3B0aW9u", "value": "eyJvcHRpb24iOjMsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", "index": true }, { "key": "cHJvcG9zYWxfaWQ=", "value": "NDI3", "index": true } ] }, { "type": "message", "attributes": [ { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true } ] } ], "codespace": "" }, "tx": "ClMKUQobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjIIqwMSK2F1cmExNDV3dmh3bmpsOG5scXBsOTkwdzRzNndhN3l3ODhzNm5qemtqeHYYAxJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDrb2c0uIp6RGGGirk/be+wlCncddDHR+4GgTYBcyffbESBAoCCH8YCRITCg0KBnV0YXVyYRIDMTU5EPPtAxpA18d8A0x9Mfb5T2AuiyIIVUOCYn43akCSl3gnqZQeZDUXE8rb1d7xKo3Agf4FjmlLakK/RBaqahu9vZr3VgFcsg==" } ], "total_count": "1" } \ No newline at end of file +{ + "txs": [ + { + "hash": "7064D3CB43CE760112CD86CD9FCE8AAEF7AD9C65D2F1204EDC25CDF6AAD71478", + "height": "6794619", + "index": 0, + "tx_result": { + "code": 0, + "data": "Ch0KGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZQ==", + "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura145wvhwnjl8nlqpl990w4s6wa7yw88s6njzkjxv\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":3,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"427\"}]}]}]", + "info": "", + "gas_wanted": "63219", + "gas_used": "62990", + "events": [ + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { "key": "ZmVl", "value": "MTU5dXRhdXJh", "index": true }, + { + "key": "ZmVlX3BheWVy", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "YWNjX3NlcQ==", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4di85", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "c2lnbmF0dXJl", + "value": "MThkOEEweDlNZmI1VDJBdWl5SUlWVU9DWW40M2FrQ1NsM2ducVpRZVpEVVhFOHJiMWQ3eEtvM0FnZjRGam1sTGFrSy9SQmFxYWh1OXZacjNWZ0Zjc2c9PQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", + "index": true + } + ] + }, + { + "type": "proposal_vote", + "attributes": [ + { + "key": "b3B0aW9u", + "value": "eyJvcHRpb24iOjMsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "index": true + }, + { "key": "cHJvcG9zYWxfaWQ=", "value": "NDI3", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + } + ] + } + ], + "codespace": "" + }, + "tx": "ClMKUQobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjIIqwMSK2F1cmExNDV3dmh3bmpsOG5scXBsOTkwdzRzNndhN3l3ODhzNm5qemtqeHYYAxJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDrb2c0uIp6RGGGirk/be+wlCncddDHR+4GgTYBcyffbESBAoCCH8YCRITCg0KBnV0YXVyYRIDMTU5EPPtAxpA18d8A0x9Mfb5T2AuiyIIVUOCYn43akCSl3gnqZQeZDUXE8rb1d7xKo3Agf4FjmlLakK/RBaqahu9vZr3VgFcsg==" + } + ], + "total_count": "1" +} diff --git a/test/unit/services/handle-vote/tx_vote_change_option_yes.fixture.json b/test/unit/services/handle-vote/tx_vote_change_option_yes.fixture.json index 2150b102f..a26b73973 100644 --- a/test/unit/services/handle-vote/tx_vote_change_option_yes.fixture.json +++ b/test/unit/services/handle-vote/tx_vote_change_option_yes.fixture.json @@ -1 +1,133 @@ -{ "txs": [ { "hash": "77DA65F419E0A03EEBABD52C9B85E60E555DB8C1DC049ED1A23F511F88420D42", "height": "6794608", "index": 0, "tx_result": { "code": 0, "data": "Ch0KGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZQ==", "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura145wvhwnjl8nlqpl990w4s6wa7yw88s6njzkjxv\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":1,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"427\"}]}]}]", "info": "", "gas_wanted": "63219", "gas_used": "62990", "events": [ { "type": "coin_spent", "attributes": [ { "key": "c3BlbmRlcg==", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true }, { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } ] }, { "type": "coin_received", "attributes": [ { "key": "cmVjZWl2ZXI=", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } ] }, { "type": "transfer", "attributes": [ { "key": "cmVjaXBpZW50", "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true }, { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } ] }, { "type": "message", "attributes": [ { "key": "c2VuZGVy", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "ZmVl", "value": "MTU5dXRhdXJh", "index": true }, { "key": "ZmVlX3BheWVy", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "YWNjX3NlcQ==", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4di84", "index": true } ] }, { "type": "tx", "attributes": [ { "key": "c2lnbmF0dXJl", "value": "bE1aNzdQWTVwT3IwQ043cGt3VGQ0L3RTZlR3OEVzR1J1NFBlZVdPS0hDWjZYV2dlT1BKZkxPckgwVGlSSXRxbjNaeE5mcEt0VmdLeDR3M3EwUG0yaWc9PQ==", "index": true } ] }, { "type": "message", "attributes": [ { "key": "YWN0aW9u", "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", "index": true } ] }, { "type": "proposal_vote", "attributes": [ { "key": "b3B0aW9u", "value": "eyJvcHRpb24iOjEsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", "index": true }, { "key": "cHJvcG9zYWxfaWQ=", "value": "NDI3", "index": true } ] }, { "type": "message", "attributes": [ { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, { "key": "c2VuZGVy", "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", "index": true } ] } ], "codespace": "" }, "tx": "ClMKUQobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjIIqwMSK2F1cmExNDV3dmh3bmpsOG5scXBsOTkwdzRzNndhN3l3ODhzNm5qemtqeHYYARJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDrb2c0uIp6RGGGirk/be+wlCncddDHR+4GgTYBcyffbESBAoCCH8YCBITCg0KBnV0YXVyYRIDMTU5EPPtAxpAlMZ77PY5pOr0CN7pkwTd4/tSfTw8EsGRu4PeeWOKHCZ6XWgeOPJfLOrH0TiRItqn3ZxNfpKtVgKx4w3q0Pm2ig==" } ], "total_count": "1" } \ No newline at end of file +{ + "txs": [ + { + "hash": "77DA65F419E0A03EEBABD52C9B85E60E555DB8C1DC049ED1A23F511F88420D42", + "height": "6794608", + "index": 0, + "tx_result": { + "code": 0, + "data": "Ch0KGy9jb3Ntb3MuZ292LnYxYmV0YTEuTXNnVm90ZQ==", + "log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.gov.v1beta1.MsgVote\"},{\"key\":\"module\",\"value\":\"governance\"},{\"key\":\"sender\",\"value\":\"aura145wvhwnjl8nlqpl990w4s6wa7yw88s6njzkjxv\"}]},{\"type\":\"proposal_vote\",\"attributes\":[{\"key\":\"option\",\"value\":\"{\\\"option\\\":1,\\\"weight\\\":\\\"1.000000000000000000\\\"}\"},{\"key\":\"proposal_id\",\"value\":\"427\"}]}]}]", + "info": "", + "gas_wanted": "63219", + "gas_used": "62990", + "events": [ + { + "type": "coin_spent", + "attributes": [ + { + "key": "c3BlbmRlcg==", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } + ] + }, + { + "type": "coin_received", + "attributes": [ + { + "key": "cmVjZWl2ZXI=", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "cmVjaXBpZW50", + "value": "YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==", + "index": true + }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + }, + { "key": "YW1vdW50", "value": "MTU5dXRhdXJh", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "c2VuZGVy", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { "key": "ZmVl", "value": "MTU5dXRhdXJh", "index": true }, + { + "key": "ZmVlX3BheWVy", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "YWNjX3NlcQ==", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4di84", + "index": true + } + ] + }, + { + "type": "tx", + "attributes": [ + { + "key": "c2lnbmF0dXJl", + "value": "bE1aNzdQWTVwT3IwQ043cGt3VGQ0L3RTZlR3OEVzR1J1NFBlZVdPS0hDWjZYV2dlT1BKZkxPckgwVGlSSXRxbjNaeE5mcEt0VmdLeDR3M3EwUG0yaWc9PQ==", + "index": true + } + ] + }, + { + "type": "message", + "attributes": [ + { + "key": "YWN0aW9u", + "value": "L2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3Rl", + "index": true + } + ] + }, + { + "type": "proposal_vote", + "attributes": [ + { + "key": "b3B0aW9u", + "value": "eyJvcHRpb24iOjEsIndlaWdodCI6IjEuMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "index": true + }, + { "key": "cHJvcG9zYWxfaWQ=", "value": "NDI3", "index": true } + ] + }, + { + "type": "message", + "attributes": [ + { "key": "bW9kdWxl", "value": "Z292ZXJuYW5jZQ==", "index": true }, + { + "key": "c2VuZGVy", + "value": "YXVyYTE0NXd2aHduamw4bmxxcGw5OTB3NHM2d2E3eXc4OHM2bmp6a2p4dg==", + "index": true + } + ] + } + ], + "codespace": "" + }, + "tx": "ClMKUQobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjIIqwMSK2F1cmExNDV3dmh3bmpsOG5scXBsOTkwdzRzNndhN3l3ODhzNm5qemtqeHYYARJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDrb2c0uIp6RGGGirk/be+wlCncddDHR+4GgTYBcyffbESBAoCCH8YCBITCg0KBnV0YXVyYRIDMTU5EPPtAxpAlMZ77PY5pOr0CN7pkwTd4/tSfTw8EsGRu4PeeWOKHCZ6XWgeOPJfLOrH0TiRItqn3ZxNfpKtVgKx4w3q0Pm2ig==" + } + ], + "total_count": "1" +} From 49cc97d4898574b4595f3330b0b4042410b5ec4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20Th=C3=A0nh=20Phong?= <49814372+phamphong9981@users.noreply.github.com> Date: Thu, 14 Sep 2023 10:02:58 +0700 Subject: [PATCH 52/54] Feat/ibc app ( main ) (#321) * feat: crawl ibc tao * feat: crawl genesis ibc tao * fix: code * fix: code * feat: ibc app * feat: ibc app * test: ibc app * fix: review --- ci/config.json.ci | 5 + config.json | 5 + .../20230810094108_create_ibc_tx_model.ts | 23 + src/common/constant.ts | 5 + src/models/event.ts | 2 + src/models/event_attribute.ts | 6 + src/models/ibc_message.ts | 93 ++++ src/models/index.ts | 1 + src/services/ibc/crawl_ibc_app.service.ts | 125 +++++ test/unit/services/ibc/crawl_ibc_app.spec.ts | 472 ++++++++++++++++++ 10 files changed, 737 insertions(+) create mode 100644 migrations/20230810094108_create_ibc_tx_model.ts create mode 100644 src/models/ibc_message.ts create mode 100644 src/services/ibc/crawl_ibc_app.service.ts create mode 100644 test/unit/services/ibc/crawl_ibc_app.spec.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index afc6cd564..a2dd5d87c 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -179,5 +179,10 @@ "key": "crawlIbcTao", "millisecondRepeatJob": 2000, "blocksPerCall": 100 + }, + "crawlIbcApp": { + "key": "crawlIbcApp", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100 } } diff --git a/config.json b/config.json index 686cbb866..731b618f4 100644 --- a/config.json +++ b/config.json @@ -179,5 +179,10 @@ "key": "crawlIbcTao", "millisecondRepeatJob": 2000, "blocksPerCall": 100 + }, + "crawlIbcApp": { + "key": "crawlIbcApp", + "millisecondRepeatJob": 2000, + "blocksPerCall": 100 } } diff --git a/migrations/20230810094108_create_ibc_tx_model.ts b/migrations/20230810094108_create_ibc_tx_model.ts new file mode 100644 index 000000000..3a376120f --- /dev/null +++ b/migrations/20230810094108_create_ibc_tx_model.ts @@ -0,0 +1,23 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('ibc_message', (table) => { + table.increments(); + table.integer('transaction_message_id').index(); + table.string('src_channel_id').notNullable().index(); + table.string('src_port_id').notNullable().index(); + table.string('dst_channel_id').notNullable().index(); + table.string('dst_port_id').notNullable().index(); + table.string('type').notNullable().index(); + table.integer('sequence').notNullable().index(); + table.string('sequence_key').notNullable().index(); + table.jsonb('data'); + table + .foreign('transaction_message_id') + .references('transaction_message.id'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists('ibc_message'); +} diff --git a/src/common/constant.ts b/src/common/constant.ts index 465d9b5ad..a0a60bed2 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -73,6 +73,7 @@ export const BULL_JOB_NAME = { JOB_REDECODE_TX: 'job:redecode-tx', CRAWL_IBC_TAO: 'crawl:ibc-tao', CRAWL_GENESIS_IBC_TAO: 'crawl:genesis-ibc-tao', + CRAWL_IBC_APP: 'crawl:ibc-app', REINDEX_CW20_CONTRACT: 'reindex:cw20-contract', REINDEX_CW20_HISTORY: 'reindex:cw20-history', }; @@ -236,6 +237,10 @@ export const SERVICE = { key: 'CrawlIBCTaoService', name: 'v1.CrawlIBCTaoService', }, + CrawlIBCAppService: { + key: 'CrawlIBCAppService', + name: 'v1.CrawlIBCAppService', + }, DailyStatisticsService: { key: 'DailyStatisticsService', name: 'v1.DailyStatisticsService', diff --git a/src/models/event.ts b/src/models/event.ts index da9a05b6b..13dfe8e3e 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -20,6 +20,8 @@ export class Event extends BaseModel { source!: string; + attributes!: EventAttribute[]; + static get tableName() { return 'event'; } diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 48a8d4f7d..fa4b7d3e3 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -105,6 +105,12 @@ export class EventAttribute extends BaseModel { PORT_ID: 'port_id', COUNTERPARTY_PORT_ID: 'counterparty_port_id', COUNTERPARTY_CHANNEL_ID: 'counterparty_channel_id', + DATA_HEX: 'packet_data_hex', + SEQUENCE: 'packet_sequence', + SRC_PORT: 'packet_src_port', + SRC_CHANNEL: 'packet_src_channel', + DST_PORT: 'packet_dst_port', + DST_CHANNEL: 'packet_dst_channel', MINTER: 'minter', FEE: 'fee', FEE_PAYER: 'fee_payer', diff --git a/src/models/ibc_message.ts b/src/models/ibc_message.ts new file mode 100644 index 000000000..c393c3a2b --- /dev/null +++ b/src/models/ibc_message.ts @@ -0,0 +1,93 @@ +/* eslint-disable import/no-cycle */ +import { Model } from 'objection'; +import BaseModel from './base'; +import { IbcChannel } from './ibc_channel'; +import { TransactionMessage } from './transaction_message'; + +export class IbcMessage extends BaseModel { + id!: number; + + transaction_message_id!: number; + + src_channel_id!: string; + + src_port_id!: string; + + dst_channel_id!: string; + + dst_port_id!: string; + + type!: string; + + sequence!: number; + + sequence_key!: string; + + data!: any; + + static get tableName() { + return 'ibc_message'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'transaction_message_id', + 'src_channel_id', + 'src_port_id', + 'dst_channel_id', + 'dst_port_id', + 'type', + 'sequence', + 'sequence_key', + ], + properties: { + transaction_message_id: { type: 'number' }, + src_channel_id: { type: 'string' }, + src_port_id: { type: 'string' }, + dst_channel_id: { type: 'string' }, + dst_port_id: { type: 'string' }, + type: { type: 'string' }, + sequence: { type: 'number' }, + sequence_key: { type: 'string' }, + }, + }; + } + + static get relationMappings() { + return { + message: { + relation: Model.BelongsToOneRelation, + modelClass: TransactionMessage, + join: { + from: 'ibc_message.transaction_message_id', + to: 'transaction_message.id', + }, + }, + src_channel: { + relation: Model.BelongsToOneRelation, + modelClass: IbcChannel, + join: { + from: 'ibc_message.src_channel_id', + to: 'ibc_channel.id', + }, + }, + dst_channel: { + relation: Model.BelongsToOneRelation, + modelClass: IbcChannel, + join: { + from: 'ibc_message.dst_channel_id', + to: 'ibc_channel.id', + }, + }, + }; + } + + static EVENT_TYPE = { + SEND_PACKET: 'send_packet', + RECV_PACKET: 'recv_packet', + ACKNOWLEDGE_PACKET: 'acknowledge_packet', + TIMEOUT_PACKET: 'timeout_packet', + }; +} diff --git a/src/models/index.ts b/src/models/index.ts index 02310cefe..bddfaad36 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -28,3 +28,4 @@ export * from './cw20_total_holder_stats'; export * from './ibc_client'; export * from './ibc_connection'; export * from './ibc_channel'; +export * from './ibc_message'; diff --git a/src/services/ibc/crawl_ibc_app.service.ts b/src/services/ibc/crawl_ibc_app.service.ts new file mode 100644 index 000000000..7ae993bf3 --- /dev/null +++ b/src/services/ibc/crawl_ibc_app.service.ts @@ -0,0 +1,125 @@ +import { fromHex, fromUtf8 } from '@cosmjs/encoding'; +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { Knex } from 'knex'; +import { ServiceBroker } from 'moleculer'; +import knex from '../../common/utils/db_connection'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import { getAttributeFrom } from '../../common/utils/smart_contract'; +import { + BlockCheckpoint, + Event, + EventAttribute, + IbcMessage, +} from '../../models'; + +@Service({ + name: SERVICE.V1.CrawlIBCAppService.key, + version: 1, +}) +export default class CrawlIbcAppService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_IBC_APP, + jobName: BULL_JOB_NAME.CRAWL_IBC_APP, + }) + public async crawlIbcApp(): Promise { + const [startHeight, endHeight, updateBlockCheckpoint] = + await BlockCheckpoint.getCheckpoint( + BULL_JOB_NAME.CRAWL_IBC_APP, + [BULL_JOB_NAME.CRAWL_IBC_TAO], + config.crawlIbcTao.key + ); + this.logger.info( + `Handle IBC/APP, startHeight: ${startHeight}, endHeight: ${endHeight}` + ); + if (startHeight > endHeight) return; + const events = await Event.query() + .withGraphFetched('attributes') + .joinRelated('message') + .select('event.id', 'event.type', 'message.id as message_id') + .whereIn('event.type', [ + IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET, + IbcMessage.EVENT_TYPE.RECV_PACKET, + IbcMessage.EVENT_TYPE.SEND_PACKET, + IbcMessage.EVENT_TYPE.TIMEOUT_PACKET, + ]) + .andWhere('event.block_height', '>', startHeight) + .andWhere('event.block_height', '<=', endHeight) + .orderBy('event.id'); + await knex.transaction(async (trx) => { + await this.handleIbcMessage(events, trx); + updateBlockCheckpoint.height = endHeight; + await BlockCheckpoint.query() + .transacting(trx) + .insert(updateBlockCheckpoint) + .onConflict('job_name') + .merge(); + }); + } + + async handleIbcMessage(events: Event[], trx: Knex.Transaction) { + const ibcMessages = events.map((event) => { + const srcChannel = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.SRC_CHANNEL + ); + const srcPort = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.SRC_PORT + ); + const dstChannel = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.DST_CHANNEL + ); + const dstPort = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.DST_PORT + ); + const sequence = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.SEQUENCE + ); + const dataHex = getAttributeFrom( + event.attributes, + EventAttribute.ATTRIBUTE_KEY.DATA_HEX + ); + return IbcMessage.fromJson({ + transaction_message_id: event.message_id, + src_channel_id: srcChannel, + src_port_id: srcPort, + dst_channel_id: dstChannel, + dst_port_id: dstPort, + type: event.type, + sequence, + sequence_key: `${srcChannel}.${srcPort}.${dstChannel}.${dstPort}.${sequence}`, + data: dataHex ? fromUtf8(fromHex(dataHex)) : null, + }); + }); + if (ibcMessages.length > 0) { + await IbcMessage.query().insert(ibcMessages).transacting(trx); + } + } + + async _start(): Promise { + await this.createJob( + BULL_JOB_NAME.CRAWL_IBC_APP, + BULL_JOB_NAME.CRAWL_IBC_APP, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + every: config.crawlIbcApp.millisecondRepeatJob, + }, + } + ); + return super._start(); + } +} diff --git a/test/unit/services/ibc/crawl_ibc_app.spec.ts b/test/unit/services/ibc/crawl_ibc_app.spec.ts new file mode 100644 index 000000000..f1776e01d --- /dev/null +++ b/test/unit/services/ibc/crawl_ibc_app.spec.ts @@ -0,0 +1,472 @@ +import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core'; +import { ServiceBroker } from 'moleculer'; +import knex from '../../../../src/common/utils/db_connection'; +import { Block, Event, IbcMessage, Transaction } from '../../../../src/models'; +import CrawlIbcAppService from '../../../../src/services/ibc/crawl_ibc_app.service'; + +@Describe('Test crawl ibc service') +export default class CrawlIbcTest { + broker = new ServiceBroker({ logger: false }); + + crawlIbcAppSerivce = this.broker.createService( + CrawlIbcAppService + ) as CrawlIbcAppService; + + block: Block = Block.fromJson({ + height: 1300000, + hash: '4801997745BDD354C8F11CE4A4137237194099E664CD8F83A5FBA9041C43FE9F', + time: '2023-01-12T01:53:57.216Z', + proposer_address: 'auraomd;cvpio3j4eg', + data: {}, + }); + + transaction = { + ...Transaction.fromJson({ + height: this.block.height, + hash: '4A8B0DE950F563553A81360D4782F6EC451F6BEF7AC50E2459D1997FA168997D', + codespace: '', + code: 0, + gas_used: '123035', + gas_wanted: '141106', + gas_limit: '141106', + fee: 353, + timestamp: '2023-01-12T01:53:57.000Z', + index: 0, + data: { + tx_response: { + logs: [], + }, + }, + }), + messages: [ + { + index: 1, + type: '/cosmwasm.wasm.v1.MsgExecuteContract', + sender: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + content: { + msg: '{"add_mint_phase":{"phase_data":{"start_time":"1679976124941000000","end_time":"1679982024941000000","max_supply":2000,"max_nfts_per_address":20,"price":{"amount":"10","denom":"ueaura"},"is_public":false},"token_id": "test"}}', + '@type': '/cosmwasm.wasm.v1.MsgExecuteContract', + funds: [], + sender: 'aura1uh24g2lc8hvvkaaf7awz25lrh5fptthu2dhq0n', + }, + }, + ], + }; + + @BeforeAll() + async initSuite() { + this.crawlIbcAppSerivce.getQueueManager().stopAll(); + await knex.raw( + 'TRUNCATE TABLE block, transaction, ibc_message RESTART IDENTITY CASCADE' + ); + await Block.query().insert(this.block); + await Transaction.query().insertGraph(this.transaction); + } + + @AfterAll() + async tearDown() { + await this.broker.stop(); + } + + @Test('Test HandleIbcSendPacket') + async testHandleIbcSendPacket() { + await knex.transaction(async (trx) => { + const content = { + '@type': '/ibc.applications.transfer.v1.MsgTransfer', + token: { + denom: 'ueaura', + amount: '2000000', + }, + sender: 'aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw', + receiver: 'axelar1qqq4cecm6yvaep46729096urqq30k3kp4repqk', + source_port: 'transfer', + source_channel: 'channel-5', + timeout_height: { + revision_height: '5272159', + revision_number: '3', + }, + timeout_timestamp: '0', + }; + const attributes = [ + { + key: 'packet_data', + value: + '{"amount":"2000000","denom":"ueaura","receiver":"axelar1qqq4cecm6yvaep46729096urqq30k3kp4repqk","sender":"aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw"}', + event_id: '1', + }, + { + event_id: '1', + key: 'packet_data_hex', + value: + '7b22616d6f756e74223a2232303030303030222c2264656e6f6d223a22756561757261222c227265636569766572223a226178656c617231717171346365636d36797661657034363732393039367572717133306b336b7034726570716b222c2273656e646572223a226175726131717171346365636d36797661657034363732393039367572717133306b336b70326d63746677227d', + }, + { + event_id: '1', + key: 'packet_timeout_height', + value: '3-5272159', + }, + { + event_id: '1', + key: 'packet_timeout_timestamp', + value: '0', + }, + { + event_id: '1', + key: 'packet_sequence', + value: '68', + }, + { + event_id: '1', + key: 'packet_src_port', + value: 'transfer', + }, + { + event_id: '1', + key: 'packet_src_channel', + value: 'channel-5', + }, + { + event_id: '1', + key: 'packet_dst_port', + value: 'transfer', + }, + { + event_id: '1', + key: 'packet_dst_channel', + value: 'channel-48', + }, + { + event_id: '1', + key: 'packet_channel_ordering', + value: 'ORDER_UNORDERED', + }, + { + event_id: '1', + key: 'packet_connection', + value: 'connection-3', + }, + ]; + const event = Event.fromJson({ + type: IbcMessage.EVENT_TYPE.SEND_PACKET, + attributes, + content, + message_id: 1, + }); + await this.crawlIbcAppSerivce.handleIbcMessage([event], trx); + const newPacket = await IbcMessage.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newPacket.src_channel_id).toEqual(attributes[6].value); + expect(newPacket.src_port_id).toEqual(attributes[5].value); + expect(newPacket.dst_port_id).toEqual(attributes[7].value); + expect(newPacket.dst_channel_id).toEqual(attributes[8].value); + expect(newPacket.type).toEqual(event.type); + expect(newPacket.sequence).toEqual(parseInt(attributes[4].value, 10)); + expect(newPacket.data).toEqual(JSON.parse(attributes[0].value)); + await trx.rollback(); + }); + } + + @Test('Test HandleIbcReceivePacket') + async testHandleIbcReceivePacket() { + await knex.transaction(async (trx) => { + const content = { + '@type': '/ibc.core.channel.v1.MsgRecvPacket', + packet: { + data: 'eyJzb3VyY2VfaWQiOiJkcmFuZDo4OTkwZTdhOWFhZWQyZmZlZDczZGJkNzA5MjEyM2Q2ZjI4OTkzMDU0MGQ3NjUxMzM2MjI1ZGMxNzJlNTFiMmNlOjI1MTg3NDMiLCJyYW5kb21uZXNzIjoiYzY3ZmI4NzBmZDkyOTY1YmJjOTIzMjFiYmZlNzEyZmE3ZDhlZTI3NzUwNzg5NDdjOGIxYmZjZTcwOTIwNGRjNyIsInNlbmRlciI6ImF1cmExbW5kY2V1Y3czYzZqOXNxanFkbWxzNGNmcHhrNWUwbTRwMGF4ZnJwN3p3c2ZuM2tuZmg0czJ5a2Z6cyIsImpvYl9pZCI6ImpvYiB0ZXN0In0=', + sequence: '1', + source_port: + 'wasm.nois1s9ly26evj8ehurptws5d6dm4a9g2z0htcqvlvn95kc30eucl4s5sd8hkgp', + source_channel: 'channel-24', + destination_port: + 'wasm.aura1qrf8f9kyh4zzckz2zy52z5gppwweumvrlxqrgd4xr3ydf3sx4dlqt8lnt8', + timeout_timestamp: '1671007842016312592', + destination_channel: 'channel-15', + }, + signer: 'aura1rynf2jnj38sh96xztpp4f7v93ceefp0592kqu9', + proof_height: { + revision_height: '2051376', + revision_number: '0', + }, + proof_commitment: + 'CrUICrIICnZjb21taXRtZW50cy9wb3J0cy93YXNtLm5vaXMxczlseTI2ZXZqOGVodXJwdHdzNWQ2ZG00YTlnMnowaHRjcXZsdm45NWtjMzBldWNsNHM1c2Q4aGtncC9jaGFubmVscy9jaGFubmVsLTI0L3NlcXVlbmNlcy8xEiBr6YV/hrYcXxxkci3Xg1iZO+R16mZdozncKdfF2Gk63hoOCAEYASABKgYAAra0+gEiLAgBEigCBNy0+gEgkbRLrBYqrFQSnylScee3SDJG9IoyvSE1NNKbhkMHjOkgIiwIARIoBAjctPoBIFJzzlvVwrtiCBp6IW4mz1U331PxUnDG3SWldAaUkqfKICIsCAESKAYO3LT6ASCjyAp5hOD2V8Ief2zE1EHS9ij+4lqh8GuQFftw1HGXySAiLggBEgcIHNy0+gEgGiEgJwCb/SGlVkH+fKcOy+VGdm52r9/CAIOXf3YJiLqBB1EiLAgBEigMRty0+gEg/Ydg1h01rorKZB7q9yuNLzyIogB6CEsbkyAbvDpoRZ0gIiwIARIoDnbctPoBIJc1GUCFYMDe51mf7/PX8k7Nsygm/97skEy3Md/hB9TSICItCAESKRCwAty0+gEgGY4ZdbCvnIfCl6jIyIpEYN3s27Fr1vyz8R3IHvX3Xm4gIi0IARIpErAF3LT6ASCRW2Sj0oCdYO4sfTggs1obpqP0SpyxZ5MNwnDNxOttzCAiLQgBEikUvAnctPoBIKc6KVNR+IjCyjUf5EmvbhCZ4ECjFfvkTS07NGG6KatHICItCAESKRb8Ddy0+gEgIk0VggsrBqM0r7bRn8tGGGb/F0OAsrkFEQlX/iIieCAgIi0IARIpGNIo3LT6ASCzl9mIXiGdiLc3Uxffs3deucwOkYLHyrOPT43POSwokSAiLwgBEggawDbctPoBIBohIDh+V4oXbhCCw2amCons5wswvWLj9GrgDmXX9ZyHzSnHIi4IARIqHsycAdy0+gEgd8O9IlSfy76C4PEut+OmsBaczmZAQIk+9lKXjKi5tPQgIi4IARIqIOSKBNy0+gEgRGhbs7TW2G7HEGOkWlnscrIJzrGFK7Av1al0Kn0evkIgIjAIARIJIu6GBdy0+gEgGiEgdYbS/e3tGdCHBK0jStUByTHfWzSAgSTuPFSABAtrvUIiMAgBEgkknPEG3LT6ASAaISAknxyxnWyS0b5tJENZdboudAgOecP6nXWoNxKCyEviaiIwCAESCSa2zw3ctPoBIBohIPNrCQdaLVoHfv59FzZZ0mu0dpj4s1yHQJDSXgI9LgyXIi4IARIqKIzpIdy0+gEgO8+y5H2wuEYl5+E29n5CHoa9L0Gv7VDK/c18U3RbpbggIi4IARIqLMjqSty0+gEg1bxzwt2bpr7IcwoFmALxeBtNJnp9orfUEwo/J2ejlo8gCoACCv0BCgNpYmMSIKpvHTP9V5wDV+p0HZXGImeQnu1m2wR4GuhY+MtrIZQNGgkIARgBIAEqAQAiJwgBEgEBGiDFBcD9SLHPK2VhnxKzFE4ZxgtOa2JSXuk2vpPQQWI+vyInCAESAQEaIB383KmKPeE797r2aZUc+sDGl1kl76j7wuBRZXW53aliIicIARIBARogHzoHZq3bsksS+szusIxZLYKclF851/17/U+U55WvjH0iJQgBEiEBh8vGkXW12Po8VvqN1YvoDbUvWlI7YprnoRvl4apyqUQiJwgBEgEBGiBWtkpHzlP2mQZ1Go55X55ylakOo+xlkABwsGtDJUAQ5Q==', + }; + const attributes = [ + { + event_id: 1, + key: 'packet_data', + value: + '{"source_id":"drand:8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce:2518743","randomness":"c67fb870fd92965bbc92321bbfe712fa7d8ee2775078947c8b1bfce709204dc7","sender":"aura1mndceucw3c6j9sqjqdmls4cfpxk5e0m4p0axfrp7zwsfn3knfh4s2ykfzs","job_id":"job test"}', + }, + { + event_id: 1, + key: 'packet_data_hex', + value: + '7b22736f757263655f6964223a226472616e643a383939306537613961616564326666656437336462643730393231323364366632383939333035343064373635313333363232356463313732653531623263653a32353138373433222c2272616e646f6d6e657373223a2263363766623837306664393239363562626339323332316262666537313266613764386565323737353037383934376338623162666365373039323034646337222c2273656e646572223a2261757261316d6e6463657563773363366a3973716a71646d6c7334636670786b3565306d3470306178667270377a7773666e336b6e6668347332796b667a73222c226a6f625f6964223a226a6f622074657374227d', + }, + { + event_id: 1, + key: 'packet_timeout_height', + value: '0-0', + }, + { + event_id: 1, + key: 'packet_timeout_timestamp', + value: '1671007842016312592', + }, + { + event_id: 1, + key: 'packet_sequence', + value: '1', + }, + { + event_id: 1, + key: 'packet_src_port', + value: + 'wasm.nois1s9ly26evj8ehurptws5d6dm4a9g2z0htcqvlvn95kc30eucl4s5sd8hkgp', + }, + { + event_id: 1, + key: 'packet_src_channel', + value: 'channel-24', + }, + { + event_id: 1, + key: 'packet_dst_port', + value: + 'wasm.aura1qrf8f9kyh4zzckz2zy52z5gppwweumvrlxqrgd4xr3ydf3sx4dlqt8lnt8', + }, + { + event_id: 1, + key: 'packet_dst_channel', + value: 'channel-15', + }, + { + event_id: 1, + key: 'packet_channel_ordering', + value: 'ORDER_UNORDERED', + }, + { + event_id: 1, + key: 'packet_connection', + value: 'connection-18', + }, + ]; + const event = Event.fromJson({ + type: IbcMessage.EVENT_TYPE.RECV_PACKET, + attributes, + content, + message_id: 1, + }); + await this.crawlIbcAppSerivce.handleIbcMessage([event], trx); + const newPacket = await IbcMessage.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newPacket.src_channel_id).toEqual(attributes[6].value); + expect(newPacket.src_port_id).toEqual(attributes[5].value); + expect(newPacket.dst_port_id).toEqual(attributes[7].value); + expect(newPacket.dst_channel_id).toEqual(attributes[8].value); + expect(newPacket.type).toEqual(event.type); + expect(newPacket.sequence).toEqual(parseInt(attributes[4].value, 10)); + expect(newPacket.data).toEqual(JSON.parse(attributes[0].value)); + await trx.rollback(); + }); + } + + @Test('Test HandleIbcAckPacket') + async testHandleIbcAckPacket() { + await knex.transaction(async (trx) => { + const content = { + '@type': '/ibc.core.channel.v1.MsgAcknowledgement', + packet: { + data: { + denom: 'ueaura', + amount: '2000000', + sender: 'aura1qqq4cecm6yvaep46729096urqq30k3kp2mctfw', + receiver: 'axelar1qqq4cecm6yvaep46729096urqq30k3kp4repqk', + }, + sequence: '70', + source_port: 'transfer', + source_channel: 'channel-5', + timeout_height: { + revision_height: '5272433', + revision_number: '3', + }, + destination_port: 'transfer', + timeout_timestamp: '0', + destination_channel: 'channel-48', + }, + signer: 'aura144uvpm209vujung88063yguqu2t4pxam8qlzcl', + proof_acked: + 'CvcHCvQHCjRhY2tzL3BvcnRzL3RyYW5zZmVyL2NoYW5uZWxzL2NoYW5uZWwtNDgvc2VxdWVuY2VzLzcwEiDk4mS81on8LJWRjs2NgRMw+qa5Riss4mL5z7CaATu7zxoOCAEYASABKgYAAtTLgwUiLAgBEigCBNTLgwUgsY73NAQp95aEYITDO1TYaZ89+2YcDYor+a1LC4XJctkgIi4IARIHBAbUy4MFIBohIIlBYKacV7IR2q8WXyJnVWmAeZVic61uXSehtFr2y702Ii4IARIHBgrUy4MFIBohIDHUbUw8RoCZl2Wx0dz54+WUsbBfDa6Zmexb/7ds7FrlIiwIARIoCBLUy4MFIJcwzVz1/aEQTrPbUSXeOu6V0GIwR9B5Fe4LgNPGP2HsICIsCAESKAoq1MuDBSBEUv4A7XistOZCvSLVYneqE5eyJVL71u8dlQM269ks1CAiLggBEgcMRNTLgwUgGiEgK4rDgfNiiMmzxJCBnFgfdk7gotZJUwuTisUwWIALky8iLAgBEigOcNTLgwUggR67/Cj4/9HbvCMEVApYUOyrpNKPi+ixw3Hu69oOEM0gIi0IARIpEL4B1MuDBSBLtWls5uYSbwyCEqFrRX0qrkskHYFpxl6iMqUsbg15TCAiLQgBEikUhAbUy4MFIGPlCJx+DRlvaAdVOlmkdR4yrwa12u7X+MNuJWnstcm+ICItCAESKRbQDNTLgwUgne8KWVfwUTHwtKt+N4JeuLA2eTdMLTPae16sBleZ9i8gIi8IARIIGJwU1MuDBSAaISAlyQEy4co8HScp9mOwxmT+54xC60l/RujMLZ0UQY1D1yItCAESKRqoJ9TLgwUg4/16hHKjfU/b/cGJ/elVImTflmDbNGJTUmH7TyAHDY4gIi0IARIpHJ5P1MuDBSCrlFOsxMBTwG/ULmdZd1FxNzMclVji1hITNEIjuTzeUiAiMAgBEgkeloQB1MuDBSAaISBamzC6oAWPluSakQhJDDL28Ick6W7bzK3Wci+zaQ5ZmCIwCAESCSCYgQLUy4MFIBohIO2Q0PKbX4ycf0DLLoiM46g6/cIGa/YuXoGnybF/ajXcIi4IARIqIuTGA9TLgwUgl7ev7UVaOt4Y+2+3MzrCd4I5tCy/QnXC6HW2cS3Jw/AgIi4IARIqJNLIBtTLgwUgIqkmWLJRQoaumYnv30l160wf56bqxe5VlqeyNKdvnd0gIjAIARIJKMrtFNTLgwUgGiEgAGipp/NfgeyJ/oAnXFeaxChIBfsJIYlHqPerz5RYV18iMAgBEgkqvOgg1MuDBSAaISBvQKHSn0bGkrQOLwDrR0dLpqOWo13BUGWEZD9cuYS5owr+AQr7AQoDaWJjEiAXBMSV5wfjh6PYbvEp/1nimbltZtOVgouKXb0KZtuXxxoJCAEYASABKgEAIiUIARIhAYaEml36WNEYpP9Nh1yNmrYXZTzq4bYigQ5h/xryTzNeIicIARIBARogFc/U9A/9Uia3YLYvLg9IUjtNLuXbpHoWAIptWKyS3/UiJwgBEgEBGiBjazpYRtM9Y+HZBtOQHXcWeJo3Ay4wqT//MpJ91bxz/CIlCAESIQHU1rzKX7ZidvkTePQ5DYHMxbnqKGC8jQoT9fF3ninSTyInCAESAQEaIEB8bwhFio2i6oAVI0jph4+6adeJMizyI8IFM6iUM+gK', + proof_height: { + revision_height: '5272299', + revision_number: '3', + }, + acknowledgement: { + error: + 'ABCI code: 1: error handling packet on destination chain: see events for details', + }, + }; + const attributes = [ + { + key: 'packet_timeout_height', + value: '3-5272433', + event_id: 1, + }, + { + key: 'packet_timeout_timestamp', + value: '0', + event_id: 1, + }, + { + key: 'packet_sequence', + value: '70', + event_id: 1, + }, + { + key: 'packet_src_port', + value: 'transfer', + event_id: 1, + }, + { + key: 'packet_src_channel', + value: 'channel-5', + event_id: 1, + }, + { + key: 'packet_dst_port', + value: 'transfer', + event_id: 1, + }, + { + key: 'packet_dst_channel', + value: 'channel-48', + event_id: 1, + }, + { + key: 'packet_channel_ordering', + value: 'ORDER_UNORDERED', + event_id: 1, + }, + { + key: 'packet_connection', + value: 'connection-3', + event_id: 1, + }, + ]; + const event = Event.fromJson({ + type: IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET, + attributes, + content, + message_id: 1, + }); + await this.crawlIbcAppSerivce.handleIbcMessage([event], trx); + const newPacket = await IbcMessage.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newPacket.src_channel_id).toEqual(attributes[4].value); + expect(newPacket.src_port_id).toEqual(attributes[3].value); + expect(newPacket.dst_port_id).toEqual(attributes[5].value); + expect(newPacket.dst_channel_id).toEqual(attributes[6].value); + expect(newPacket.type).toEqual(event.type); + expect(newPacket.sequence).toEqual(parseInt(attributes[2].value, 10)); + expect(newPacket.data).toEqual(null); + await trx.rollback(); + }); + } + + @Test('Test HandleIbcTimeoutPacket') + async testHandleIbcTimeoutPacket() { + await knex.transaction(async (trx) => { + const content = { + '@type': '/ibc.core.channel.v1.MsgTimeout', + packet: { + data: 'eyJhbW91bnQiOiIxMDAwIiwiZGVub20iOiJ1ZWF1cmEiLCJyZWNlaXZlciI6ImF4ZWxhcjF4cW4ydG5yZTg0Y211d2Z1ZGZmcnd4bXFrMDcwMnk2ZjNuNmwzZyIsInNlbmRlciI6ImF1cmExOGh3eWZ5dHZqY3VoN2praDR1ZTluNjBoMjR4cDd0bndoamh5bTYifQ==', + sequence: '76', + source_port: 'transfer', + source_channel: 'channel-5', + timeout_height: { + revision_height: '6254585', + revision_number: '3', + }, + destination_port: 'transfer', + timeout_timestamp: '1676722677558827165', + destination_channel: 'channel-48', + }, + signer: 'aura1wzjuznqenty8ryv43sq08w6rp8g25hye7wmy38', + proof_height: { + revision_height: '6268541', + revision_number: '3', + }, + proof_unreceived: + 'CogPEoUPCjhyZWNlaXB0cy9wb3J0cy90cmFuc2Zlci9jaGFubmVscy9jaGFubmVsLTQ4L3NlcXVlbmNlcy83NhKhBwo4cmVjZWlwdHMvcG9ydHMvdHJhbnNmZXIvY2hhbm5lbHMvY2hhbm5lbC00OC9zZXF1ZW5jZXMvNzUSAQEaDggBGAEgASoGAAKe48MFIiwIARIoAgSe48MFIN3Vb+hlVv+zvhy3kd1THkOR4SFjcrTbuQqXwiNhpFMCICIsCAESKAQGnuPDBSBQTbdqCqNqQAB53hvr5gfuesvX2fn9OK0XQZJ3Zzxf5yAiLAgBEigGCp7jwwUg1nx/OtK/sGaeHNT+VJWN50hwXGWsoo5dBfqE+a8ICfcgIi4IARIHCiKIw98FIBohIDvfZH45Z13if/Ny+54SgV2U2FJ3jpcABF9qpE1482ECIiwIARIoDDqIw98FINku+gKJjsRa5Tfa8Y2R4lR1kY+IYxCWALmhFTSTfuZ/ICIsCAESKA5kiMPfBSCBI5WGqXmXQBcRaVXB2YEeIkgph1r3xyMGXnwmwYflySAiLwgBEggQ2gGIw98FIBohIDsamD3Lpmx3oq+BFt9vIDnt1Bcg05Sf+uxGVYtKLBvWIi0IARIpEpQDiMPfBSAZUDMl3fWVGqTezqlP5Lr6uBst/u54+SoyHvhNPzHR5yAiLQgBEikW8AiIw98FIPZJTLCCsasYB8vDD8rXw//7MNlwTdWata9S3BWxByAaICItCAESKRjAFIjD3wUgxNwUCnbU/eE32+rLWKNzlX/E7cUVRoPG5ZwsBybaOnUgIi0IARIpGsQuiMPfBSBwGtRH1qq46BwlB730ILSQznSPI+1QULj7baSTsIFpXiAiLQgBEikc9lmIw98FIDOzBeIRZD0ocOttUekUn1lZqO6eQn7A68yOnia/dRc5ICIwCAESCR78rwGAlP0FIBohIHCzxdFnxRJKnEJSqq/WYTx9eYyxnicsfOh1fWMTIS4QIi4IARIqILTBAoCU/QUgDUn8gD4vL/pSDdsWPY6OoIm3J974x5a6TvS7zV5wcF8gIi4IARIqIviLBMiZ/QUguuzhS+i2AlQNzB63FzFX2AiplTASFAwTE9oaEVaYxDEgIi4IARIqJKr2BciZ/QUgGowiOzih90Ccv8pByzwI30hR9bsSjpcYugnG1xrwOk8gIi4IARIqJsLADM6Z/QUgTdCIyxxcM2IGhzd8mp15gJ60UcXWJZGFWTirLvFOYW0gIi4IARIqKoDVJO6Z/QUg3HlPWIXUn3ZmEKOWkF8DIVrYUwIn7XOrsGN74R7XARggGqQHCjdyZWNlaXB0cy9wb3J0cy90cmFuc2Zlci9jaGFubmVscy9jaGFubmVsLTQ4L3NlcXVlbmNlcy84EgEBGg4IARgBIAEqBgACqP6uAyIuCAESBwIEyKGYBSAaISC1E8GUqxWmC4V03fic1pXfM67XWqCJKpwePLpmsWhS1SIuCAESBwYKiMPfBSAaISCoHpaUcIJJ5Y+Dir4rs0v92ln95UGyawStiRtpaP7aaiIuCAESBwgYiMPfBSAaISDC93Qf3Dmgi3XzR1KBcKryu7m5MHA7pCqPWlWp8AKrLiIsCAESKAoiiMPfBSD3LEp2vtbcelb8GZ8MMbNWtWM2BqpnOAxr3LhO4oMXpyAiLAgBEigMOojD3wUg2S76AomOxFrlN9rxjZHiVHWRj4hjEJYAuaEVNJN+5n8gIiwIARIoDmSIw98FIIEjlYapeZdAFxFpVcHZgR4iSCmHWvfHIwZefCbBh+XJICIvCAESCBDaAYjD3wUgGiEgOxqYPcumbHeir4EW328gOe3UFyDTlJ/67EZVi0osG9YiLQgBEikSlAOIw98FIBlQMyXd9ZUapN7OqU/kuvq4Gy3+7nj5KjIe+E0/MdHnICItCAESKRbwCIjD3wUg9klMsIKxqxgHy8MPytfD//sw2XBN1Zq1r1LcFbEHIBogIi0IARIpGMAUiMPfBSDE3BQKdtT94Tfb6stYo3OVf8TtxRVGg8blnCwHJto6dSAiLQgBEikaxC6Iw98FIHAa1EfWqrjoHCUHvfQgtJDOdI8j7VBQuPttpJOwgWleICItCAESKRz2WYjD3wUgM7MF4hFkPShw621R6RSfWVmo7p5CfsDrzI6eJr91FzkgIjAIARIJHvyvAYCU/QUgGiEgcLPF0WfFEkqcQlKqr9ZhPH15jLGeJyx86HV9YxMhLhAiLggBEiogtMECgJT9BSANSfyAPi8v+lIN2xY9jo6gibcn3vjHlrpO9LvNXnBwXyAiLggBEioi+IsEyJn9BSC67OFL6LYCVA3MHrcXMVfYCKmVMBIUDBMT2hoRVpjEMSAiLggBEiokqvYFyJn9BSAajCI7OKH3QJy/ykHLPAjfSFH1uxKOlxi6CcbXGvA6TyAiLggBEiomwsAMzpn9BSBN0IjLHFwzYgaHN3yanXmAnrRRxdYlkYVZOKsu8U5hbSAiLggBEioqgNUk7pn9BSDceU9YhdSfdmYQo5aQXwMhWthTAiftc6uwY3vhHtcBGCAK/gEK+wEKA2liYxIgXNTiISTgnF7vIC4drLL5j19W79dvWWv4j6s35pfRAFAaCQgBGAEgASoBACIlCAESIQFmzX0n9gBWiQS0wk2PDkiUg/OVlZC+Yx5kx+ZxMn5c4CInCAESAQEaIAKyhN/eeksFqt2XbKyKfbvvJ/kLFo0F9fHbnPiocTHyIicIARIBARog1CdJ3IWWPMBZMOQbHImE2JZNOfV9VeJtolbOdwBoIC0iJQgBEiEBG1sS5rbCFpKt+VeumIkXhBwDI5WnBrX3wtzlsmFbMMMiJwgBEgEBGiBT7wAf0cZ/wVLGRzF/wbvikxZ8IHEPnBxaLMXcVhukAg==', + next_sequence_recv: '76', + }; + const attributes = [ + { + key: 'packet_timeout_height', + value: '3-6254585', + event_id: 1, + }, + { + key: 'packet_timeout_timestamp', + value: '1676722677558827165', + event_id: 1, + }, + { + key: 'packet_sequence', + value: '76', + event_id: 1, + }, + { + key: 'packet_src_port', + value: 'transfer', + event_id: 1, + }, + { + key: 'packet_src_channel', + value: 'channel-5', + event_id: 1, + }, + { + key: 'packet_dst_port', + value: 'transfer', + event_id: 1, + }, + { + key: 'packet_dst_channel', + value: 'channel-48', + event_id: 1, + }, + { + key: 'packet_channel_ordering', + value: 'ORDER_UNORDERED', + event_id: 1, + }, + ]; + const event = Event.fromJson({ + type: IbcMessage.EVENT_TYPE.TIMEOUT_PACKET, + attributes, + content, + message_id: 1, + }); + await this.crawlIbcAppSerivce.handleIbcMessage([event], trx); + const newPacket = await IbcMessage.query() + .transacting(trx) + .first() + .throwIfNotFound(); + expect(newPacket.src_channel_id).toEqual(attributes[4].value); + expect(newPacket.src_port_id).toEqual(attributes[3].value); + expect(newPacket.dst_port_id).toEqual(attributes[5].value); + expect(newPacket.dst_channel_id).toEqual(attributes[6].value); + expect(newPacket.type).toEqual(event.type); + expect(newPacket.sequence).toEqual(parseInt(attributes[2].value, 10)); + expect(newPacket.data).toEqual(null); + await trx.rollback(); + }); + } +} From 7ad5fe9c7545168700b7abe0f8d3cfb11f1c01e9 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 15 Sep 2023 15:43:20 +0700 Subject: [PATCH 53/54] refactor: update status --- migrations/20230823070516_ics20_model.ts | 4 +- src/models/ibc_ics20.ts | 27 +++++++- src/services/ibc/crawl_ibc_ics20.service.ts | 62 +++++++++++++++++-- .../unit/services/ibc/crawl_ibc_ics20.spec.ts | 51 ++++++++++++--- 4 files changed, 126 insertions(+), 18 deletions(-) diff --git a/migrations/20230823070516_ics20_model.ts b/migrations/20230823070516_ics20_model.ts index 6f2a523ce..4ad2657f6 100644 --- a/migrations/20230823070516_ics20_model.ts +++ b/migrations/20230823070516_ics20_model.ts @@ -8,9 +8,11 @@ export async function up(knex: Knex): Promise { table.string('receiver').index().notNullable(); table.decimal('amount', 80, 0).notNullable(); table.string('denom').notNullable().index(); - table.boolean('status').defaultTo(true); + table.string('status').notNullable(); table.string('channel_id').notNullable().index(); table.foreign('ibc_message_id').references('ibc_message.id'); + table.string('sequence_key').notNullable().index(); + table.string('type').notNullable().index(); }); } diff --git a/src/models/ibc_ics20.ts b/src/models/ibc_ics20.ts index b2a1e97f8..5fd618cee 100644 --- a/src/models/ibc_ics20.ts +++ b/src/models/ibc_ics20.ts @@ -17,12 +17,16 @@ export class IbcIcs20 extends BaseModel { denom!: string; - status!: boolean; + status!: string; channel_id!: string; ibc_message!: IbcMessage; + sequence_key!: string; + + type!: string; + static get tableName() { return 'ibc_ics20'; } @@ -30,13 +34,25 @@ export class IbcIcs20 extends BaseModel { static get jsonSchema() { return { type: 'object', - required: ['receiver', 'amount', 'denom', 'ibc_message_id', 'channel_id'], + required: [ + 'receiver', + 'amount', + 'denom', + 'ibc_message_id', + 'channel_id', + 'sequence_key', + 'status', + 'type', + ], properties: { receiver: { type: 'string' }, denom: { type: 'string' }, ibc_message_id: { type: 'number' }, amount: { type: 'string' }, channel_id: { type: 'string' }, + sequence_key: { type: 'string' }, + status: { type: 'string' }, + type: { type: 'string' }, }, }; } @@ -67,4 +83,11 @@ export class IbcIcs20 extends BaseModel { FUNGIBLE_TOKEN_PACKET: 'fungible_token_packet', DENOM_TRACE: 'denomination_trace', }; + + static STATUS_TYPE = { + TIMEOUT: 'timeout', + ACK_ERROR: 'ack_error', + ACK_SUCCESS: 'ack_success', + ONGOING: 'ongoing', + }; } diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index 082be84ae..dad02f0ed 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -69,6 +69,9 @@ export default class CrawlIBCIcs20Service extends BullableService { ibc_message_id: msg.id, ...msg.data, channel_id: msg.src_channel_id, + status: IbcIcs20.STATUS_TYPE.ONGOING, + sequence_key: msg.sequence_key, + type: msg.type, }) ); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -131,8 +134,13 @@ export default class CrawlIBCIcs20Service extends BullableService { receiver, amount, denom, - status: ackStatus === 'true', + status: + ackStatus === 'true' + ? IbcIcs20.STATUS_TYPE.ACK_SUCCESS + : IbcIcs20.STATUS_TYPE.ACK_ERROR, channel_id: msg.dst_channel_id, + sequence_key: msg.sequence_key, + type: msg.type, }); }); await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); @@ -182,11 +190,33 @@ export default class CrawlIBCIcs20Service extends BullableService { receiver, amount, denom, - status: success !== undefined, + status: + success !== undefined + ? IbcIcs20.STATUS_TYPE.ACK_SUCCESS + : IbcIcs20.STATUS_TYPE.ACK_ERROR, channel_id: msg.src_channel_id, + sequence_key: msg.sequence_key, + type: msg.type, }); }); - await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + const acks = await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + // update ack status for origin send ics20 + const acksSuccess = acks.filter( + (ack) => ack.status === IbcIcs20.STATUS_TYPE.ACK_SUCCESS + ); + const acksError = acks.filter( + (ack) => ack.status === IbcIcs20.STATUS_TYPE.ACK_ERROR + ); + await this.updateSendStatus( + acksSuccess, + IbcIcs20.STATUS_TYPE.ACK_SUCCESS, + trx + ); + await this.updateSendStatus( + acksError, + IbcIcs20.STATUS_TYPE.ACK_ERROR, + trx + ); } } @@ -223,10 +253,15 @@ export default class CrawlIBCIcs20Service extends BullableService { amount, denom, channel_id: msg.src_channel_id, - status: false, + status: IbcIcs20.STATUS_TYPE.TIMEOUT, + sequence_key: msg.sequence_key, + type: msg.type, }); }); - await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); + const timeouts = await IbcIcs20.query() + .insert(ibcIcs20s) + .transacting(trx); + await this.updateSendStatus(timeouts, IbcIcs20.STATUS_TYPE.TIMEOUT, trx); } } @@ -243,6 +278,23 @@ export default class CrawlIBCIcs20Service extends BullableService { return `${dstPort}/${dstChannel}/${denom}`; } + async updateSendStatus( + msgs: IbcIcs20[], + type: string, + trx: Knex.Transaction + ) { + await IbcIcs20.query() + .transacting(trx) + .patch({ + status: type, + }) + .whereIn( + 'sequence_key', + msgs.map((msg) => msg.sequence_key) + ) + .andWhere('type', IbcMessage.EVENT_TYPE.SEND_PACKET); + } + async _start(): Promise { await this.createJob( BULL_JOB_NAME.CRAWL_IBC_ICS20, diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index fbc76b340..9d45cecb1 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -112,9 +112,10 @@ export default class CrawlIbcIcs20Test { expect(result?.receiver).toEqual(ibcMessage.data.receiver); expect(result?.amount).toEqual(ibcMessage.data.amount); expect(result?.denom).toEqual(ibcMessage.data.denom); - expect(result?.status).toEqual(true); + expect(result?.status).toEqual(IbcIcs20.STATUS_TYPE.ONGOING); + expect(result?.sequence_key).toEqual(ibcMessage.sequence_key); + expect(result?.type).toEqual(ibcMessage.type); expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); - await trx.rollback(); }); } @@ -221,7 +222,10 @@ export default class CrawlIbcIcs20Test { this.block.height, trx ); - const result = await IbcIcs20.query().first().transacting(trx); + const result = await IbcIcs20.query() + .where('type', IbcMessage.EVENT_TYPE.RECV_PACKET) + .first() + .transacting(trx); expect(result?.ibc_message_id).toEqual(ibcMsg.id); expect(result?.receiver).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) @@ -237,7 +241,9 @@ export default class CrawlIbcIcs20Test { ibcMessage.dst_channel_id }/${getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM)}` ); - expect(result?.status).toEqual(true); + expect(result?.status).toEqual(IbcIcs20.STATUS_TYPE.ACK_SUCCESS); + expect(result?.sequence_key).toEqual(ibcMessage.sequence_key); + expect(result?.type).toEqual(ibcMessage.type); expect(result?.channel_id).toEqual(ibcMessage.dst_channel_id); await trx.rollback(); }); @@ -319,7 +325,10 @@ export default class CrawlIbcIcs20Test { this.block.height, trx ); - const result = await IbcIcs20.query().first().transacting(trx); + const result = await IbcIcs20.query() + .where('type', IbcMessage.EVENT_TYPE.RECV_PACKET) + .first() + .transacting(trx); expect(result?.ibc_message_id).toEqual(ibcMsg.id); expect(result?.receiver).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) @@ -331,7 +340,9 @@ export default class CrawlIbcIcs20Test { getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) ); expect(result?.denom).toEqual('uatom'); - expect(result?.status).toEqual(true); + expect(result?.status).toEqual(IbcIcs20.STATUS_TYPE.ACK_SUCCESS); + expect(result?.sequence_key).toEqual(ibcMessage.sequence_key); + expect(result?.type).toEqual(ibcMessage.type); expect(result?.channel_id).toEqual(ibcMessage.dst_channel_id); await trx.rollback(); }); @@ -433,7 +444,10 @@ export default class CrawlIbcIcs20Test { this.block.height, trx ); - const result = await IbcIcs20.query().first().transacting(trx); + const result = await IbcIcs20.query() + .where('type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) + .first() + .transacting(trx); expect(result?.receiver).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) ); @@ -446,8 +460,15 @@ export default class CrawlIbcIcs20Test { expect(result?.denom).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM) ); - expect(result?.status).toEqual(false); + expect(result?.status).toEqual(IbcIcs20.STATUS_TYPE.ACK_ERROR); + expect(result?.sequence_key).toEqual(ibcMessage.sequence_key); + expect(result?.type).toEqual(ibcMessage.type); expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); + const originSend = await IbcIcs20.query() + .where('type', IbcMessage.EVENT_TYPE.SEND_PACKET) + .first() + .transacting(trx); + expect(originSend?.status).toEqual(IbcIcs20.STATUS_TYPE.ACK_ERROR); await trx.rollback(); }); } @@ -514,7 +535,10 @@ export default class CrawlIbcIcs20Test { }), }), ]; - await Event.query().insertGraph(events).transacting(trx); + await Event.query() + .insertGraph(events) + .where('type', IbcMessage.EVENT_TYPE.TIMEOUT_PACKET) + .transacting(trx); await this.crawlIbcIcs20Serivce.handleIcs20Timeout( this.block.height - 1, this.block.height, @@ -538,8 +562,15 @@ export default class CrawlIbcIcs20Test { expect(result?.denom).toEqual( getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM) ); - expect(result?.status).toBe(false); + expect(result?.status).toEqual(IbcIcs20.STATUS_TYPE.TIMEOUT); + expect(result?.sequence_key).toEqual(ibcMessage.sequence_key); + expect(result?.type).toEqual(ibcMessage.type); expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); + const originSend = await IbcIcs20.query() + .where('type', IbcMessage.EVENT_TYPE.SEND_PACKET) + .first() + .transacting(trx); + expect(originSend?.status).toEqual(IbcIcs20.STATUS_TYPE.TIMEOUT); await trx.rollback(); }); } From 0fe2af1ad648f224f7a90c9ddd9dbab9902bca59 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Tue, 19 Sep 2023 10:21:58 +0700 Subject: [PATCH 54/54] refactor: code --- src/services/ibc/crawl_ibc_ics20.service.ts | 91 ++++------- .../unit/services/ibc/crawl_ibc_ics20.spec.ts | 151 ++++++++++++------ 2 files changed, 134 insertions(+), 108 deletions(-) diff --git a/src/services/ibc/crawl_ibc_ics20.service.ts b/src/services/ibc/crawl_ibc_ics20.service.ts index dad02f0ed..588096622 100644 --- a/src/services/ibc/crawl_ibc_ics20.service.ts +++ b/src/services/ibc/crawl_ibc_ics20.service.ts @@ -167,52 +167,35 @@ export default class CrawlIBCIcs20Service extends BullableService { .orderBy('message.id') .transacting(trx); if (ics20Acks.length > 0) { - const ibcIcs20s = ics20Acks.map((msg) => { - const ackEvents = msg.message.events; + // update success ack status for origin send ics20 + const acksSuccess = ics20Acks.filter((ack) => { + const ackEvents = ack.message.events; if (ackEvents.length !== 2) { - throw Error(`Ack ibc hasn't emmitted enough events: ${msg.id}`); + throw Error(`Ack ibc hasn't emmitted enough events: ${ack.id}`); } - const [sender, receiver, amount, denom, success] = [ - ...ackEvents[0].getAttributesFrom([ - EventAttribute.ATTRIBUTE_KEY.SENDER, - EventAttribute.ATTRIBUTE_KEY.RECEIVER, - EventAttribute.ATTRIBUTE_KEY.AMOUNT, - EventAttribute.ATTRIBUTE_KEY.DENOM, - ]), - ...ackEvents[1].getAttributesFrom([ - EventAttribute.ATTRIBUTE_KEY.SUCCESS, - ]), - ]; + const [success] = ackEvents[1].getAttributesFrom([ + EventAttribute.ATTRIBUTE_KEY.SUCCESS, + ]); - return IbcIcs20.fromJson({ - ibc_message_id: msg.id, - sender, - receiver, - amount, - denom, - status: - success !== undefined - ? IbcIcs20.STATUS_TYPE.ACK_SUCCESS - : IbcIcs20.STATUS_TYPE.ACK_ERROR, - channel_id: msg.src_channel_id, - sequence_key: msg.sequence_key, - type: msg.type, - }); + return success !== undefined; }); - const acks = await IbcIcs20.query().insert(ibcIcs20s).transacting(trx); - // update ack status for origin send ics20 - const acksSuccess = acks.filter( - (ack) => ack.status === IbcIcs20.STATUS_TYPE.ACK_SUCCESS - ); - const acksError = acks.filter( - (ack) => ack.status === IbcIcs20.STATUS_TYPE.ACK_ERROR - ); - await this.updateSendStatus( + await this.updateOriginSendStatus( acksSuccess, IbcIcs20.STATUS_TYPE.ACK_SUCCESS, trx ); - await this.updateSendStatus( + // update error ack status for origin send ics20 + const acksError = ics20Acks.filter((ack) => { + const ackEvents = ack.message.events; + if (ackEvents.length !== 2) { + throw Error(`Ack ibc hasn't emmitted enough events: ${ack.id}`); + } + const [success] = ackEvents[1].getAttributesFrom([ + EventAttribute.ATTRIBUTE_KEY.SUCCESS, + ]); + return success === undefined; + }); + await this.updateOriginSendStatus( acksError, IbcIcs20.STATUS_TYPE.ACK_ERROR, trx @@ -226,7 +209,6 @@ export default class CrawlIBCIcs20Service extends BullableService { trx: Knex.Transaction ) { const ics20Timeouts = await IbcMessage.query() - .withGraphFetched('message.events(selectIcs20Event).attributes') .joinRelated('message.transaction') .modifiers({ selectIcs20Event(builder) { @@ -240,28 +222,11 @@ export default class CrawlIBCIcs20Service extends BullableService { .orderBy('message.id') .transacting(trx); if (ics20Timeouts.length > 0) { - const ibcIcs20s = ics20Timeouts.map((msg) => { - const timeoutEvent = msg.message.events[0]; - const [receiver, amount, denom] = timeoutEvent.getAttributesFrom([ - EventAttribute.ATTRIBUTE_KEY.REFUND_RECEIVER, - EventAttribute.ATTRIBUTE_KEY.REFUND_AMOUNT, - EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM, - ]); - return IbcIcs20.fromJson({ - ibc_message_id: msg.id, - receiver, - amount, - denom, - channel_id: msg.src_channel_id, - status: IbcIcs20.STATUS_TYPE.TIMEOUT, - sequence_key: msg.sequence_key, - type: msg.type, - }); - }); - const timeouts = await IbcIcs20.query() - .insert(ibcIcs20s) - .transacting(trx); - await this.updateSendStatus(timeouts, IbcIcs20.STATUS_TYPE.TIMEOUT, trx); + await this.updateOriginSendStatus( + ics20Timeouts, + IbcIcs20.STATUS_TYPE.TIMEOUT, + trx + ); } } @@ -278,8 +243,8 @@ export default class CrawlIBCIcs20Service extends BullableService { return `${dstPort}/${dstChannel}/${denom}`; } - async updateSendStatus( - msgs: IbcIcs20[], + async updateOriginSendStatus( + msgs: IbcMessage[], type: string, trx: Knex.Transaction ) { diff --git a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts index 9d45cecb1..574891955 100644 --- a/test/unit/services/ibc/crawl_ibc_ics20.spec.ts +++ b/test/unit/services/ibc/crawl_ibc_ics20.spec.ts @@ -348,8 +348,8 @@ export default class CrawlIbcIcs20Test { }); } - @Test('Test handleIcs20Ack') - async testHandleIcs20Ack() { + @Test('Test handleIcs20AckError') + async testHandleIcs20AckError() { await knex.transaction(async (trx) => { const ibcMessage = IbcMessage.fromJson({ transaction_message_id: 1, @@ -444,31 +444,116 @@ export default class CrawlIbcIcs20Test { this.block.height, trx ); - const result = await IbcIcs20.query() - .where('type', IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET) + const originSend = await IbcIcs20.query() + .where('type', IbcMessage.EVENT_TYPE.SEND_PACKET) .first() .transacting(trx); - expect(result?.receiver).toEqual( - getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.RECEIVER) - ); - expect(result?.sender).toEqual( - getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.SENDER) - ); - expect(result?.amount).toEqual( - getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.AMOUNT) - ); - expect(result?.denom).toEqual( - getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.DENOM) + expect(originSend?.status).toEqual(IbcIcs20.STATUS_TYPE.ACK_ERROR); + await trx.rollback(); + }); + } + + @Test('Test handleIcs20AckSuccess') + async testHandleIcs20AckSuccess() { + await knex.transaction(async (trx) => { + const ibcMessage = IbcMessage.fromJson({ + transaction_message_id: 1, + src_channel_id: 'aaa', + src_port_id: PORT, + dst_channel_id: 'cccc', + dst_port_id: 'dddd', + type: IbcMessage.EVENT_TYPE.ACKNOWLEDGE_PACKET, + sequence: 256, + sequence_key: 'hcc', + data: { + amount: '10000', + denom: 'uatom', + receiver: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + }); + await IbcMessage.query().insert(ibcMessage).transacting(trx); + const event1Attrs = [ + { + key: 'module', + value: 'transfer', + }, + { + key: 'sender', + value: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', + }, + { + key: 'receiver', + value: + '{"autopilot":{"stakeibc":{"stride_address":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl","action":"LiquidStake"},"receiver":"stride1e8288j8swfy7rwkyx0h3lz82fe58vz2medxndl"}}', + }, + { + key: 'denom', + value: 'uatom', + }, + { + key: 'amount', + value: '10000', + }, + { + key: 'memo', + value: '', + }, + { + key: 'acknowledgement', + value: 'result:"\\001" ', + }, + ]; + const event2Attrs = [ + { + key: 'success', + value: '\u0001', + }, + ]; + const events = [ + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event1Attrs.map((e, index) => { + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); + return e; + }), + }), + Event.fromJson({ + tx_id: 1, + tx_msg_index: 1, + type: IbcIcs20.EVENT_TYPE.FUNGIBLE_TOKEN_PACKET, + block_height: this.block.height, + source: 'TX_EVENT', + attributes: event2Attrs.map((e, index) => { + Object.assign(e, { + block_height: this.block.height, + event_id: 1, + index, + }); + return e; + }), + }), + ]; + await Event.query().insertGraph(events).transacting(trx); + await this.crawlIbcIcs20Serivce.handleIcs20Ack( + this.block.height - 1, + this.block.height, + trx ); - expect(result?.status).toEqual(IbcIcs20.STATUS_TYPE.ACK_ERROR); - expect(result?.sequence_key).toEqual(ibcMessage.sequence_key); - expect(result?.type).toEqual(ibcMessage.type); - expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); const originSend = await IbcIcs20.query() .where('type', IbcMessage.EVENT_TYPE.SEND_PACKET) .first() .transacting(trx); - expect(originSend?.status).toEqual(IbcIcs20.STATUS_TYPE.ACK_ERROR); + expect(originSend?.status).toEqual(IbcIcs20.STATUS_TYPE.ACK_SUCCESS); await trx.rollback(); }); } @@ -493,9 +578,7 @@ export default class CrawlIbcIcs20Test { sender: 'cosmos1e8288j8swfy7rwkyx0h3lz82fe58vz2m6xx0en', }, }); - const ibcMsg = await IbcMessage.query() - .insert(ibcMessage) - .transacting(trx); + await IbcMessage.query().insert(ibcMessage).transacting(trx); const event1Attrs = [ { key: 'module', @@ -544,28 +627,6 @@ export default class CrawlIbcIcs20Test { this.block.height, trx ); - const result = await IbcIcs20.query().first().transacting(trx); - expect(result?.ibc_message_id).toEqual(ibcMsg.id); - expect(result?.receiver).toEqual( - getAttributeFrom( - event1Attrs, - EventAttribute.ATTRIBUTE_KEY.REFUND_RECEIVER - ) - ); - expect(result?.sender).toBeNull(); - expect(result?.amount).toEqual( - getAttributeFrom( - event1Attrs, - EventAttribute.ATTRIBUTE_KEY.REFUND_AMOUNT - ) - ); - expect(result?.denom).toEqual( - getAttributeFrom(event1Attrs, EventAttribute.ATTRIBUTE_KEY.REFUND_DENOM) - ); - expect(result?.status).toEqual(IbcIcs20.STATUS_TYPE.TIMEOUT); - expect(result?.sequence_key).toEqual(ibcMessage.sequence_key); - expect(result?.type).toEqual(ibcMessage.type); - expect(result?.channel_id).toEqual(ibcMessage.src_channel_id); const originSend = await IbcIcs20.query() .where('type', IbcMessage.EVENT_TYPE.SEND_PACKET) .first()