From 5980118a93390fd5176ff10a3f2410706598d1f7 Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Mon, 30 Jun 2025 14:46:10 +0700 Subject: [PATCH 01/11] feat: remove fs --- src/api.ts | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/api.ts b/src/api.ts index 4c81249..5af458f 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,6 +1,4 @@ -import axios, { AxiosResponse } from "axios"; -import { promises as fs } from "fs"; -import path from "path"; +import axios from "axios"; import { DecoderConfig } from "./interfaces"; import { @@ -86,28 +84,28 @@ export class ApiClient { } } - private async _debugApiCall(address: string, response: AxiosResponse) { - const key = `/initia/move/v1/accounts/${address}/resources`; - const filePath = path.join(__dirname, "tests/_output/resources.json"); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let existingData: any = {}; - try { - const fileContent = await fs.readFile(filePath, "utf-8"); - existingData = JSON.parse(fileContent); - } catch { - // File does not exist or is invalid — start fresh - existingData = {}; - } - - existingData[key] = response.data; - - await fs.writeFile( - filePath, - JSON.stringify(existingData, null, 2), - "utf-8" - ); - } + // private async _debugApiCall(address: string, response: AxiosResponse) { + // const key = `/initia/move/v1/accounts/${address}/resources`; + // const filePath = path.join(__dirname, "tests/_output/resources.json"); + + // // eslint-disable-next-line @typescript-eslint/no-explicit-any + // let existingData: any = {}; + // try { + // const fileContent = await fs.readFile(filePath, "utf-8"); + // existingData = JSON.parse(fileContent); + // } catch { + // // File does not exist or is invalid — start fresh + // existingData = {}; + // } + + // existingData[key] = response.data; + + // await fs.writeFile( + // filePath, + // JSON.stringify(existingData, null, 2), + // "utf-8" + // ); + // } private async _getAccountResources(address: string) { try { From 77aa2335dde8b46e699fd429bbdb30e01126b328 Mon Sep 17 00:00:00 2001 From: tansawit Date: Mon, 3 Nov 2025 17:07:31 +0700 Subject: [PATCH 02/11] feat: add authz and feegrant support Add decoders for Cosmos authz and feegrant modules with comprehensive test coverage. - Implement authz decoder for grant, revoke, and exec operations - Implement feegrant decoder for allowance grant and revoke operations - Add schema definitions and message type constants - Add comprehensive test fixtures and test cases --- README.md | 6 + src/decoder-registry.ts | 15 + src/decoder.ts | 37 +- src/decoders/cosmos/authz.ts | 144 ++ src/decoders/cosmos/feegrant.ts | 72 + src/decoders/cosmos/index.ts | 2 + src/interfaces/cosmos.ts | 55 + src/interfaces/decoder.ts | 3 +- src/message-types.ts | 11 +- src/schema/cosmos/authz.ts | 35 + src/schema/cosmos/feegrant.ts | 25 + src/schema/cosmos/index.ts | 2 + .../move/authz-message-types.fixture.ts | 527 ++++++ src/tests/cosmos/move/authz.fixture.ts | 1474 +++++++++++++++++ src/tests/cosmos/move/authz.test.ts | 820 +++++++++ src/tests/cosmos/move/feegrant.test.ts | 454 +++++ 16 files changed, 3665 insertions(+), 17 deletions(-) create mode 100644 src/decoders/cosmos/authz.ts create mode 100644 src/decoders/cosmos/feegrant.ts create mode 100644 src/schema/cosmos/authz.ts create mode 100644 src/schema/cosmos/feegrant.ts create mode 100644 src/tests/cosmos/move/authz-message-types.fixture.ts create mode 100644 src/tests/cosmos/move/authz.fixture.ts create mode 100644 src/tests/cosmos/move/authz.test.ts create mode 100644 src/tests/cosmos/move/feegrant.test.ts diff --git a/README.md b/README.md index 845b8ba..a633d80 100644 --- a/README.md +++ b/README.md @@ -352,6 +352,12 @@ The decoder returns a structured object with the following format: - `/initia.mstaking.v1.MsgUndelegate` - `/initia.mstaking.v1.MsgBeginRedelegate` +#### Authz Messages + +- `/cosmos.authz.v1beta1.MsgExec` - Execute authorized messages on behalf of another account + +**Note:** MsgExec recursively decodes the inner messages it contains. All currently supported message types can be wrapped within MsgExec and will be properly decoded. + ### Move VM Messages - `/initia.move.v1.MsgExecute` diff --git a/src/decoder-registry.ts b/src/decoder-registry.ts index 8ece508..534d347 100644 --- a/src/decoder-registry.ts +++ b/src/decoder-registry.ts @@ -2,6 +2,11 @@ import * as Decoders from "./decoders"; import { MessageDecoder } from "./interfaces"; export const cosmosEvmMessageDecoders: MessageDecoder[] = [ + Decoders.authzExecDecoder, + Decoders.authzGrantDecoder, + Decoders.authzRevokeDecoder, + Decoders.feegrantGrantAllowanceDecoder, + Decoders.feegrantRevokeAllowanceDecoder, Decoders.sendDecoder, Decoders.finalizeTokenDepositDecoder, Decoders.initiateTokenWithdrawalDecoder, @@ -12,10 +17,20 @@ export const cosmosEvmMessageDecoders: MessageDecoder[] = [ ]; export const cosmosWasmMessageDecoders: MessageDecoder[] = [ + Decoders.authzExecDecoder, + Decoders.authzGrantDecoder, + Decoders.authzRevokeDecoder, + Decoders.feegrantGrantAllowanceDecoder, + Decoders.feegrantRevokeAllowanceDecoder, Decoders.sendDecoder ]; export const cosmosMoveMessageDecoders: MessageDecoder[] = [ + Decoders.authzExecDecoder, + Decoders.authzGrantDecoder, + Decoders.authzRevokeDecoder, + Decoders.feegrantGrantAllowanceDecoder, + Decoders.feegrantRevokeAllowanceDecoder, Decoders.claimMinitswapDecoder, Decoders.delegateDecoder, Decoders.delegateLockedDecoder, diff --git a/src/decoder.ts b/src/decoder.ts index c37bb66..ddbd35b 100644 --- a/src/decoder.ts +++ b/src/decoder.ts @@ -224,26 +224,20 @@ export class TxDecoder { return notSupportedMessage; } - let decoders; - switch (vm) { - case "evm": - decoders = cosmosEvmMessageDecoders; - break; - case "move": - decoders = cosmosMoveMessageDecoders; - break; - case "wasm": - decoders = cosmosWasmMessageDecoders; - break; - default: - throw new Error(`Unknown VM type: ${vm}`); - } + const decoders = this._getDecodersForVm(vm); try { const decoder = this._findDecoderForMessage(message, log, vm, decoders); if (!decoder) return notSupportedMessage; - return await decoder.decode(message, log, this.apiClient, txResponse, vm); + return await decoder.decode( + message, + log, + this.apiClient, + txResponse, + vm, + this._getDecodersForVm.bind(this) + ); } catch (e) { console.error(e); return notSupportedMessage; @@ -296,6 +290,19 @@ export class TxDecoder { return ethereumDecoders.find((decoder) => decoder.check(payload)); } + private _getDecodersForVm(vm: VmType): MessageDecoder[] { + switch (vm) { + case "evm": + return cosmosEvmMessageDecoders; + case "move": + return cosmosMoveMessageDecoders; + case "wasm": + return cosmosWasmMessageDecoders; + default: + throw new Error(`Unknown VM type: ${vm}`); + } + } + private async _processMessages( txResponse: TxResponse, vm: VmType diff --git a/src/decoders/cosmos/authz.ts b/src/decoders/cosmos/authz.ts new file mode 100644 index 0000000..aaf8ace --- /dev/null +++ b/src/decoders/cosmos/authz.ts @@ -0,0 +1,144 @@ +import type { ApiClient } from "@/api"; +import type { DecodedMessage, MessageDecoder, VmType } from "@/interfaces"; + +import { SUPPORTED_MESSAGE_TYPES } from "@/message-types"; +import { + Log, + Message, + TxResponse, + zMsgExec, + zMsgGrant, + zMsgRevoke +} from "@/schema"; +import { createNotSupportedMessage } from "@/utils"; + +export const authzExecDecoder: MessageDecoder = { + check: (message, _log) => + message["@type"] === SUPPORTED_MESSAGE_TYPES.MsgExec, + decode: async ( + message: Message, + log: Log, + apiClient: ApiClient, + txResponse: TxResponse, + vm: VmType, + getDecodersForVm?: (vm: VmType) => MessageDecoder[] + ) => { + const parsed = zMsgExec.safeParse(message); + if (!parsed.success) { + throw new Error("Invalid authz exec message"); + } + + const { grantee, msgs } = parsed.data; + + if (!getDecodersForVm) { + const decodedMessage: DecodedMessage = { + action: "authz_exec", + data: { + grantee, + messages: msgs.map((msg) => createNotSupportedMessage(msg["@type"])) + }, + isIbc: false, + isOp: false + }; + return decodedMessage; + } + + const decoders = getDecodersForVm(vm); + + const decodedInnerMessages: DecodedMessage[] = []; + for (const innerMessage of msgs) { + const decoder = decoders.find((d) => d.check(innerMessage, log, vm)); + + if (decoder) { + const decoded = await decoder.decode( + innerMessage, + log, + apiClient, + txResponse, + vm, + getDecodersForVm + ); + decodedInnerMessages.push(decoded); + } else { + decodedInnerMessages.push( + createNotSupportedMessage(innerMessage["@type"]) + ); + } + } + + const decodedMessage: DecodedMessage = { + action: "authz_exec", + data: { + grantee, + messages: decodedInnerMessages + }, + isIbc: false, + isOp: false + }; + + return decodedMessage; + } +}; + +export const authzGrantDecoder: MessageDecoder = { + check: (message, _log) => + message["@type"] === SUPPORTED_MESSAGE_TYPES.MsgGrant, + decode: async ( + message: Message, + _log: Log, + _apiClient: ApiClient, + _txResponse: TxResponse + ) => { + const parsed = zMsgGrant.safeParse(message); + if (!parsed.success) { + throw new Error("Invalid authz grant message"); + } + + const { grant, grantee, granter } = parsed.data; + + const decodedMessage: DecodedMessage = { + action: "authz_grant", + data: { + authorization: grant.authorization, + expiration: grant.expiration, + grantee, + granter + }, + isIbc: false, + isOp: false + }; + + return decodedMessage; + } +}; + +export const authzRevokeDecoder: MessageDecoder = { + check: (message, _log) => + message["@type"] === SUPPORTED_MESSAGE_TYPES.MsgRevoke, + decode: async ( + message: Message, + _log: Log, + _apiClient: ApiClient, + _txResponse: TxResponse + ) => { + const parsed = zMsgRevoke.safeParse(message); + if (!parsed.success) { + throw new Error("Invalid authz revoke message"); + } + + const { grantee, granter, msg_type_url } = parsed.data; + + const decodedMessage: DecodedMessage = { + action: "authz_revoke", + data: { + grantee, + granter, + msg_type_url + }, + isIbc: false, + isOp: false + }; + + return decodedMessage; + } +}; diff --git a/src/decoders/cosmos/feegrant.ts b/src/decoders/cosmos/feegrant.ts new file mode 100644 index 0000000..529e605 --- /dev/null +++ b/src/decoders/cosmos/feegrant.ts @@ -0,0 +1,72 @@ +import type { ApiClient } from "@/api"; +import type { DecodedMessage, MessageDecoder } from "@/interfaces"; + +import { SUPPORTED_MESSAGE_TYPES } from "@/message-types"; +import { + Log, + Message, + TxResponse, + zMsgGrantAllowance, + zMsgRevokeAllowance +} from "@/schema"; + +export const feegrantGrantAllowanceDecoder: MessageDecoder = { + check: (message, _log) => + message["@type"] === SUPPORTED_MESSAGE_TYPES.MsgGrantAllowance, + decode: async ( + message: Message, + _log: Log, + _apiClient: ApiClient, + _txResponse: TxResponse + ) => { + const parsed = zMsgGrantAllowance.safeParse(message); + if (!parsed.success) { + throw new Error("Invalid feegrant grant allowance message"); + } + + const { allowance, grantee, granter } = parsed.data; + + const decodedMessage: DecodedMessage = { + action: "feegrant_grant_allowance", + data: { + allowance, + grantee, + granter + }, + isIbc: false, + isOp: false + }; + + return decodedMessage; + } +}; + +export const feegrantRevokeAllowanceDecoder: MessageDecoder = { + check: (message, _log) => + message["@type"] === SUPPORTED_MESSAGE_TYPES.MsgRevokeAllowance, + decode: async ( + message: Message, + _log: Log, + _apiClient: ApiClient, + _txResponse: TxResponse + ) => { + const parsed = zMsgRevokeAllowance.safeParse(message); + if (!parsed.success) { + throw new Error("Invalid feegrant revoke allowance message"); + } + + const { grantee, granter } = parsed.data; + + const decodedMessage: DecodedMessage = { + action: "feegrant_revoke_allowance", + data: { + grantee, + granter + }, + isIbc: false, + isOp: false + }; + + return decodedMessage; + } +}; diff --git a/src/decoders/cosmos/index.ts b/src/decoders/cosmos/index.ts index 1e0198d..c86ad55 100644 --- a/src/decoders/cosmos/index.ts +++ b/src/decoders/cosmos/index.ts @@ -1,4 +1,6 @@ +export * from "./authz"; export * from "./bank"; export * from "./distribution"; +export * from "./feegrant"; export * from "./move"; export * from "./mstaking"; diff --git a/src/interfaces/cosmos.ts b/src/interfaces/cosmos.ts index 3421748..8460777 100644 --- a/src/interfaces/cosmos.ts +++ b/src/interfaces/cosmos.ts @@ -6,6 +6,9 @@ interface DecodedMessageBase { } export type DecodedMessage = + | DecodedAuthzExecMessage + | DecodedAuthzGrantMessage + | DecodedAuthzRevokeMessage | DecodedClaimMinitswapMessage | DecodedDelegateMessage | DecodedDepositLiquidityMessage @@ -13,6 +16,8 @@ export type DecodedMessage = | DecodedDepositStakeLiquidityMessage | DecodedDepositStakeLockLiquidityMessage | DecodedExtendLiquidityMessage + | DecodedFeegrantGrantAllowanceMessage + | DecodedFeegrantRevokeAllowanceMessage | DecodedFinalizeTokenDepositMessage | DecodedFinalizeTokenWithdrawalMessage | DecodedIbcFtReceiveMessage @@ -40,6 +45,56 @@ export type DecodedMessage = | DecodedWithdrawMinitswapMessage | DecodedWithdrawStableswapMessage; +interface DecodedAuthzExecMessage extends DecodedMessageBase { + action: "authz_exec"; + data: { + grantee: string; + messages: DecodedMessage[]; + }; +} + +interface DecodedAuthzGrantMessage extends DecodedMessageBase { + action: "authz_grant"; + data: { + authorization: { + "@type": string; + [key: string]: unknown; + }; + expiration?: string; + grantee: string; + granter: string; + }; +} + +interface DecodedAuthzRevokeMessage extends DecodedMessageBase { + action: "authz_revoke"; + data: { + grantee: string; + granter: string; + msg_type_url: string; + }; +} + +interface DecodedFeegrantGrantAllowanceMessage extends DecodedMessageBase { + action: "feegrant_grant_allowance"; + data: { + allowance: { + "@type": string; + [key: string]: unknown; + }; + grantee: string; + granter: string; + }; +} + +interface DecodedFeegrantRevokeAllowanceMessage extends DecodedMessageBase { + action: "feegrant_revoke_allowance"; + data: { + grantee: string; + granter: string; + }; +} + interface DecodedSendMessage extends DecodedMessageBase { action: "send"; data: { diff --git a/src/interfaces/decoder.ts b/src/interfaces/decoder.ts index 0089aef..755f581 100644 --- a/src/interfaces/decoder.ts +++ b/src/interfaces/decoder.ts @@ -14,7 +14,8 @@ export type MessageDecoder = { log: L, apiClient: ApiClient, txResponse: TxResponse, - vm: VmType + vm: VmType, + getDecodersForVm?: (vm: VmType) => MessageDecoder[] ) => Promise; }; diff --git a/src/message-types.ts b/src/message-types.ts index fe440fb..729ddfe 100644 --- a/src/message-types.ts +++ b/src/message-types.ts @@ -23,5 +23,14 @@ export const SUPPORTED_MESSAGE_TYPES = { // ibc messages MsgIbcTransfer: "/ibc.applications.transfer.v1.MsgTransfer", MsgIbcNftTransfer: "/ibc.applications.nft_transfer.v1.MsgTransfer", - MsgIbcRecvPacket: "/ibc.core.channel.v1.MsgRecvPacket" + MsgIbcRecvPacket: "/ibc.core.channel.v1.MsgRecvPacket", + + // authz messages + MsgExec: "/cosmos.authz.v1beta1.MsgExec", + MsgGrant: "/cosmos.authz.v1beta1.MsgGrant", + MsgRevoke: "/cosmos.authz.v1beta1.MsgRevoke", + + // feegrant messages + MsgGrantAllowance: "/cosmos.feegrant.v1beta1.MsgGrantAllowance", + MsgRevokeAllowance: "/cosmos.feegrant.v1beta1.MsgRevokeAllowance" } as const; diff --git a/src/schema/cosmos/authz.ts b/src/schema/cosmos/authz.ts new file mode 100644 index 0000000..7c5f4af --- /dev/null +++ b/src/schema/cosmos/authz.ts @@ -0,0 +1,35 @@ +import { z } from "zod"; + +import { SUPPORTED_MESSAGE_TYPES } from "../../message-types"; +import { zMessage } from "./bank"; + +export const zMsgExec = z.object({ + "@type": z.literal(SUPPORTED_MESSAGE_TYPES.MsgExec), + grantee: z.string(), + msgs: z.array(zMessage) +}); + +const zAuthorization = z + .object({ + "@type": z.string() + }) + .passthrough(); + +const zGrant = z.object({ + authorization: zAuthorization, + expiration: z.string().optional() +}); + +export const zMsgGrant = z.object({ + "@type": z.literal(SUPPORTED_MESSAGE_TYPES.MsgGrant), + grant: zGrant, + grantee: z.string(), + granter: z.string() +}); + +export const zMsgRevoke = z.object({ + "@type": z.literal(SUPPORTED_MESSAGE_TYPES.MsgRevoke), + grantee: z.string(), + granter: z.string(), + msg_type_url: z.string() +}); diff --git a/src/schema/cosmos/feegrant.ts b/src/schema/cosmos/feegrant.ts new file mode 100644 index 0000000..c581eb8 --- /dev/null +++ b/src/schema/cosmos/feegrant.ts @@ -0,0 +1,25 @@ +import { z } from "zod"; + +import { SUPPORTED_MESSAGE_TYPES } from "../../message-types"; +import { zCoin } from "../common"; + +const zAllowance = z + .object({ + "@type": z.string(), + expiration: z.string().optional(), + spend_limit: z.array(zCoin).optional() + }) + .passthrough(); + +export const zMsgGrantAllowance = z.object({ + "@type": z.literal(SUPPORTED_MESSAGE_TYPES.MsgGrantAllowance), + allowance: zAllowance, + grantee: z.string(), + granter: z.string() +}); + +export const zMsgRevokeAllowance = z.object({ + "@type": z.literal(SUPPORTED_MESSAGE_TYPES.MsgRevokeAllowance), + grantee: z.string(), + granter: z.string() +}); diff --git a/src/schema/cosmos/index.ts b/src/schema/cosmos/index.ts index 8a20d2b..49c91e7 100644 --- a/src/schema/cosmos/index.ts +++ b/src/schema/cosmos/index.ts @@ -1,6 +1,8 @@ +export * from "./authz"; export * from "./bank"; export * from "./bridge"; export * from "./events"; +export * from "./feegrant"; export * from "./ibc"; export * from "./move"; export * from "./staking"; diff --git a/src/tests/cosmos/move/authz-message-types.fixture.ts b/src/tests/cosmos/move/authz-message-types.fixture.ts new file mode 100644 index 0000000..57cdcf2 --- /dev/null +++ b/src/tests/cosmos/move/authz-message-types.fixture.ts @@ -0,0 +1,527 @@ +// Fixtures for testing MsgExec with various message types + +export const mockMsgExecWithDelegate = { + code: 0, + codespace: "", + data: "12290A272F696E697469612E6D7374616B696E672E76312E4D736744656C6567617465526573706F6E7365", + events: [], + gas_used: "240849", + gas_wanted: "344162", + height: "4141866", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { + index: true, + key: "validator", + value: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + }, + { + index: true, + key: "delegator", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { index: true, key: "amount", value: "100000uinit" }, + { + index: true, + key: "new_shares", + value: "100000.000000000000000000uinit" + }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "delegate" + }, + { + attributes: [ + { + index: true, + key: "spender", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { index: true, key: "amount", value: "100000uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + index: true, + key: "receiver", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { index: true, key: "amount", value: "100000uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { + index: true, + key: "recipient", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { + index: true, + key: "sender", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { index: true, key: "amount", value: "100000uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { index: true, key: "sender", value: "init1testgrantee" }, + { index: true, key: "module", value: "authz" }, + { index: true, key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2023-10-20T10:00:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "5000", denom: "uinit" }], + gas_limit: "344162", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "1" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1testgrantee", + msgs: [ + { + "@type": "/initia.mstaking.v1.MsgDelegate", + amount: [{ amount: "100000", denom: "uinit" }], + delegator_address: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", + validator_address: + "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TESTDELEGATE123" +}; + +export const mockApiResponsesForDelegate = { + GET: { + "/initia/move/v1/accounts/0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x4fea76427b8345861e80a3540a8a9d936fd39391","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAAT+p2QnuDRYYegKNUCoqdk2/Tk5EAAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"109114080715522","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdkCYwUaPWMAAAA=", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/accounts/0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820","version":"3"}}', + raw_bytes: + "AAAAAAAAAAAAAACzlcny6E91sWhu/cYvjLYqVyt4IAADAAAAAgAAAA==", + struct_tag: "0x1::object::ObjectCore" + } + ] + }, + "/initia/move/v1/metadata/0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": + { + metadata: { + decimals: 6, + denom: "uinit", + symbol: "INIT" + } + }, + "/initia/mstaking/v1/validators/initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66": + { + validator: { + commission: { + commission_rates: { + max_change_rate: "0.010000000000000000", + max_rate: "0.200000000000000000", + rate: "0.100000000000000000" + } + }, + description: { + details: "Provides secure validation services for dPoS networks", + identity: "8957C5091FBF4192", + moniker: "B-Harvest", + security_contact: "contact@bharvest.io", + website: "https://bharvest.io" + }, + operator_address: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + } + } + }, + POST: {} +}; + +export const mockMsgExecWithWithdrawReward = { + code: 0, + codespace: "", + data: "12410A3F2F636F736D6F732E646973747269627574696F6E2E763162657461312E4D7367576974686472617744656C656761746F72526577617264526573706F6E7365", + events: [], + gas_used: "240849", + gas_wanted: "344162", + height: "4141866", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { index: true, key: "amount", value: "33490255uinit" }, + { + index: true, + key: "validator", + value: "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" + }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "withdraw_rewards" + }, + { + attributes: [ + { + index: true, + key: "spender", + value: "init1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ffy0za" + }, + { index: true, key: "amount", value: "33490255uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + index: true, + key: "receiver", + value: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78" + }, + { index: true, key: "amount", value: "33490255uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { + index: true, + key: "recipient", + value: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78" + }, + { + index: true, + key: "sender", + value: "init1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ffy0za" + }, + { index: true, key: "amount", value: "33490255uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { index: true, key: "sender", value: "init1testgrantee" }, + { index: true, key: "module", value: "authz" }, + { index: true, key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2023-10-20T10:00:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "5000", denom: "uinit" }], + gas_limit: "344162", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "1" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1testgrantee", + msgs: [ + { + "@type": + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + delegator_address: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78", + validator_address: + "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TESTWITHDRAW123" +}; + +export const mockApiResponsesForWithdrawReward = { + GET: { + "/initia/move/v1/accounts/0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x8aef69782640e3fd5c44ddbe9c84777a6dd3cf5b","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAAiu9peCZA4/1cRN2+nIR3em3Tz1sAAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"0","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdkAAA==", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/accounts/0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x8aef69782640e3fd5c44ddbe9c84777a6dd3cf5b","version":"3"}}', + raw_bytes: + "AAAAAAAAAAAAAACKr2l4JkDj/VxE3b6chHd6bdPPWwADAAAAAgAAAA==", + struct_tag: "0x1::object::ObjectCore" + } + ] + }, + "/initia/move/v1/metadata/0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": + { + metadata: { + decimals: 6, + denom: "uinit", + symbol: "INIT" + } + }, + "/initia/mstaking/v1/validators/initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4": + { + validator: { + commission: { + commission_rates: { + max_change_rate: "0.010000000000000000", + max_rate: "0.200000000000000000", + rate: "0.050000000000000000" + } + }, + description: { + details: "", + identity: "1F41B95A84CFA4B6", + moniker: "Inertia Foundation | 🎁 Delegation Drop 🎁", + security_contact: "", + website: "https://inrt.fi" + }, + operator_address: "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" + } + } + }, + POST: {} +}; + +export const mockMsgExecWithUndelegate = { + code: 0, + codespace: "", + data: "122B0A292F696E697469612E6D7374616B696E672E76312E4D7367556E64656C6567617465526573706F6E7365", + events: [], + gas_used: "240849", + gas_wanted: "344162", + height: "4141866", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { + index: true, + key: "validator", + value: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + }, + { index: true, key: "amount", value: "50000uinit" }, + { + index: true, + key: "completion_time", + value: "2024-11-03T10:00:00Z" + }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "unbond" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { index: true, key: "sender", value: "init1testgrantee" }, + { index: true, key: "module", value: "authz" }, + { index: true, key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2023-10-20T10:00:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "5000", denom: "uinit" }], + gas_limit: "344162", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "1" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1testgrantee", + msgs: [ + { + "@type": "/initia.mstaking.v1.MsgUndelegate", + amount: [{ amount: "50000", denom: "uinit" }], + delegator_address: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", + validator_address: + "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TESTUNDELEGATE123" +}; + +export const mockApiResponsesForUndelegate = mockApiResponsesForDelegate; diff --git a/src/tests/cosmos/move/authz.fixture.ts b/src/tests/cosmos/move/authz.fixture.ts new file mode 100644 index 0000000..268c9d4 --- /dev/null +++ b/src/tests/cosmos/move/authz.fixture.ts @@ -0,0 +1,1474 @@ +// Authz MsgExec wrapping a MsgSend +export const mockMsgExecWithSendMessage = { + code: 0, + codespace: "", + data: "12260A242F636F736D6F732E62616E6B2E763162657461312E4D736753656E64526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "sender", + value: "0x1,0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820" + }, + { + index: true, + key: "module_addr", + value: "0x1" + }, + { + index: true, + key: "module_name", + value: "coin" + }, + { + index: true, + key: "function_name", + value: "sudo_transfer" + } + ], + type: "execute" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawEvent" + }, + { + index: true, + key: "data", + value: + '{"store_addr":"0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"100"}' + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::DepositEvent" + }, + { + index: true, + key: "data", + value: + '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"100"}' + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "spender", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { + index: true, + key: "amount", + value: "100uinit" + } + ], + type: "coin_spent" + }, + { + attributes: [ + { + index: true, + key: "receiver", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { + index: true, + key: "amount", + value: "100uinit" + } + ], + type: "coin_received" + }, + { + attributes: [ + { + index: true, + key: "recipient", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { + index: true, + key: "sender", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { + index: true, + key: "amount", + value: "100uinit" + } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { + index: true, + key: "sender", + value: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh" + }, + { + index: true, + key: "module", + value: "authz" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "sender", + value: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + } + ], + gas_used: "201637", + gas_wanted: "339037", + height: "4169271", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { + index: true, + key: "sender", + value: "0x1,0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820" + }, + { + index: true, + key: "module_addr", + value: "0x1" + }, + { + index: true, + key: "module_name", + value: "coin" + }, + { + index: true, + key: "function_name", + value: "sudo_transfer" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "execute" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawEvent" + }, + { + index: true, + key: "data", + value: + '{"store_addr":"0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"100"}' + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::DepositEvent" + }, + { + index: true, + key: "data", + value: + '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"100"}' + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "spender", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { + index: true, + key: "amount", + value: "100uinit" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "coin_spent" + }, + { + attributes: [ + { + index: true, + key: "receiver", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { + index: true, + key: "amount", + value: "100uinit" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "coin_received" + }, + { + attributes: [ + { + index: true, + key: "recipient", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { + index: true, + key: "sender", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { + index: true, + key: "amount", + value: "100uinit" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { + index: true, + key: "sender", + value: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh" + }, + { + index: true, + key: "module", + value: "authz" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "sender", + value: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2025-06-23T09:43:18Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [ + { + amount: "6103", + denom: "uinit" + } + ], + gas_limit: "339037", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { + single: { + mode: "SIGN_MODE_LEGACY_AMINO_JSON" + } + }, + public_key: { + "@type": "/initia.crypto.v1beta1.ethsecp256k1.PubKey", + key: "Axdt0OtFq4TF/TA4EnlF4J0Lv6E+9jFccR2/ULQVXP2a" + }, + sequence: "22" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh", + msgs: [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [ + { + amount: "100", + denom: "uinit" + } + ], + from_address: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", + to_address: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [ + "MiOnd/7iTA4hJvHSoo27HyvInTQt0LdV313+Hz5H/jNSJPeKdoHuNEatY66g3SobQehjSA7ej/3AUMSLCSvcUw==" + ] + }, + txhash: "7C8164D579F96B5C3C43BA2AB63A85B889A4917E5B29ECE801FD9F177DF71AAE" +}; + +export const mockApiResponsesForAuthzExec = { + GET: { + "/initia/move/v1/accounts/0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0xf1829676db577682e944fc3493d451b67ff3e29f","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAA8YKWdttXdoLpRPw0k9RRtn/z4p8AAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"100","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdlkAAAAAAAAAAA=", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/accounts/0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAAs5XJ8uhPdbFob83GL4y2KlcreCAAAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"74857716271","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdkvGt5tEQAAAAA=", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/denom?metadata=0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": + { + denom: "uinit" + } + }, + POST: {} +}; + +// Real transaction from Initia blockchain +// Source: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/4600CD81879C9D99A4C9EA4692448E3E40C4BBE0DB707C2E578CC2FEDC66CB71 +export const mockRealMsgExecTransaction = { + code: 0, + codespace: "", + data: "122B0A252F636F736D6F732E617574687A2E763162657461312E4D736745786563526573706F6E736512020A00", + events: [ + { + attributes: [ + { + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "grantee", value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" } + ], + type: "use_feegrant" + }, + { + attributes: [ + { + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "grantee", value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" } + ], + type: "update_feegrant" + }, + { + attributes: [ + { + key: "sender", + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" + }, + { key: "module_addr", value: "0x1" }, + { key: "module_name", value: "coin" }, + { key: "function_name", value: "sudo_transfer" } + ], + type: "execute" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"3075"}' + } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawOwnerEvent" }, + { + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositEvent" }, + { + key: "data", + value: + '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"3075"}' + } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositOwnerEvent" }, + { + key: "data", + value: '{"owner":"0xf1829676db577682e944fc3493d451b67ff3e29f"}' + } + ], + type: "move" + }, + { + attributes: [ + { + key: "spender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "3075uinit" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + key: "receiver", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { key: "amount", value: "3075uinit" } + ], + type: "coin_received" + }, + { + attributes: [ + { + key: "recipient", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { key: "sender", value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, + { key: "amount", value: "3075uinit" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "sender", value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" } + ], + type: "message" + }, + { + attributes: [ + { key: "fee", value: "3075uinit" }, + { + key: "fee_payer", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + type: "tx" + }, + { + attributes: [ + { + key: "acc_seq", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg/5" + } + ], + type: "tx" + }, + { + attributes: [ + { + key: "signature", + value: + "SMTTAEoUbh8j4P7x2lNe5BtT1BDGu/NmSpNgg/IxlftsOl4c8fd9Nfrp8OaxQ7XyDaPNvj+01tB4XASLO98aTw==" + } + ], + type: "tx" + }, + { + attributes: [ + { key: "action", value: "/cosmos.authz.v1beta1.MsgExec" }, + { key: "sender", value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" }, + { key: "module", value: "authz" }, + { key: "msg_index", value: "0" } + ], + type: "message" + }, + { + attributes: [ + { + key: "sender", + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" + }, + { key: "module_addr", value: "0x1" }, + { key: "module_name", value: "coin" }, + { key: "function_name", value: "sudo_transfer" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "execute" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawOwnerEvent" }, + { + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositOwnerEvent" }, + { + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { + key: "spender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + key: "receiver", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { + key: "recipient", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "sender", value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "sender", value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "message" + } + ], + gas_used: "156566", + gas_wanted: "204960", + height: "9303763", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { + key: "sender", + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" + }, + { key: "module_addr", value: "0x1" }, + { key: "module_name", value: "coin" }, + { key: "function_name", value: "sudo_transfer" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "execute" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { + key: "type_tag", + value: "0x1::fungible_asset::WithdrawOwnerEvent" + }, + { + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { + key: "type_tag", + value: "0x1::fungible_asset::DepositOwnerEvent" + }, + { + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { + key: "spender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + key: "receiver", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { + key: "recipient", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "action", value: "/cosmos.authz.v1beta1.MsgExec" }, + { + key: "sender", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" + }, + { key: "module", value: "authz" }, + { key: "msg_index", value: "0" } + ], + type: "message" + }, + { + attributes: [ + { + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2025-11-03T09:04:15Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "3075", denom: "uinit" }], + gas_limit: "204960", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_EIP_191" } }, + public_key: { + "@type": "/initia.crypto.v1beta1.ethsecp256k1.PubKey", + key: "Asnlgbxa6R/heXxfbYAJEELDsXuohrHFB3YftSIPJL+e" + }, + sequence: "5" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + msgs: [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [{ amount: "1000000", denom: "uinit" }], + from_address: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + to_address: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [ + "SMTTAEoUbh8j4P7x2lNe5BtT1BDGu/NmSpNgg/IxlftsOl4c8fd9Nfrp8OaxQ7XyDaPNvj+01tB4XASLO98aTw==" + ] + }, + txhash: "4600CD81879C9D99A4C9EA4692448E3E40C4BBE0DB707C2E578CC2FEDC66CB71" +}; + +export const mockApiResponsesForRealTx = { + GET: { + "/initia/move/v1/accounts/0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x190687eefda41791730cec891dcf27efabcba364","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAAGJOH7v2kF5FzDOyJHc8n76vLo2QAAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"1000000","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdlAQg8AAAAAAA==", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/denom?metadata=0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": + { denom: "uinit" } + }, + POST: {} +}; + +// Real transaction with different from/to addresses +// Source: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/C6E8685C20434A97A52EE3B040DB82B313323BB08407C5A58AB7CEA3448EB46F +export const mockRealMsgExecWithDifferentAddresses = { + code: 0, + codespace: "", + data: "122B0A252F636F736D6F732E617574687A2E763162657461312E4D736745786563526573706F6E736512020A00", + events: [ + { + attributes: [ + { + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "grantee", value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" } + ], + type: "use_feegrant" + }, + { + attributes: [ + { + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "grantee", value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" } + ], + type: "update_feegrant" + }, + { + attributes: [ + { + key: "sender", + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" + }, + { key: "module_addr", value: "0x1" }, + { key: "module_name", value: "coin" }, + { key: "function_name", value: "sudo_transfer" } + ], + type: "execute" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"3164"}' + } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawOwnerEvent" }, + { + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositEvent" }, + { + key: "data", + value: + '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"3164"}' + } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositOwnerEvent" }, + { + key: "data", + value: '{"owner":"0xf1829676db577682e944fc3493d451b67ff3e29f"}' + } + ], + type: "move" + }, + { + attributes: [ + { + key: "spender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "3164uinit" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + key: "receiver", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { key: "amount", value: "3164uinit" } + ], + type: "coin_received" + }, + { + attributes: [ + { + key: "recipient", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { key: "sender", value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, + { key: "amount", value: "3164uinit" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "sender", value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" } + ], + type: "message" + }, + { + attributes: [ + { key: "fee", value: "3164uinit" }, + { + key: "fee_payer", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + type: "tx" + }, + { + attributes: [ + { + key: "acc_seq", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg/6" + } + ], + type: "tx" + }, + { + attributes: [ + { + key: "signature", + value: + "Kn9W/NZdIyalI71QT1fUN9kniYMYyDMvT1Hf1DafUr1tmlp6A+ASxyhhUb+Sc7ZTCbjRTswb5Z5S5E07Cz5Q9A==" + } + ], + type: "tx" + }, + { + attributes: [ + { key: "action", value: "/cosmos.authz.v1beta1.MsgExec" }, + { key: "sender", value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" }, + { key: "module", value: "authz" }, + { key: "msg_index", value: "0" } + ], + type: "message" + }, + { + attributes: [ + { + key: "sender", + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" + }, + { key: "module_addr", value: "0x1" }, + { key: "module_name", value: "coin" }, + { key: "function_name", value: "sudo_transfer" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "execute" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawOwnerEvent" }, + { + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe4216a00894a4981c78ee0071e5025a1dae88fcda8d82f2e53d9c61b68258e3e","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositOwnerEvent" }, + { + key: "data", + value: '{"owner":"0xf6675f0bc278299b9d20ef71f225b0b3fd10e1b0"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { + key: "spender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + key: "receiver", + value: "init17en47z7z0q5eh8fqaaclyfdsk073pcds5pu0gr" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { + key: "recipient", + value: "init17en47z7z0q5eh8fqaaclyfdsk073pcds5pu0gr" + }, + { key: "sender", value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "sender", value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "message" + } + ], + gas_used: "160812", + gas_wanted: "210905", + height: "9303853", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { + key: "sender", + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" + }, + { key: "module_addr", value: "0x1" }, + { key: "module_name", value: "coin" }, + { key: "function_name", value: "sudo_transfer" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "execute" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::WithdrawEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { + key: "type_tag", + value: "0x1::fungible_asset::WithdrawOwnerEvent" + }, + { + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { key: "type_tag", value: "0x1::fungible_asset::DepositEvent" }, + { + key: "data", + value: + '{"store_addr":"0xe4216a00894a4981c78ee0071e5025a1dae88fcda8d82f2e53d9c61b68258e3e","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { + key: "type_tag", + value: "0x1::fungible_asset::DepositOwnerEvent" + }, + { + key: "data", + value: '{"owner":"0xf6675f0bc278299b9d20ef71f225b0b3fd10e1b0"}' + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "move" + }, + { + attributes: [ + { + key: "spender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + key: "receiver", + value: "init17en47z7z0q5eh8fqaaclyfdsk073pcds5pu0gr" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { + key: "recipient", + value: "init17en47z7z0q5eh8fqaaclyfdsk073pcds5pu0gr" + }, + { + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "amount", value: "1000000uinit" }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "action", value: "/cosmos.authz.v1beta1.MsgExec" }, + { + key: "sender", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" + }, + { key: "module", value: "authz" }, + { key: "msg_index", value: "0" } + ], + type: "message" + }, + { + attributes: [ + { + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { key: "authz_msg_index", value: "0" }, + { key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2025-11-03T09:07:34Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "3164", denom: "uinit" }], + gas_limit: "210905", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_EIP_191" } }, + public_key: { + "@type": "/initia.crypto.v1beta1.ethsecp256k1.PubKey", + key: "Asnlgbxa6R/heXxfbYAJEELDsXuohrHFB3YftSIPJL+e" + }, + sequence: "6" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + msgs: [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [{ amount: "1000000", denom: "uinit" }], + from_address: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + to_address: "init17en47z7z0q5eh8fqaaclyfdsk073pcds5pu0gr" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [ + "Kn9W/NZdIyalI71QT1fUN9kniYMYyDMvT1Hf1DafUr1tmlp6A+ASxyhhUb+Sc7ZTCbjRTswb5Z5S5E07Cz5Q9A==" + ] + }, + txhash: "C6E8685C20434A97A52EE3B040DB82B313323BB08407C5A58AB7CEA3448EB46F" +}; + +export const mockApiResponsesForRealTx2 = { + GET: { + "/initia/move/v1/accounts/0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x190687eefda41791730cec891dcf27efabcba364","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAAGJOH7v2kF5FzDOyJHc8n76vLo2QAAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"1000000","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdlAQg8AAAAAAA==", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/accounts/0xe4216a00894a4981c78ee0071e5025a1dae88fcda8d82f2e53d9c61b68258e3e/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0xe4216a00894a4981c78ee0071e5025a1dae88fcda8d82f2e53d9c61b68258e3e", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0xf6675f0bc278299b9d20ef71f225b0b3fd10e1b0","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAA9mdl8Lwngpm50g73HyJbCz/RDhsAAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0xe4216a00894a4981c78ee0071e5025a1dae88fcda8d82f2e53d9c61b68258e3e", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"1000000","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdlAQg8AAAAAAA==", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/denom?metadata=0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": + { denom: "uinit" } + }, + POST: {} +}; diff --git a/src/tests/cosmos/move/authz.test.ts b/src/tests/cosmos/move/authz.test.ts new file mode 100644 index 0000000..ae38b1c --- /dev/null +++ b/src/tests/cosmos/move/authz.test.ts @@ -0,0 +1,820 @@ +import { + initialize, + mockedAxios, + resetMockApi, + setupMockApi +} from "@/tests/_shared/helpers"; + +import { + mockApiResponsesForDelegate, + mockApiResponsesForUndelegate, + mockApiResponsesForWithdrawReward, + mockMsgExecWithDelegate, + mockMsgExecWithUndelegate, + mockMsgExecWithWithdrawReward +} from "./authz-message-types.fixture"; +import { + mockApiResponsesForAuthzExec, + mockApiResponsesForRealTx, + mockApiResponsesForRealTx2, + mockMsgExecWithSendMessage, + mockRealMsgExecTransaction, + mockRealMsgExecWithDifferentAddresses +} from "./authz.fixture"; + +jest.mock("axios"); + +const decoder = initialize(); + +describe("Authz Messages", () => { + beforeEach(() => { + resetMockApi(mockedAxios); + }); + + describe("MsgExec", () => { + it("should decode MsgExec wrapping MsgSend correctly", async () => { + setupMockApi(mockedAxios, mockApiResponsesForAuthzExec); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgExecWithSendMessage + ); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "authz_exec", + data: { + grantee: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh", + messages: [ + { + action: "send", + data: { + coins: [{ amount: "100", denom: "uinit" }], + from: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", + to: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + isIbc: false, + isOp: false + } + ] + }, + isIbc: false, + isOp: false + }); + + // Balance changes should reflect the inner message transfer + expect(decoded.messages[0].balanceChanges).toEqual({ + ft: { + init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm: { uinit: "-100" }, + init17xpfvakm2amg962yls6f84z3kell8c5l70rnql: { uinit: "100" } + }, + object: {}, + vm: "move" + }); + + expect(decoded.totalBalanceChanges).toEqual({ + ft: { + init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm: { uinit: "-100" }, + init17xpfvakm2amg962yls6f84z3kell8c5l70rnql: { uinit: "100" } + }, + object: {}, + vm: "move" + }); + }); + + it("should decode real MsgExec transaction from blockchain", async () => { + // Real transaction: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/4600CD81879C9D99A4C9EA4692448E3E40C4BBE0DB707C2E578CC2FEDC66CB71 + setupMockApi(mockedAxios, mockApiResponsesForRealTx); + + const decoded = await decoder.decodeCosmosTransaction( + mockRealMsgExecTransaction + ); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "authz_exec", + data: { + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + messages: [ + { + action: "send", + data: { + coins: [{ amount: "1000000", denom: "uinit" }], + from: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + to: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + isIbc: false, + isOp: false + } + ] + }, + isIbc: false, + isOp: false + }); + + // Since this is a self-send transaction, balance changes should net to zero + expect(decoded.totalBalanceChanges).toEqual({ + ft: { + init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp: { uinit: "0" } + }, + object: {}, + vm: "move" + }); + }); + + it("should decode real MsgExec with different addresses", async () => { + // Real transaction: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/C6E8685C20434A97A52EE3B040DB82B313323BB08407C5A58AB7CEA3448EB46F + setupMockApi(mockedAxios, mockApiResponsesForRealTx2); + + const decoded = await decoder.decodeCosmosTransaction( + mockRealMsgExecWithDifferentAddresses + ); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "authz_exec", + data: { + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + messages: [ + { + action: "send", + data: { + coins: [{ amount: "1000000", denom: "uinit" }], + from: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + to: "init17en47z7z0q5eh8fqaaclyfdsk073pcds5pu0gr" + }, + isIbc: false, + isOp: false + } + ] + }, + isIbc: false, + isOp: false + }); + + // Balance changes should show the transfer + expect(decoded.messages[0].balanceChanges).toEqual({ + ft: { + init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp: { uinit: "-1000000" }, + init17en47z7z0q5eh8fqaaclyfdsk073pcds5pu0gr: { uinit: "1000000" } + }, + object: {}, + vm: "move" + }); + + expect(decoded.totalBalanceChanges).toEqual({ + ft: { + init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp: { uinit: "-1000000" }, + init17en47z7z0q5eh8fqaaclyfdsk073pcds5pu0gr: { uinit: "1000000" } + }, + object: {}, + vm: "move" + }); + }); + + it("should decode MsgExec wrapping multiple MsgSend messages", async () => { + const mockMsgExecWithMultiple = { + code: 0, + codespace: "", + data: "TEST", + events: [], + gas_used: "250000", + gas_wanted: "400000", + height: "1000002", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { key: "spender", value: "init1sender" }, + { key: "amount", value: "100uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { key: "receiver", value: "init1receiver1" }, + { key: "amount", value: "100uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { key: "recipient", value: "init1receiver1" }, + { key: "sender", value: "init1sender" }, + { key: "amount", value: "100uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "spender", value: "init1sender" }, + { key: "amount", value: "200uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "1" } + ], + type: "coin_spent" + }, + { + attributes: [ + { key: "receiver", value: "init1receiver2" }, + { key: "amount", value: "200uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "1" } + ], + type: "coin_received" + }, + { + attributes: [ + { key: "recipient", value: "init1receiver2" }, + { key: "sender", value: "init1sender" }, + { key: "amount", value: "200uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "1" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "action", value: "/cosmos.authz.v1beta1.MsgExec" }, + { key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2025-11-03T10:10:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "6000", denom: "uinit" }], + gas_limit: "400000", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "3" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1testgrantee", + msgs: [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [{ amount: "100", denom: "uinit" }], + from_address: "init1sender", + to_address: "init1receiver1" + }, + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [{ amount: "200", denom: "uinit" }], + from_address: "init1sender", + to_address: "init1receiver2" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TEST789" + }; + + setupMockApi(mockedAxios, mockApiResponsesForAuthzExec); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgExecWithMultiple + ); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage.action).toBe("authz_exec"); + + if (decoded.messages[0].decodedMessage.action === "authz_exec") { + expect(decoded.messages[0].decodedMessage.data.grantee).toBe( + "init1testgrantee" + ); + expect(decoded.messages[0].decodedMessage.data.messages).toHaveLength( + 2 + ); + + // First inner message: MsgSend + expect(decoded.messages[0].decodedMessage.data.messages[0].action).toBe( + "send" + ); + if ( + decoded.messages[0].decodedMessage.data.messages[0].action === "send" + ) { + expect( + decoded.messages[0].decodedMessage.data.messages[0].data.from + ).toBe("init1sender"); + expect( + decoded.messages[0].decodedMessage.data.messages[0].data.to + ).toBe("init1receiver1"); + } + + // Second inner message: MsgSend + expect(decoded.messages[0].decodedMessage.data.messages[1].action).toBe( + "send" + ); + if ( + decoded.messages[0].decodedMessage.data.messages[1].action === "send" + ) { + expect( + decoded.messages[0].decodedMessage.data.messages[1].data.from + ).toBe("init1sender"); + expect( + decoded.messages[0].decodedMessage.data.messages[1].data.to + ).toBe("init1receiver2"); + } + } + }); + + it("should decode MsgExec wrapping MsgDelegate", async () => { + setupMockApi(mockedAxios, mockApiResponsesForDelegate); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgExecWithDelegate + ); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage.action).toBe("authz_exec"); + + if (decoded.messages[0].decodedMessage.action === "authz_exec") { + expect(decoded.messages[0].decodedMessage.data.grantee).toBe( + "init1testgrantee" + ); + expect(decoded.messages[0].decodedMessage.data.messages).toHaveLength( + 1 + ); + + const innerMessage = + decoded.messages[0].decodedMessage.data.messages[0]; + expect(innerMessage.action).toBe("delegate"); + + if (innerMessage.action === "delegate") { + expect(innerMessage.data.delegatorAddress).toBe( + "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + ); + expect(innerMessage.data.validatorAddress).toBe( + "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + ); + expect(innerMessage.data.coins).toEqual([ + { amount: "100000", denom: "uinit" } + ]); + expect(innerMessage.data.validator).toEqual({ + description: { + details: "Provides secure validation services for dPoS networks", + identity: "8957C5091FBF4192", + moniker: "B-Harvest", + security_contact: "contact@bharvest.io", + website: "https://bharvest.io" + }, + operator_address: + "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + }); + } + } + }); + + it("should decode MsgExec wrapping MsgWithdrawDelegatorReward", async () => { + setupMockApi(mockedAxios, mockApiResponsesForWithdrawReward); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgExecWithWithdrawReward + ); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage.action).toBe("authz_exec"); + + if (decoded.messages[0].decodedMessage.action === "authz_exec") { + expect(decoded.messages[0].decodedMessage.data.grantee).toBe( + "init1testgrantee" + ); + expect(decoded.messages[0].decodedMessage.data.messages).toHaveLength( + 1 + ); + + const innerMessage = + decoded.messages[0].decodedMessage.data.messages[0]; + expect(innerMessage.action).toBe("withdraw_delegator_reward"); + + if (innerMessage.action === "withdraw_delegator_reward") { + expect(innerMessage.data.delegatorAddress).toBe( + "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78" + ); + expect(innerMessage.data.validatorAddress).toBe( + "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" + ); + expect(innerMessage.data.coins).toEqual([ + { amount: "33490255", denom: "uinit" } + ]); + expect(innerMessage.data.validator).toEqual({ + description: { + details: "", + identity: "1F41B95A84CFA4B6", + moniker: "Inertia Foundation | 🎁 Delegation Drop 🎁", + security_contact: "", + website: "https://inrt.fi" + }, + operator_address: + "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" + }); + } + } + }); + + it("should decode MsgExec wrapping MsgUndelegate", async () => { + setupMockApi(mockedAxios, mockApiResponsesForUndelegate); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgExecWithUndelegate + ); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage.action).toBe("authz_exec"); + + if (decoded.messages[0].decodedMessage.action === "authz_exec") { + expect(decoded.messages[0].decodedMessage.data.grantee).toBe( + "init1testgrantee" + ); + expect(decoded.messages[0].decodedMessage.data.messages).toHaveLength( + 1 + ); + + const innerMessage = + decoded.messages[0].decodedMessage.data.messages[0]; + expect(innerMessage.action).toBe("undelegate"); + + if (innerMessage.action === "undelegate") { + expect(innerMessage.data.delegatorAddress).toBe( + "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + ); + expect(innerMessage.data.validatorAddress).toBe( + "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + ); + expect(innerMessage.data.coins).toEqual([ + { amount: "50000", denom: "uinit" } + ]); + expect(innerMessage.data.validator).toEqual({ + description: { + details: "Provides secure validation services for dPoS networks", + identity: "8957C5091FBF4192", + moniker: "B-Harvest", + security_contact: "contact@bharvest.io", + website: "https://bharvest.io" + }, + operator_address: + "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + }); + } + } + }); + + it("should correctly identify sender when grantee A sends granter B's tokens to C", async () => { + // Scenario: + // - Grantee A (init1grantee) has authorization from Granter B (init1granter) + // - A executes a MsgSend on behalf of B, sending B's tokens to C (init1recipient) + // - The "sender" in the inner MsgSend should be B (the granter), not A (the grantee) + + const mockMsgExecGranteeExecutesForGranter = { + code: 0, + codespace: "", + data: "TEST", + events: [], + gas_used: "150000", + gas_wanted: "250000", + height: "1000000", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { index: true, key: "spender", value: "init1granter" }, // B's tokens are spent + { index: true, key: "amount", value: "500uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { index: true, key: "receiver", value: "init1recipient" }, // C receives + { index: true, key: "amount", value: "500uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { index: true, key: "recipient", value: "init1recipient" }, + { index: true, key: "sender", value: "init1granter" }, // B is the sender + { index: true, key: "amount", value: "500uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { index: true, key: "sender", value: "init1grantee" }, // A is the executor + { index: true, key: "module", value: "authz" }, + { index: true, key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2025-11-03T10:00:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "3000", denom: "uinit" }], + gas_limit: "250000", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "1" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1grantee", // A - the one who has permission to execute + msgs: [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [{ amount: "500", denom: "uinit" }], + from_address: "init1granter", // B - the actual token owner/sender + to_address: "init1recipient" // C - the recipient + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TESTAUTHZABC" + }; + + setupMockApi(mockedAxios, mockApiResponsesForAuthzExec); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgExecGranteeExecutesForGranter + ); + + expect(decoded.messages).toHaveLength(1); + + // The outer message is MsgExec + expect(decoded.messages[0].decodedMessage.action).toBe("authz_exec"); + + if (decoded.messages[0].decodedMessage.action === "authz_exec") { + // The grantee (A) is who executed the transaction + expect(decoded.messages[0].decodedMessage.data.grantee).toBe( + "init1grantee" + ); + + // The inner message should be decoded + expect(decoded.messages[0].decodedMessage.data.messages).toHaveLength( + 1 + ); + + const innerMessage = + decoded.messages[0].decodedMessage.data.messages[0]; + expect(innerMessage.action).toBe("send"); + + if (innerMessage.action === "send") { + // CRITICAL: The sender is the granter (B), NOT the grantee (A) + expect(innerMessage.data.from).toBe("init1granter"); // B's address + expect(innerMessage.data.to).toBe("init1recipient"); // C's address + expect(innerMessage.data.coins).toEqual([ + { amount: "500", denom: "uinit" } + ]); + } + } + + // The key takeaway: + // - Grantee (A) = "init1grantee" - executes the transaction + // - Sender/Granter (B) = "init1granter" - whose tokens are actually sent + // - Recipient (C) = "init1recipient" - receives the tokens + // The grantee (A) does NOT appear as the sender in the decoded inner message! + }); + }); + + describe("MsgGrant", () => { + it("should decode MsgGrant correctly", async () => { + const mockTransaction = { + code: 0, + codespace: "", + data: "12280A262F636F736D6F732E617574687A2E763162657461312E4D73674772616E74526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgGrant" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + } + ], + gas_used: "61281", + gas_wanted: "88254", + height: "9304783", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:42:01Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1324", denom: "uinit" }], + gas_limit: "88254", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgGrant", + grant: { + authorization: { + "@type": "/cosmos.authz.v1beta1.GenericAuthorization", + msg: "/cosmos.bank.v1beta1.MsgSend" + }, + expiration: "2025-11-03T09:51:56.513Z" + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_AUTHZ_GRANT_HASH" + }; + + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "authz_grant", + data: { + authorization: { + "@type": "/cosmos.authz.v1beta1.GenericAuthorization", + msg: "/cosmos.bank.v1beta1.MsgSend" + }, + expiration: "2025-11-03T09:51:56.513Z", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + isIbc: false, + isOp: false + }); + }); + }); + + describe("MsgRevoke", () => { + it("should decode MsgRevoke correctly", async () => { + const mockTransaction = { + code: 0, + codespace: "", + data: "12290A272F636F736D6F732E617574687A2E763162657461312E4D73675265766F6B65526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgRevoke" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + } + ], + gas_used: "50587", + gas_wanted: "73283", + height: "9304830", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:43:45Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1099", denom: "uinit" }], + gas_limit: "73283", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgRevoke", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + msg_type_url: "/cosmos.bank.v1beta1.MsgSend" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_AUTHZ_REVOKE_HASH" + }; + + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "authz_revoke", + data: { + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + msg_type_url: "/cosmos.bank.v1beta1.MsgSend" + }, + isIbc: false, + isOp: false + }); + }); + }); +}); diff --git a/src/tests/cosmos/move/feegrant.test.ts b/src/tests/cosmos/move/feegrant.test.ts new file mode 100644 index 0000000..a72a70e --- /dev/null +++ b/src/tests/cosmos/move/feegrant.test.ts @@ -0,0 +1,454 @@ +import { + initialize, + mockedAxios, + resetMockApi, + setupMockApi +} from "@/tests/_shared/helpers"; + +jest.mock("axios"); + +const decoder = initialize(); + +describe("Feegrant Messages", () => { + beforeEach(() => { + resetMockApi(mockedAxios); + }); + + describe("MsgGrantAllowance", () => { + it("should decode MsgGrantAllowance correctly", async () => { + const mockTransaction = { + code: 0, + codespace: "", + data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" + }, + { + index: true, + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "module", + value: "feegrant" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "grantee", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "set_feegrant" + } + ], + gas_used: "61281", + gas_wanted: "88254", + height: "9304783", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:42:01Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1324", denom: "uinit" }], + gas_limit: "88254", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2025-11-03T09:51:56.513Z", + spend_limit: [] + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_FEEGRANT_GRANT_ALLOWANCE_HASH" + }; + + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "feegrant_grant_allowance", + data: { + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2025-11-03T09:51:56.513Z", + spend_limit: [] + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + isIbc: false, + isOp: false + }); + }); + + it("should decode MsgGrantAllowance with spend limit correctly", async () => { + const mockTransaction = { + code: 0, + codespace: "", + data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + } + ], + gas_used: "61281", + gas_wanted: "88254", + height: "9304783", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:42:01Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1324", denom: "uinit" }], + gas_limit: "88254", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2026-01-01T00:00:00.000Z", + spend_limit: [{ amount: "1000000", denom: "uinit" }] + }, + grantee: "init1abc123", + granter: "init1xyz789" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_FEEGRANT_WITH_SPEND_LIMIT_HASH" + }; + + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "feegrant_grant_allowance", + data: { + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2026-01-01T00:00:00.000Z", + spend_limit: [{ amount: "1000000", denom: "uinit" }] + }, + grantee: "init1abc123", + granter: "init1xyz789" + }, + isIbc: false, + isOp: false + }); + }); + }); + + describe("MsgRevokeAllowance", () => { + it("should decode MsgRevokeAllowance correctly", async () => { + const mockTransaction = { + code: 0, + codespace: "", + data: "12350A332F636F736D6F732E6665656772616E742E763162657461312E4D73675265766F6B65416C6C6F77616E6365526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.feegrant.v1beta1.MsgRevokeAllowance" + }, + { + index: true, + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "module", + value: "feegrant" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "grantee", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "revoke_feegrant" + } + ], + gas_used: "50587", + gas_wanted: "73283", + height: "9304830", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:43:45Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1099", denom: "uinit" }], + gas_limit: "73283", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.feegrant.v1beta1.MsgRevokeAllowance", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_FEEGRANT_REVOKE_ALLOWANCE_HASH" + }; + + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "feegrant_revoke_allowance", + data: { + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + isIbc: false, + isOp: false + }); + }); + }); + + describe("Mixed transactions", () => { + it("should decode transaction with both MsgGrantAllowance and MsgGrant", async () => { + const mockTransaction = { + code: 0, + codespace: "", + data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E736512280A262F636F736D6F732E617574687A2E763162657461312E4D73674772616E74526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgGrant" + }, + { + index: true, + key: "msg_index", + value: "1" + } + ], + type: "message" + } + ], + gas_used: "122562", + gas_wanted: "176508", + height: "9304783", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:42:01Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "2648", denom: "uinit" }], + gas_limit: "176508", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2025-11-03T09:51:56.513Z", + spend_limit: [] + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + "@type": "/cosmos.authz.v1beta1.MsgGrant", + grant: { + authorization: { + "@type": "/cosmos.authz.v1beta1.GenericAuthorization", + msg: "/cosmos.bank.v1beta1.MsgSend" + }, + expiration: "2025-11-03T09:51:56.513Z" + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: + "D2A8706153B5E0323F499A13A5D2E4EEF53D6BACF9BBC1B08D8E161EE42B8828" + }; + + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + + expect(decoded.messages).toHaveLength(2); + + // Check feegrant grant allowance + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "feegrant_grant_allowance", + data: { + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2025-11-03T09:51:56.513Z", + spend_limit: [] + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + isIbc: false, + isOp: false + }); + + // Check authz grant + expect(decoded.messages[1].decodedMessage).toEqual({ + action: "authz_grant", + data: { + authorization: { + "@type": "/cosmos.authz.v1beta1.GenericAuthorization", + msg: "/cosmos.bank.v1beta1.MsgSend" + }, + expiration: "2025-11-03T09:51:56.513Z", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + isIbc: false, + isOp: false + }); + }); + }); +}); From d5f902cd0460b385a5c0e6cc9929a14d0777d7a0 Mon Sep 17 00:00:00 2001 From: tansawit Date: Mon, 3 Nov 2025 17:12:06 +0700 Subject: [PATCH 03/11] docs: add comment explaining getDecodersForVm parameter - Clarifies purpose of optional callback in authz exec decoder - Explains recursive decoding of nested messages --- src/decoders/cosmos/authz.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/decoders/cosmos/authz.ts b/src/decoders/cosmos/authz.ts index aaf8ace..3018cc4 100644 --- a/src/decoders/cosmos/authz.ts +++ b/src/decoders/cosmos/authz.ts @@ -21,6 +21,7 @@ export const authzExecDecoder: MessageDecoder = { apiClient: ApiClient, txResponse: TxResponse, vm: VmType, + // Optional callback to recursively decode nested messages within authz exec getDecodersForVm?: (vm: VmType) => MessageDecoder[] ) => { const parsed = zMsgExec.safeParse(message); From 65e1f497e634ef0358cae3e53ee3b3a3728561cb Mon Sep 17 00:00:00 2001 From: Sawit Trisirisatayawong Date: Mon, 3 Nov 2025 17:16:26 +0700 Subject: [PATCH 04/11] Update README.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a633d80..3d14a21 100644 --- a/README.md +++ b/README.md @@ -355,9 +355,15 @@ The decoder returns a structured object with the following format: #### Authz Messages - `/cosmos.authz.v1beta1.MsgExec` - Execute authorized messages on behalf of another account +- `/cosmos.authz.v1beta1.MsgGrant` - Grant authorization to another account +- `/cosmos.authz.v1beta1.MsgRevoke` - Revoke a previously granted authorization -**Note:** MsgExec recursively decodes the inner messages it contains. All currently supported message types can be wrapped within MsgExec and will be properly decoded. +#### Feegrant Messages + +- `/cosmos.feegrant.v1beta1.MsgGrantAllowance` - Grant a fee allowance to another account +- `/cosmos.feegrant.v1beta1.MsgRevokeAllowance` - Revoke an existing fee allowance +**Note:** MsgExec recursively decodes the inner messages it contains. All currently supported message types can be wrapped within MsgExec and will be properly decoded. ### Move VM Messages - `/initia.move.v1.MsgExecute` From 14b7fab13a6472fabb1982d473bf2144af7dc770 Mon Sep 17 00:00:00 2001 From: tansawit Date: Mon, 3 Nov 2025 17:25:56 +0700 Subject: [PATCH 05/11] Fix NFT burn test by adding mock for burned token address - Add mock API response for burned NFT token address - Return empty resources array to simulate burned NFT state - Fixes test failure where metadata resolver queries burned token --- src/tests/cosmos/move/nft/nft-burn.fixture.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tests/cosmos/move/nft/nft-burn.fixture.ts b/src/tests/cosmos/move/nft/nft-burn.fixture.ts index d146fc0..691e8c2 100644 --- a/src/tests/cosmos/move/nft/nft-burn.fixture.ts +++ b/src/tests/cosmos/move/nft/nft-burn.fixture.ts @@ -303,6 +303,12 @@ export const mockMsgNftBurn = { export const mockApiResponsesForNftBurn = { GET: { + // Mock response for the burned NFT token address (NFT no longer exists after burn) + "/initia/move/v1/accounts/0x3d879bacd0cb90899bacfa30385498cdd522b06a6e7b8352e9e62aaa1036f814/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [] + }, "/initia/move/v1/accounts/0x6490737f36898086529ecc342f6abe7846d12d40d7df2a4f392e6df9b57af0f4/resources": { pagination: { next_key: null, total: "0" }, From 15dce51d52a86b390c7313b3d267024edf364f37 Mon Sep 17 00:00:00 2001 From: tansawit Date: Mon, 3 Nov 2025 17:27:49 +0700 Subject: [PATCH 06/11] feat: fix readme spacing --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3d14a21..bb38397 100644 --- a/README.md +++ b/README.md @@ -364,6 +364,7 @@ The decoder returns a structured object with the following format: - `/cosmos.feegrant.v1beta1.MsgRevokeAllowance` - Revoke an existing fee allowance **Note:** MsgExec recursively decodes the inner messages it contains. All currently supported message types can be wrapped within MsgExec and will be properly decoded. + ### Move VM Messages - `/initia.move.v1.MsgExecute` From 2148565e282870c0e04aacc307dc343c2d8b6f50 Mon Sep 17 00:00:00 2001 From: tansawit Date: Mon, 10 Nov 2025 12:24:31 +0700 Subject: [PATCH 07/11] impv: cleanup comments and update authz exec test case transaction --- src/decoder.ts | 14 - src/decoders/cosmos/authz.ts | 1 - src/tests/cosmos/move/authz.fixture.ts | 644 ++++++++++++++++-- src/tests/cosmos/move/authz.test.ts | 52 +- src/tests/cosmos/move/nft/nft-burn.fixture.ts | 1 - 5 files changed, 610 insertions(+), 102 deletions(-) diff --git a/src/decoder.ts b/src/decoder.ts index f5367b0..5d059cb 100644 --- a/src/decoder.ts +++ b/src/decoder.ts @@ -151,7 +151,6 @@ export class TxDecoder { ): Promise { const ethereumPayload = validateAndPrepareEthereumPayload(payload); - // PRE-CHECK: Is this a mirrored Cosmos transaction? const cosmosTxHash = extractCosmosTxHashFromEvm(ethereumPayload); if (cosmosTxHash) { try { @@ -167,7 +166,6 @@ export class TxDecoder { } } - // Regular Ethereum transaction flow const decoder = this._findEthereumDecoder(ethereumPayload); const balanceChanges = await calculateBalanceChangesFromEthereumLogs( @@ -221,8 +219,6 @@ export class TxDecoder { ): ReturnType { const notSupportedMessage = createNotSupportedMessage(message["@type"]); - // For failed transactions (code !== 0), logs array is empty - // Create a synthetic log from txResponse events to allow decoders to process the message const effectiveLog: Log = log || { events: txResponse.events, log: txResponse.raw_log, @@ -254,24 +250,15 @@ export class TxDecoder { } } - /** - * Decodes a mirrored Cosmos transaction by fetching and decoding the original Cosmos tx. - * - * Returns only the cosmos messages in the data field to avoid duplication. - * Metadata and totalBalanceChanges are at the root level only. - */ private async _decodeMirroredCosmosTx( cosmosTxHash: string, ethereumPayload: EthereumRpcPayload ): Promise { - // Fetch the Cosmos transaction from REST API const cosmosTxResponse = await this.apiClient.getCosmosTx(cosmosTxHash); - // Decode it using the Cosmos transaction decoder const decodedCosmosEvmTx = await this.decodeCosmosEvmTransaction(cosmosTxResponse); - // Return in cosmos_mirror format return { decodedTransaction: { action: "cosmos_mirror", @@ -281,7 +268,6 @@ export class TxDecoder { evmTxHash: ethereumPayload.tx.hash } }, - // Metadata and balance changes from the decoded Cosmos transaction metadata: decodedCosmosEvmTx.metadata, totalBalanceChanges: decodedCosmosEvmTx.totalBalanceChanges }; diff --git a/src/decoders/cosmos/authz.ts b/src/decoders/cosmos/authz.ts index 3018cc4..aaf8ace 100644 --- a/src/decoders/cosmos/authz.ts +++ b/src/decoders/cosmos/authz.ts @@ -21,7 +21,6 @@ export const authzExecDecoder: MessageDecoder = { apiClient: ApiClient, txResponse: TxResponse, vm: VmType, - // Optional callback to recursively decode nested messages within authz exec getDecodersForVm?: (vm: VmType) => MessageDecoder[] ) => { const parsed = zMsgExec.safeParse(message); diff --git a/src/tests/cosmos/move/authz.fixture.ts b/src/tests/cosmos/move/authz.fixture.ts index 268c9d4..0e4ffb5 100644 --- a/src/tests/cosmos/move/authz.fixture.ts +++ b/src/tests/cosmos/move/authz.fixture.ts @@ -1,15 +1,46 @@ // Authz MsgExec wrapping a MsgSend +// Source: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/2E9D83D12374789C6E80D64727167DCE5A9C4D5EB520FD4CDE2FCF61A8186A34 export const mockMsgExecWithSendMessage = { code: 0, codespace: "", - data: "12260A242F636F736D6F732E62616E6B2E763162657461312E4D736753656E64526573706F6E7365", + data: "122B0A252F636F736D6F732E617574687A2E763162657461312E4D736745786563526573706F6E736512020A00", events: [ + { + attributes: [ + { + index: true, + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "grantee", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" + } + ], + type: "use_feegrant" + }, + { + attributes: [ + { + index: true, + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "grantee", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" + } + ], + type: "update_feegrant" + }, { attributes: [ { index: true, key: "sender", - value: "0x1,0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820" + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" }, { index: true, @@ -40,7 +71,44 @@ export const mockMsgExecWithSendMessage = { index: true, key: "data", value: - '{"store_addr":"0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"100"}' + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"3075"}' + }, + { + index: true, + key: "amount", + value: "3075" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawOwnerEvent" + }, + { + index: true, + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { + index: true, + key: "owner", + value: "0x190687eefda41791730cec891dcf27efabcba364" } ], type: "move" @@ -56,7 +124,44 @@ export const mockMsgExecWithSendMessage = { index: true, key: "data", value: - '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"100"}' + '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"3075"}' + }, + { + index: true, + key: "amount", + value: "3075" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::DepositOwnerEvent" + }, + { + index: true, + key: "data", + value: '{"owner":"0xf1829676db577682e944fc3493d451b67ff3e29f"}' + }, + { + index: true, + key: "owner", + value: "0xf1829676db577682e944fc3493d451b67ff3e29f" } ], type: "move" @@ -66,12 +171,12 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "spender", - value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, { index: true, key: "amount", - value: "100uinit" + value: "3075uinit" } ], type: "coin_spent" @@ -86,7 +191,7 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "amount", - value: "100uinit" + value: "3075uinit" } ], type: "coin_received" @@ -101,16 +206,62 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "sender", - value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, { index: true, key: "amount", - value: "100uinit" + value: "3075uinit" } ], type: "transfer" }, + { + attributes: [ + { + index: true, + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "fee", + value: "3075uinit" + }, + { + index: true, + key: "fee_payer", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + type: "tx" + }, + { + attributes: [ + { + index: true, + key: "acc_seq", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg/7" + } + ], + type: "tx" + }, + { + attributes: [ + { + index: true, + key: "signature", + value: + "kzhSlaf54a3ZqS6uYP4Rm5R1AVmq0qyEDA5lsT91tcRNpij+ukTTbrPpysxh5JQyDXqg599PDVVctyafE4RptA==" + } + ], + type: "tx" + }, { attributes: [ { @@ -121,7 +272,7 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "sender", - value: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh" + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" }, { index: true, @@ -141,7 +292,273 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "sender", - value: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh" + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" + }, + { + index: true, + key: "module_addr", + value: "0x1" + }, + { + index: true, + key: "module_name", + value: "coin" + }, + { + index: true, + key: "function_name", + value: "sudo_transfer" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "execute" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawEvent" + }, + { + index: true, + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { + index: true, + key: "amount", + value: "1000000" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawOwnerEvent" + }, + { + index: true, + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { + index: true, + key: "owner", + value: "0x190687eefda41791730cec891dcf27efabcba364" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::DepositEvent" + }, + { + index: true, + key: "data", + value: + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { + index: true, + key: "amount", + value: "1000000" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::DepositOwnerEvent" + }, + { + index: true, + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { + index: true, + key: "owner", + value: "0x190687eefda41791730cec891dcf27efabcba364" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "spender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "amount", + value: "1000000uinit" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "coin_spent" + }, + { + attributes: [ + { + index: true, + key: "receiver", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "amount", + value: "1000000uinit" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "coin_received" + }, + { + attributes: [ + { + index: true, + key: "recipient", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "amount", + value: "1000000uinit" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "authz_msg_index", + value: "0" }, { index: true, @@ -152,9 +569,9 @@ export const mockMsgExecWithSendMessage = { type: "message" } ], - gas_used: "201637", - gas_wanted: "339037", - height: "4169271", + gas_used: "156566", + gas_wanted: "204960", + height: "9569539", info: "", logs: [ { @@ -164,7 +581,7 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "sender", - value: "0x1,0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820" + value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" }, { index: true, @@ -181,6 +598,11 @@ export const mockMsgExecWithSendMessage = { key: "function_name", value: "sudo_transfer" }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, { index: true, key: "msg_index", @@ -200,7 +622,59 @@ export const mockMsgExecWithSendMessage = { index: true, key: "data", value: - '{"store_addr":"0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"100"}' + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { + index: true, + key: "amount", + value: "1000000" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawOwnerEvent" + }, + { + index: true, + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { + index: true, + key: "owner", + value: "0x190687eefda41791730cec891dcf27efabcba364" + }, + { + index: true, + key: "authz_msg_index", + value: "0" }, { index: true, @@ -221,7 +695,59 @@ export const mockMsgExecWithSendMessage = { index: true, key: "data", value: - '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"100"}' + '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' + }, + { + index: true, + key: "amount", + value: "1000000" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" + }, + { + index: true, + key: "authz_msg_index", + value: "0" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::DepositOwnerEvent" + }, + { + index: true, + key: "data", + value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' + }, + { + index: true, + key: "owner", + value: "0x190687eefda41791730cec891dcf27efabcba364" + }, + { + index: true, + key: "authz_msg_index", + value: "0" }, { index: true, @@ -236,12 +762,17 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "spender", - value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, { index: true, key: "amount", - value: "100uinit" + value: "1000000uinit" + }, + { + index: true, + key: "authz_msg_index", + value: "0" }, { index: true, @@ -256,12 +787,17 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "receiver", - value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, { index: true, key: "amount", - value: "100uinit" + value: "1000000uinit" + }, + { + index: true, + key: "authz_msg_index", + value: "0" }, { index: true, @@ -276,17 +812,22 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "recipient", - value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, { index: true, key: "sender", - value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, { index: true, key: "amount", - value: "100uinit" + value: "1000000uinit" + }, + { + index: true, + key: "authz_msg_index", + value: "0" }, { index: true, @@ -306,7 +847,7 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "sender", - value: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh" + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" }, { index: true, @@ -326,7 +867,12 @@ export const mockMsgExecWithSendMessage = { { index: true, key: "sender", - value: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh" + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "authz_msg_index", + value: "0" }, { index: true, @@ -342,33 +888,33 @@ export const mockMsgExecWithSendMessage = { } ], raw_log: "", - timestamp: "2025-06-23T09:43:18Z", + timestamp: "2025-11-10T04:54:19Z", tx: { "@type": "/cosmos.tx.v1beta1.Tx", auth_info: { fee: { amount: [ { - amount: "6103", + amount: "3075", denom: "uinit" } ], - gas_limit: "339037", - granter: "", + gas_limit: "204960", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", payer: "" }, signer_infos: [ { mode_info: { single: { - mode: "SIGN_MODE_LEGACY_AMINO_JSON" + mode: "SIGN_MODE_EIP_191" } }, public_key: { "@type": "/initia.crypto.v1beta1.ethsecp256k1.PubKey", - key: "Axdt0OtFq4TF/TA4EnlF4J0Lv6E+9jFccR2/ULQVXP2a" + key: "Asnlgbxa6R/heXxfbYAJEELDsXuohrHFB3YftSIPJL+e" }, - sequence: "22" + sequence: "7" } ], tip: null @@ -379,18 +925,18 @@ export const mockMsgExecWithSendMessage = { messages: [ { "@type": "/cosmos.authz.v1beta1.MsgExec", - grantee: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", msgs: [ { "@type": "/cosmos.bank.v1beta1.MsgSend", amount: [ { - amount: "100", + amount: "1000000", denom: "uinit" } ], - from_address: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", - to_address: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + from_address: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + to_address: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" } ] } @@ -399,10 +945,10 @@ export const mockMsgExecWithSendMessage = { timeout_height: "0" }, signatures: [ - "MiOnd/7iTA4hJvHSoo27HyvInTQt0LdV313+Hz5H/jNSJPeKdoHuNEatY66g3SobQehjSA7ej/3AUMSLCSvcUw==" + "kzhSlaf54a3ZqS6uYP4Rm5R1AVmq0qyEDA5lsT91tcRNpij+ukTTbrPpysxh5JQyDXqg599PDVVctyafE4RptA==" ] }, - txhash: "7C8164D579F96B5C3C43BA2AB63A85B889A4917E5B29ECE801FD9F177DF71AAE" + txhash: "2E9D83D12374789C6E80D64727167DCE5A9C4D5EB520FD4CDE2FCF61A8186A34" }; export const mockApiResponsesForAuthzExec = { @@ -424,33 +970,33 @@ export const mockApiResponsesForAuthzExec = { address: "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", move_resource: - '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"100","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"1000000","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', raw_bytes: - "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdlkAAAAAAAAAAA=", + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdlAQg8AAAAAAA==", struct_tag: "0x1::fungible_asset::FungibleStore" } ] }, - "/initia/move/v1/accounts/0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e/resources": + "/initia/move/v1/accounts/0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7/resources": { pagination: { next_key: null, total: "0" }, resources: [ { address: - "0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e", + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7", move_resource: - '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820","version":"1"}}', + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x190687eefda41791730cec891dcf27efabcba364","version":"1"}}', raw_bytes: - "AAAAAAAAAAAAAAAAs5XJ8uhPdbFob83GL4y2KlcreCAAAQAAAAAAAAA=", + "AAAAAAAAAAAAAAAAGJOH7v2kF5FzDOyJHc8n76vLo2QAAQAAAAAAAAA=", struct_tag: "0x1::object::ObjectCore" }, { address: - "0xe2d99fcea59da8da2d4095cc11cb83e6ff80cf2e41522c678b8f152233d1b65e", + "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7", move_resource: - '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"74857716271","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"1000000","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', raw_bytes: - "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdkvGt5tEQAAAAA=", + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdlAQg8AAAAAAA==", struct_tag: "0x1::fungible_asset::FungibleStore" } ] @@ -463,7 +1009,6 @@ export const mockApiResponsesForAuthzExec = { POST: {} }; -// Real transaction from Initia blockchain // Source: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/4600CD81879C9D99A4C9EA4692448E3E40C4BBE0DB707C2E578CC2FEDC66CB71 export const mockRealMsgExecTransaction = { code: 0, @@ -956,7 +1501,6 @@ export const mockApiResponsesForRealTx = { POST: {} }; -// Real transaction with different from/to addresses // Source: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/C6E8685C20434A97A52EE3B040DB82B313323BB08407C5A58AB7CEA3448EB46F export const mockRealMsgExecWithDifferentAddresses = { code: 0, diff --git a/src/tests/cosmos/move/authz.test.ts b/src/tests/cosmos/move/authz.test.ts index ae38b1c..79184b4 100644 --- a/src/tests/cosmos/move/authz.test.ts +++ b/src/tests/cosmos/move/authz.test.ts @@ -43,14 +43,14 @@ describe("Authz Messages", () => { expect(decoded.messages[0].decodedMessage).toEqual({ action: "authz_exec", data: { - grantee: "init1nyky4mrxx69l4zpqtdldkk9rlf9grlnn77djlh", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", messages: [ { action: "send", data: { - coins: [{ amount: "100", denom: "uinit" }], - from: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", - to: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + coins: [{ amount: "1000000", denom: "uinit" }], + from: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + to: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" }, isIbc: false, isOp: false @@ -61,11 +61,10 @@ describe("Authz Messages", () => { isOp: false }); - // Balance changes should reflect the inner message transfer + // Since this is a self-send transaction, balance changes should net to zero expect(decoded.messages[0].balanceChanges).toEqual({ ft: { - init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm: { uinit: "-100" }, - init17xpfvakm2amg962yls6f84z3kell8c5l70rnql: { uinit: "100" } + init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp: { uinit: "0" } }, object: {}, vm: "move" @@ -73,8 +72,7 @@ describe("Authz Messages", () => { expect(decoded.totalBalanceChanges).toEqual({ ft: { - init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm: { uinit: "-100" }, - init17xpfvakm2amg962yls6f84z3kell8c5l70rnql: { uinit: "100" } + init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp: { uinit: "0" } }, object: {}, vm: "move" @@ -122,7 +120,6 @@ describe("Authz Messages", () => { }); it("should decode real MsgExec with different addresses", async () => { - // Real transaction: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/C6E8685C20434A97A52EE3B040DB82B313323BB08407C5A58AB7CEA3448EB46F setupMockApi(mockedAxios, mockApiResponsesForRealTx2); const decoded = await decoder.decodeCosmosTransaction( @@ -497,11 +494,6 @@ describe("Authz Messages", () => { }); it("should correctly identify sender when grantee A sends granter B's tokens to C", async () => { - // Scenario: - // - Grantee A (init1grantee) has authorization from Granter B (init1granter) - // - A executes a MsgSend on behalf of B, sending B's tokens to C (init1recipient) - // - The "sender" in the inner MsgSend should be B (the granter), not A (the grantee) - const mockMsgExecGranteeExecutesForGranter = { code: 0, codespace: "", @@ -516,7 +508,7 @@ describe("Authz Messages", () => { events: [ { attributes: [ - { index: true, key: "spender", value: "init1granter" }, // B's tokens are spent + { index: true, key: "spender", value: "init1granter" }, { index: true, key: "amount", value: "500uinit" }, { index: true, key: "msg_index", value: "0" }, { index: true, key: "authz_msg_index", value: "0" } @@ -525,7 +517,7 @@ describe("Authz Messages", () => { }, { attributes: [ - { index: true, key: "receiver", value: "init1recipient" }, // C receives + { index: true, key: "receiver", value: "init1recipient" }, { index: true, key: "amount", value: "500uinit" }, { index: true, key: "msg_index", value: "0" }, { index: true, key: "authz_msg_index", value: "0" } @@ -535,7 +527,7 @@ describe("Authz Messages", () => { { attributes: [ { index: true, key: "recipient", value: "init1recipient" }, - { index: true, key: "sender", value: "init1granter" }, // B is the sender + { index: true, key: "sender", value: "init1granter" }, { index: true, key: "amount", value: "500uinit" }, { index: true, key: "msg_index", value: "0" }, { index: true, key: "authz_msg_index", value: "0" } @@ -549,7 +541,7 @@ describe("Authz Messages", () => { key: "action", value: "/cosmos.authz.v1beta1.MsgExec" }, - { index: true, key: "sender", value: "init1grantee" }, // A is the executor + { index: true, key: "sender", value: "init1grantee" }, { index: true, key: "module", value: "authz" }, { index: true, key: "msg_index", value: "0" } ], @@ -589,13 +581,13 @@ describe("Authz Messages", () => { messages: [ { "@type": "/cosmos.authz.v1beta1.MsgExec", - grantee: "init1grantee", // A - the one who has permission to execute + grantee: "init1grantee", msgs: [ { "@type": "/cosmos.bank.v1beta1.MsgSend", amount: [{ amount: "500", denom: "uinit" }], - from_address: "init1granter", // B - the actual token owner/sender - to_address: "init1recipient" // C - the recipient + from_address: "init1granter", + to_address: "init1recipient" } ] } @@ -615,17 +607,12 @@ describe("Authz Messages", () => { ); expect(decoded.messages).toHaveLength(1); - - // The outer message is MsgExec expect(decoded.messages[0].decodedMessage.action).toBe("authz_exec"); if (decoded.messages[0].decodedMessage.action === "authz_exec") { - // The grantee (A) is who executed the transaction expect(decoded.messages[0].decodedMessage.data.grantee).toBe( "init1grantee" ); - - // The inner message should be decoded expect(decoded.messages[0].decodedMessage.data.messages).toHaveLength( 1 ); @@ -635,20 +622,13 @@ describe("Authz Messages", () => { expect(innerMessage.action).toBe("send"); if (innerMessage.action === "send") { - // CRITICAL: The sender is the granter (B), NOT the grantee (A) - expect(innerMessage.data.from).toBe("init1granter"); // B's address - expect(innerMessage.data.to).toBe("init1recipient"); // C's address + expect(innerMessage.data.from).toBe("init1granter"); + expect(innerMessage.data.to).toBe("init1recipient"); expect(innerMessage.data.coins).toEqual([ { amount: "500", denom: "uinit" } ]); } } - - // The key takeaway: - // - Grantee (A) = "init1grantee" - executes the transaction - // - Sender/Granter (B) = "init1granter" - whose tokens are actually sent - // - Recipient (C) = "init1recipient" - receives the tokens - // The grantee (A) does NOT appear as the sender in the decoded inner message! }); }); diff --git a/src/tests/cosmos/move/nft/nft-burn.fixture.ts b/src/tests/cosmos/move/nft/nft-burn.fixture.ts index 691e8c2..cdd9a5b 100644 --- a/src/tests/cosmos/move/nft/nft-burn.fixture.ts +++ b/src/tests/cosmos/move/nft/nft-burn.fixture.ts @@ -303,7 +303,6 @@ export const mockMsgNftBurn = { export const mockApiResponsesForNftBurn = { GET: { - // Mock response for the burned NFT token address (NFT no longer exists after burn) "/initia/move/v1/accounts/0x3d879bacd0cb90899bacfa30385498cdd522b06a6e7b8352e9e62aaa1036f814/resources": { pagination: { next_key: null, total: "0" }, From 2d06910ec8e133863d735aecebb357a475b42fbf Mon Sep 17 00:00:00 2001 From: tansawit Date: Mon, 10 Nov 2025 12:33:56 +0700 Subject: [PATCH 08/11] impv: remove duplicate authz test case --- src/tests/cosmos/move/authz.fixture.ts | 953 ------------------------- src/tests/cosmos/move/authz.test.ts | 48 -- 2 files changed, 1001 deletions(-) diff --git a/src/tests/cosmos/move/authz.fixture.ts b/src/tests/cosmos/move/authz.fixture.ts index 0e4ffb5..e2c54c6 100644 --- a/src/tests/cosmos/move/authz.fixture.ts +++ b/src/tests/cosmos/move/authz.fixture.ts @@ -1,956 +1,3 @@ -// Authz MsgExec wrapping a MsgSend -// Source: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/2E9D83D12374789C6E80D64727167DCE5A9C4D5EB520FD4CDE2FCF61A8186A34 -export const mockMsgExecWithSendMessage = { - code: 0, - codespace: "", - data: "122B0A252F636F736D6F732E617574687A2E763162657461312E4D736745786563526573706F6E736512020A00", - events: [ - { - attributes: [ - { - index: true, - key: "granter", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "grantee", - value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" - } - ], - type: "use_feegrant" - }, - { - attributes: [ - { - index: true, - key: "granter", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "grantee", - value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" - } - ], - type: "update_feegrant" - }, - { - attributes: [ - { - index: true, - key: "sender", - value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" - }, - { - index: true, - key: "module_addr", - value: "0x1" - }, - { - index: true, - key: "module_name", - value: "coin" - }, - { - index: true, - key: "function_name", - value: "sudo_transfer" - } - ], - type: "execute" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::WithdrawEvent" - }, - { - index: true, - key: "data", - value: - '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"3075"}' - }, - { - index: true, - key: "amount", - value: "3075" - }, - { - index: true, - key: "metadata_addr", - value: - "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" - }, - { - index: true, - key: "store_addr", - value: - "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::WithdrawOwnerEvent" - }, - { - index: true, - key: "data", - value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' - }, - { - index: true, - key: "owner", - value: "0x190687eefda41791730cec891dcf27efabcba364" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::DepositEvent" - }, - { - index: true, - key: "data", - value: - '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"3075"}' - }, - { - index: true, - key: "amount", - value: "3075" - }, - { - index: true, - key: "metadata_addr", - value: - "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" - }, - { - index: true, - key: "store_addr", - value: - "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::DepositOwnerEvent" - }, - { - index: true, - key: "data", - value: '{"owner":"0xf1829676db577682e944fc3493d451b67ff3e29f"}' - }, - { - index: true, - key: "owner", - value: "0xf1829676db577682e944fc3493d451b67ff3e29f" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "spender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "amount", - value: "3075uinit" - } - ], - type: "coin_spent" - }, - { - attributes: [ - { - index: true, - key: "receiver", - value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" - }, - { - index: true, - key: "amount", - value: "3075uinit" - } - ], - type: "coin_received" - }, - { - attributes: [ - { - index: true, - key: "recipient", - value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" - }, - { - index: true, - key: "sender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "amount", - value: "3075uinit" - } - ], - type: "transfer" - }, - { - attributes: [ - { - index: true, - key: "sender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - } - ], - type: "message" - }, - { - attributes: [ - { - index: true, - key: "fee", - value: "3075uinit" - }, - { - index: true, - key: "fee_payer", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - } - ], - type: "tx" - }, - { - attributes: [ - { - index: true, - key: "acc_seq", - value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg/7" - } - ], - type: "tx" - }, - { - attributes: [ - { - index: true, - key: "signature", - value: - "kzhSlaf54a3ZqS6uYP4Rm5R1AVmq0qyEDA5lsT91tcRNpij+ukTTbrPpysxh5JQyDXqg599PDVVctyafE4RptA==" - } - ], - type: "tx" - }, - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgExec" - }, - { - index: true, - key: "sender", - value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" - }, - { - index: true, - key: "module", - value: "authz" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - }, - { - attributes: [ - { - index: true, - key: "sender", - value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" - }, - { - index: true, - key: "module_addr", - value: "0x1" - }, - { - index: true, - key: "module_name", - value: "coin" - }, - { - index: true, - key: "function_name", - value: "sudo_transfer" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "execute" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::WithdrawEvent" - }, - { - index: true, - key: "data", - value: - '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' - }, - { - index: true, - key: "amount", - value: "1000000" - }, - { - index: true, - key: "metadata_addr", - value: - "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" - }, - { - index: true, - key: "store_addr", - value: - "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::WithdrawOwnerEvent" - }, - { - index: true, - key: "data", - value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' - }, - { - index: true, - key: "owner", - value: "0x190687eefda41791730cec891dcf27efabcba364" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::DepositEvent" - }, - { - index: true, - key: "data", - value: - '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' - }, - { - index: true, - key: "amount", - value: "1000000" - }, - { - index: true, - key: "metadata_addr", - value: - "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" - }, - { - index: true, - key: "store_addr", - value: - "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::DepositOwnerEvent" - }, - { - index: true, - key: "data", - value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' - }, - { - index: true, - key: "owner", - value: "0x190687eefda41791730cec891dcf27efabcba364" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "spender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "amount", - value: "1000000uinit" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "coin_spent" - }, - { - attributes: [ - { - index: true, - key: "receiver", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "amount", - value: "1000000uinit" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "coin_received" - }, - { - attributes: [ - { - index: true, - key: "recipient", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "sender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "amount", - value: "1000000uinit" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "transfer" - }, - { - attributes: [ - { - index: true, - key: "sender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - } - ], - gas_used: "156566", - gas_wanted: "204960", - height: "9569539", - info: "", - logs: [ - { - events: [ - { - attributes: [ - { - index: true, - key: "sender", - value: "0x1,0x190687eefda41791730cec891dcf27efabcba364" - }, - { - index: true, - key: "module_addr", - value: "0x1" - }, - { - index: true, - key: "module_name", - value: "coin" - }, - { - index: true, - key: "function_name", - value: "sudo_transfer" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "execute" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::WithdrawEvent" - }, - { - index: true, - key: "data", - value: - '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' - }, - { - index: true, - key: "amount", - value: "1000000" - }, - { - index: true, - key: "metadata_addr", - value: - "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" - }, - { - index: true, - key: "store_addr", - value: - "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::WithdrawOwnerEvent" - }, - { - index: true, - key: "data", - value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' - }, - { - index: true, - key: "owner", - value: "0x190687eefda41791730cec891dcf27efabcba364" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::DepositEvent" - }, - { - index: true, - key: "data", - value: - '{"store_addr":"0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"1000000"}' - }, - { - index: true, - key: "amount", - value: "1000000" - }, - { - index: true, - key: "metadata_addr", - value: - "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" - }, - { - index: true, - key: "store_addr", - value: - "0xe498fd3cd677d753de48bea10c4ebeb8ff99d8be88d442fcf156687ff3f8add7" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "type_tag", - value: "0x1::fungible_asset::DepositOwnerEvent" - }, - { - index: true, - key: "data", - value: '{"owner":"0x190687eefda41791730cec891dcf27efabcba364"}' - }, - { - index: true, - key: "owner", - value: "0x190687eefda41791730cec891dcf27efabcba364" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "move" - }, - { - attributes: [ - { - index: true, - key: "spender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "amount", - value: "1000000uinit" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "coin_spent" - }, - { - attributes: [ - { - index: true, - key: "receiver", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "amount", - value: "1000000uinit" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "coin_received" - }, - { - attributes: [ - { - index: true, - key: "recipient", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "sender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "amount", - value: "1000000uinit" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "transfer" - }, - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgExec" - }, - { - index: true, - key: "sender", - value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" - }, - { - index: true, - key: "module", - value: "authz" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - }, - { - attributes: [ - { - index: true, - key: "sender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "authz_msg_index", - value: "0" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - } - ], - log: "", - msg_index: 0 - } - ], - raw_log: "", - timestamp: "2025-11-10T04:54:19Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [ - { - amount: "3075", - denom: "uinit" - } - ], - gas_limit: "204960", - granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", - payer: "" - }, - signer_infos: [ - { - mode_info: { - single: { - mode: "SIGN_MODE_EIP_191" - } - }, - public_key: { - "@type": "/initia.crypto.v1beta1.ethsecp256k1.PubKey", - key: "Asnlgbxa6R/heXxfbYAJEELDsXuohrHFB3YftSIPJL+e" - }, - sequence: "7" - } - ], - tip: null - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.authz.v1beta1.MsgExec", - grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", - msgs: [ - { - "@type": "/cosmos.bank.v1beta1.MsgSend", - amount: [ - { - amount: "1000000", - denom: "uinit" - } - ], - from_address: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", - to_address: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - } - ] - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: [ - "kzhSlaf54a3ZqS6uYP4Rm5R1AVmq0qyEDA5lsT91tcRNpij+ukTTbrPpysxh5JQyDXqg599PDVVctyafE4RptA==" - ] - }, - txhash: "2E9D83D12374789C6E80D64727167DCE5A9C4D5EB520FD4CDE2FCF61A8186A34" -}; - export const mockApiResponsesForAuthzExec = { GET: { "/initia/move/v1/accounts/0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686/resources": diff --git a/src/tests/cosmos/move/authz.test.ts b/src/tests/cosmos/move/authz.test.ts index 79184b4..71d211b 100644 --- a/src/tests/cosmos/move/authz.test.ts +++ b/src/tests/cosmos/move/authz.test.ts @@ -17,7 +17,6 @@ import { mockApiResponsesForAuthzExec, mockApiResponsesForRealTx, mockApiResponsesForRealTx2, - mockMsgExecWithSendMessage, mockRealMsgExecTransaction, mockRealMsgExecWithDifferentAddresses } from "./authz.fixture"; @@ -32,53 +31,6 @@ describe("Authz Messages", () => { }); describe("MsgExec", () => { - it("should decode MsgExec wrapping MsgSend correctly", async () => { - setupMockApi(mockedAxios, mockApiResponsesForAuthzExec); - - const decoded = await decoder.decodeCosmosTransaction( - mockMsgExecWithSendMessage - ); - - expect(decoded.messages).toHaveLength(1); - expect(decoded.messages[0].decodedMessage).toEqual({ - action: "authz_exec", - data: { - grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", - messages: [ - { - action: "send", - data: { - coins: [{ amount: "1000000", denom: "uinit" }], - from: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", - to: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - isIbc: false, - isOp: false - } - ] - }, - isIbc: false, - isOp: false - }); - - // Since this is a self-send transaction, balance changes should net to zero - expect(decoded.messages[0].balanceChanges).toEqual({ - ft: { - init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp: { uinit: "0" } - }, - object: {}, - vm: "move" - }); - - expect(decoded.totalBalanceChanges).toEqual({ - ft: { - init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp: { uinit: "0" } - }, - object: {}, - vm: "move" - }); - }); - it("should decode real MsgExec transaction from blockchain", async () => { // Real transaction: https://scan-api.initia.xyz/v1/initia/interwoven-1/txs/4600CD81879C9D99A4C9EA4692448E3E40C4BBE0DB707C2E578CC2FEDC66CB71 setupMockApi(mockedAxios, mockApiResponsesForRealTx); From 837713fd544eb76ecd63acfc5dc223e0b8b2c068 Mon Sep 17 00:00:00 2001 From: tansawit Date: Mon, 10 Nov 2025 12:42:59 +0700 Subject: [PATCH 09/11] impv: move authz test fixtures to fixtures file --- src/tests/cosmos/move/authz.fixture.ts | 362 ++++++++++++++++++++++++ src/tests/cosmos/move/authz.test.ts | 374 +------------------------ 2 files changed, 372 insertions(+), 364 deletions(-) diff --git a/src/tests/cosmos/move/authz.fixture.ts b/src/tests/cosmos/move/authz.fixture.ts index e2c54c6..f48a48b 100644 --- a/src/tests/cosmos/move/authz.fixture.ts +++ b/src/tests/cosmos/move/authz.fixture.ts @@ -1063,3 +1063,365 @@ export const mockApiResponsesForRealTx2 = { }, POST: {} }; + +export const mockMsgExecWithMultiple = { + code: 0, + codespace: "", + data: "TEST", + events: [], + gas_used: "250000", + gas_wanted: "400000", + height: "1000002", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { key: "spender", value: "init1sender" }, + { key: "amount", value: "100uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { key: "receiver", value: "init1receiver1" }, + { key: "amount", value: "100uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { key: "recipient", value: "init1receiver1" }, + { key: "sender", value: "init1sender" }, + { key: "amount", value: "100uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "spender", value: "init1sender" }, + { key: "amount", value: "200uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "1" } + ], + type: "coin_spent" + }, + { + attributes: [ + { key: "receiver", value: "init1receiver2" }, + { key: "amount", value: "200uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "1" } + ], + type: "coin_received" + }, + { + attributes: [ + { key: "recipient", value: "init1receiver2" }, + { key: "sender", value: "init1sender" }, + { key: "amount", value: "200uinit" }, + { key: "msg_index", value: "0" }, + { key: "authz_msg_index", value: "1" } + ], + type: "transfer" + }, + { + attributes: [ + { key: "action", value: "/cosmos.authz.v1beta1.MsgExec" }, + { key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2025-11-03T10:10:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "6000", denom: "uinit" }], + gas_limit: "400000", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "3" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1testgrantee", + msgs: [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [{ amount: "100", denom: "uinit" }], + from_address: "init1sender", + to_address: "init1receiver1" + }, + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [{ amount: "200", denom: "uinit" }], + from_address: "init1sender", + to_address: "init1receiver2" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TEST789" +}; + +export const mockMsgExecGranteeExecutesForGranter = { + code: 0, + codespace: "", + data: "TEST", + events: [], + gas_used: "150000", + gas_wanted: "250000", + height: "1000000", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { index: true, key: "spender", value: "init1granter" }, + { index: true, key: "amount", value: "500uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { index: true, key: "receiver", value: "init1recipient" }, + { index: true, key: "amount", value: "500uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { index: true, key: "recipient", value: "init1recipient" }, + { index: true, key: "sender", value: "init1granter" }, + { index: true, key: "amount", value: "500uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { index: true, key: "sender", value: "init1grantee" }, + { index: true, key: "module", value: "authz" }, + { index: true, key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2025-11-03T10:00:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "3000", denom: "uinit" }], + gas_limit: "250000", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "1" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1grantee", + msgs: [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + amount: [{ amount: "500", denom: "uinit" }], + from_address: "init1granter", + to_address: "init1recipient" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TESTAUTHZABC" +}; + +export const mockAuthzGrantTransaction = { + code: 0, + codespace: "", + data: "12280A262F636F736D6F732E617574687A2E763162657461312E4D73674772616E74526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgGrant" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + } + ], + gas_used: "61281", + gas_wanted: "88254", + height: "9304783", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:42:01Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1324", denom: "uinit" }], + gas_limit: "88254", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgGrant", + grant: { + authorization: { + "@type": "/cosmos.authz.v1beta1.GenericAuthorization", + msg: "/cosmos.bank.v1beta1.MsgSend" + }, + expiration: "2025-11-03T09:51:56.513Z" + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_AUTHZ_GRANT_HASH" +}; + +export const mockAuthzRevokeTransaction = { + code: 0, + codespace: "", + data: "12290A272F636F736D6F732E617574687A2E763162657461312E4D73675265766F6B65526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgRevoke" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + } + ], + gas_used: "50587", + gas_wanted: "73283", + height: "9304830", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:43:45Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1099", denom: "uinit" }], + gas_limit: "73283", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgRevoke", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", + msg_type_url: "/cosmos.bank.v1beta1.MsgSend" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_AUTHZ_REVOKE_HASH" +}; diff --git a/src/tests/cosmos/move/authz.test.ts b/src/tests/cosmos/move/authz.test.ts index 71d211b..aa5d75b 100644 --- a/src/tests/cosmos/move/authz.test.ts +++ b/src/tests/cosmos/move/authz.test.ts @@ -17,6 +17,10 @@ import { mockApiResponsesForAuthzExec, mockApiResponsesForRealTx, mockApiResponsesForRealTx2, + mockAuthzGrantTransaction, + mockAuthzRevokeTransaction, + mockMsgExecGranteeExecutesForGranter, + mockMsgExecWithMultiple, mockRealMsgExecTransaction, mockRealMsgExecWithDifferentAddresses } from "./authz.fixture"; @@ -121,140 +125,6 @@ describe("Authz Messages", () => { }); it("should decode MsgExec wrapping multiple MsgSend messages", async () => { - const mockMsgExecWithMultiple = { - code: 0, - codespace: "", - data: "TEST", - events: [], - gas_used: "250000", - gas_wanted: "400000", - height: "1000002", - info: "", - logs: [ - { - events: [ - { - attributes: [ - { key: "spender", value: "init1sender" }, - { key: "amount", value: "100uinit" }, - { key: "msg_index", value: "0" }, - { key: "authz_msg_index", value: "0" } - ], - type: "coin_spent" - }, - { - attributes: [ - { key: "receiver", value: "init1receiver1" }, - { key: "amount", value: "100uinit" }, - { key: "msg_index", value: "0" }, - { key: "authz_msg_index", value: "0" } - ], - type: "coin_received" - }, - { - attributes: [ - { key: "recipient", value: "init1receiver1" }, - { key: "sender", value: "init1sender" }, - { key: "amount", value: "100uinit" }, - { key: "msg_index", value: "0" }, - { key: "authz_msg_index", value: "0" } - ], - type: "transfer" - }, - { - attributes: [ - { key: "spender", value: "init1sender" }, - { key: "amount", value: "200uinit" }, - { key: "msg_index", value: "0" }, - { key: "authz_msg_index", value: "1" } - ], - type: "coin_spent" - }, - { - attributes: [ - { key: "receiver", value: "init1receiver2" }, - { key: "amount", value: "200uinit" }, - { key: "msg_index", value: "0" }, - { key: "authz_msg_index", value: "1" } - ], - type: "coin_received" - }, - { - attributes: [ - { key: "recipient", value: "init1receiver2" }, - { key: "sender", value: "init1sender" }, - { key: "amount", value: "200uinit" }, - { key: "msg_index", value: "0" }, - { key: "authz_msg_index", value: "1" } - ], - type: "transfer" - }, - { - attributes: [ - { key: "action", value: "/cosmos.authz.v1beta1.MsgExec" }, - { key: "msg_index", value: "0" } - ], - type: "message" - } - ], - log: "", - msg_index: 0 - } - ], - raw_log: "", - timestamp: "2025-11-03T10:10:00Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "6000", denom: "uinit" }], - gas_limit: "400000", - granter: "", - payer: "" - }, - signer_infos: [ - { - mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, - public_key: { - "@type": "/cosmos.crypto.secp256k1.PubKey", - key: "test" - }, - sequence: "3" - } - ], - tip: null - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.authz.v1beta1.MsgExec", - grantee: "init1testgrantee", - msgs: [ - { - "@type": "/cosmos.bank.v1beta1.MsgSend", - amount: [{ amount: "100", denom: "uinit" }], - from_address: "init1sender", - to_address: "init1receiver1" - }, - { - "@type": "/cosmos.bank.v1beta1.MsgSend", - amount: [{ amount: "200", denom: "uinit" }], - from_address: "init1sender", - to_address: "init1receiver2" - } - ] - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: ["test"] - }, - txhash: "TEST789" - }; - setupMockApi(mockedAxios, mockApiResponsesForAuthzExec); const decoded = await decoder.decodeCosmosTransaction( @@ -446,112 +316,6 @@ describe("Authz Messages", () => { }); it("should correctly identify sender when grantee A sends granter B's tokens to C", async () => { - const mockMsgExecGranteeExecutesForGranter = { - code: 0, - codespace: "", - data: "TEST", - events: [], - gas_used: "150000", - gas_wanted: "250000", - height: "1000000", - info: "", - logs: [ - { - events: [ - { - attributes: [ - { index: true, key: "spender", value: "init1granter" }, - { index: true, key: "amount", value: "500uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "coin_spent" - }, - { - attributes: [ - { index: true, key: "receiver", value: "init1recipient" }, - { index: true, key: "amount", value: "500uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "coin_received" - }, - { - attributes: [ - { index: true, key: "recipient", value: "init1recipient" }, - { index: true, key: "sender", value: "init1granter" }, - { index: true, key: "amount", value: "500uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "transfer" - }, - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgExec" - }, - { index: true, key: "sender", value: "init1grantee" }, - { index: true, key: "module", value: "authz" }, - { index: true, key: "msg_index", value: "0" } - ], - type: "message" - } - ], - log: "", - msg_index: 0 - } - ], - raw_log: "", - timestamp: "2025-11-03T10:00:00Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "3000", denom: "uinit" }], - gas_limit: "250000", - granter: "", - payer: "" - }, - signer_infos: [ - { - mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, - public_key: { - "@type": "/cosmos.crypto.secp256k1.PubKey", - key: "test" - }, - sequence: "1" - } - ], - tip: null - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.authz.v1beta1.MsgExec", - grantee: "init1grantee", - msgs: [ - { - "@type": "/cosmos.bank.v1beta1.MsgSend", - amount: [{ amount: "500", denom: "uinit" }], - from_address: "init1granter", - to_address: "init1recipient" - } - ] - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: ["test"] - }, - txhash: "TESTAUTHZABC" - }; - setupMockApi(mockedAxios, mockApiResponsesForAuthzExec); const decoded = await decoder.decodeCosmosTransaction( @@ -586,73 +350,11 @@ describe("Authz Messages", () => { describe("MsgGrant", () => { it("should decode MsgGrant correctly", async () => { - const mockTransaction = { - code: 0, - codespace: "", - data: "12280A262F636F736D6F732E617574687A2E763162657461312E4D73674772616E74526573706F6E7365", - events: [ - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgGrant" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - } - ], - gas_used: "61281", - gas_wanted: "88254", - height: "9304783", - info: "", - logs: [], - raw_log: "", - timestamp: "2025-11-03T09:42:01Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "1324", denom: "uinit" }], - gas_limit: "88254", - granter: "", - payer: "" - }, - signer_infos: [] - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.authz.v1beta1.MsgGrant", - grant: { - authorization: { - "@type": "/cosmos.authz.v1beta1.GenericAuthorization", - msg: "/cosmos.bank.v1beta1.MsgSend" - }, - expiration: "2025-11-03T09:51:56.513Z" - }, - grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", - granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: [] - }, - txhash: "TEST_AUTHZ_GRANT_HASH" - }; - setupMockApi(mockedAxios, {}); - const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + const decoded = await decoder.decodeCosmosTransaction( + mockAuthzGrantTransaction + ); expect(decoded.messages).toHaveLength(1); expect(decoded.messages[0].decodedMessage).toEqual({ @@ -674,67 +376,11 @@ describe("Authz Messages", () => { describe("MsgRevoke", () => { it("should decode MsgRevoke correctly", async () => { - const mockTransaction = { - code: 0, - codespace: "", - data: "12290A272F636F736D6F732E617574687A2E763162657461312E4D73675265766F6B65526573706F6E7365", - events: [ - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgRevoke" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - } - ], - gas_used: "50587", - gas_wanted: "73283", - height: "9304830", - info: "", - logs: [], - raw_log: "", - timestamp: "2025-11-03T09:43:45Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "1099", denom: "uinit" }], - gas_limit: "73283", - granter: "", - payer: "" - }, - signer_infos: [] - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.authz.v1beta1.MsgRevoke", - grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", - granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp", - msg_type_url: "/cosmos.bank.v1beta1.MsgSend" - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: [] - }, - txhash: "TEST_AUTHZ_REVOKE_HASH" - }; - setupMockApi(mockedAxios, {}); - const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + const decoded = await decoder.decodeCosmosTransaction( + mockAuthzRevokeTransaction + ); expect(decoded.messages).toHaveLength(1); expect(decoded.messages[0].decodedMessage).toEqual({ From d676ee653f478c0d875183e40d2dbddf138d422f Mon Sep 17 00:00:00 2001 From: evilpeach Date: Tue, 18 Nov 2025 10:42:36 +0700 Subject: [PATCH 10/11] refactor: consolidate authz decoders into a single array for better maintainability --- src/decoder-registry.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/decoder-registry.ts b/src/decoder-registry.ts index d6579c2..91d1c66 100644 --- a/src/decoder-registry.ts +++ b/src/decoder-registry.ts @@ -1,12 +1,16 @@ import * as Decoders from "./decoders"; import { MessageDecoder } from "./interfaces"; -export const cosmosEvmMessageDecoders: MessageDecoder[] = [ +const cosmosAuthDecoders: MessageDecoder[] = [ Decoders.authzExecDecoder, Decoders.authzGrantDecoder, Decoders.authzRevokeDecoder, Decoders.feegrantGrantAllowanceDecoder, - Decoders.feegrantRevokeAllowanceDecoder, + Decoders.feegrantRevokeAllowanceDecoder +]; + +export const cosmosEvmMessageDecoders: MessageDecoder[] = [ + ...cosmosAuthDecoders, Decoders.sendDecoder, Decoders.finalizeTokenDepositDecoder, Decoders.initiateTokenWithdrawalDecoder, @@ -17,11 +21,7 @@ export const cosmosEvmMessageDecoders: MessageDecoder[] = [ ]; export const cosmosWasmMessageDecoders: MessageDecoder[] = [ - Decoders.authzExecDecoder, - Decoders.authzGrantDecoder, - Decoders.authzRevokeDecoder, - Decoders.feegrantGrantAllowanceDecoder, - Decoders.feegrantRevokeAllowanceDecoder, + ...cosmosAuthDecoders, Decoders.sendDecoder, Decoders.initiateTokenWithdrawalDecoder, Decoders.finalizeTokenDepositDecoder, @@ -38,11 +38,7 @@ export const cosmosWasmMessageDecoders: MessageDecoder[] = [ ]; export const cosmosMoveMessageDecoders: MessageDecoder[] = [ - Decoders.authzExecDecoder, - Decoders.authzGrantDecoder, - Decoders.authzRevokeDecoder, - Decoders.feegrantGrantAllowanceDecoder, - Decoders.feegrantRevokeAllowanceDecoder, + ...cosmosAuthDecoders, Decoders.claimMinitswapDecoder, Decoders.delegateDecoder, Decoders.delegateLockedDecoder, From ccc5440c95140df314b495324f4df464b8ede747 Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Tue, 18 Nov 2025 13:29:56 +0700 Subject: [PATCH 11/11] fix: pr comments --- .../move/authz-message-types.fixture.ts | 527 ----------------- src/tests/cosmos/move/authz.fixture.ts | 528 ++++++++++++++++++ src/tests/cosmos/move/authz.test.ts | 14 +- src/tests/cosmos/move/feegrant.fixture.ts | 329 +++++++++++ src/tests/cosmos/move/feegrant.test.ts | 354 +----------- 5 files changed, 882 insertions(+), 870 deletions(-) delete mode 100644 src/tests/cosmos/move/authz-message-types.fixture.ts create mode 100644 src/tests/cosmos/move/feegrant.fixture.ts diff --git a/src/tests/cosmos/move/authz-message-types.fixture.ts b/src/tests/cosmos/move/authz-message-types.fixture.ts deleted file mode 100644 index 57cdcf2..0000000 --- a/src/tests/cosmos/move/authz-message-types.fixture.ts +++ /dev/null @@ -1,527 +0,0 @@ -// Fixtures for testing MsgExec with various message types - -export const mockMsgExecWithDelegate = { - code: 0, - codespace: "", - data: "12290A272F696E697469612E6D7374616B696E672E76312E4D736744656C6567617465526573706F6E7365", - events: [], - gas_used: "240849", - gas_wanted: "344162", - height: "4141866", - info: "", - logs: [ - { - events: [ - { - attributes: [ - { - index: true, - key: "validator", - value: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" - }, - { - index: true, - key: "delegator", - value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" - }, - { index: true, key: "amount", value: "100000uinit" }, - { - index: true, - key: "new_shares", - value: "100000.000000000000000000uinit" - }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "delegate" - }, - { - attributes: [ - { - index: true, - key: "spender", - value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" - }, - { index: true, key: "amount", value: "100000uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "coin_spent" - }, - { - attributes: [ - { - index: true, - key: "receiver", - value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" - }, - { index: true, key: "amount", value: "100000uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "coin_received" - }, - { - attributes: [ - { - index: true, - key: "recipient", - value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" - }, - { - index: true, - key: "sender", - value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" - }, - { index: true, key: "amount", value: "100000uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "transfer" - }, - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgExec" - }, - { index: true, key: "sender", value: "init1testgrantee" }, - { index: true, key: "module", value: "authz" }, - { index: true, key: "msg_index", value: "0" } - ], - type: "message" - } - ], - log: "", - msg_index: 0 - } - ], - raw_log: "", - timestamp: "2023-10-20T10:00:00Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "5000", denom: "uinit" }], - gas_limit: "344162", - granter: "", - payer: "" - }, - signer_infos: [ - { - mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, - public_key: { - "@type": "/cosmos.crypto.secp256k1.PubKey", - key: "test" - }, - sequence: "1" - } - ], - tip: null - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.authz.v1beta1.MsgExec", - grantee: "init1testgrantee", - msgs: [ - { - "@type": "/initia.mstaking.v1.MsgDelegate", - amount: [{ amount: "100000", denom: "uinit" }], - delegator_address: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", - validator_address: - "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" - } - ] - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: ["test"] - }, - txhash: "TESTDELEGATE123" -}; - -export const mockApiResponsesForDelegate = { - GET: { - "/initia/move/v1/accounts/0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d/resources": - { - pagination: { next_key: null, total: "0" }, - resources: [ - { - address: - "0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d", - move_resource: - '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x4fea76427b8345861e80a3540a8a9d936fd39391","version":"1"}}', - raw_bytes: - "AAAAAAAAAAAAAAAAT+p2QnuDRYYegKNUCoqdk2/Tk5EAAQAAAAAAAAA=", - struct_tag: "0x1::object::ObjectCore" - }, - { - address: - "0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d", - move_resource: - '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"109114080715522","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', - raw_bytes: - "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdkCYwUaPWMAAAA=", - struct_tag: "0x1::fungible_asset::FungibleStore" - } - ] - }, - "/initia/move/v1/accounts/0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686/resources": - { - pagination: { next_key: null, total: "0" }, - resources: [ - { - address: - "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", - move_resource: - '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820","version":"3"}}', - raw_bytes: - "AAAAAAAAAAAAAACzlcny6E91sWhu/cYvjLYqVyt4IAADAAAAAgAAAA==", - struct_tag: "0x1::object::ObjectCore" - } - ] - }, - "/initia/move/v1/metadata/0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": - { - metadata: { - decimals: 6, - denom: "uinit", - symbol: "INIT" - } - }, - "/initia/mstaking/v1/validators/initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66": - { - validator: { - commission: { - commission_rates: { - max_change_rate: "0.010000000000000000", - max_rate: "0.200000000000000000", - rate: "0.100000000000000000" - } - }, - description: { - details: "Provides secure validation services for dPoS networks", - identity: "8957C5091FBF4192", - moniker: "B-Harvest", - security_contact: "contact@bharvest.io", - website: "https://bharvest.io" - }, - operator_address: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" - } - } - }, - POST: {} -}; - -export const mockMsgExecWithWithdrawReward = { - code: 0, - codespace: "", - data: "12410A3F2F636F736D6F732E646973747269627574696F6E2E763162657461312E4D7367576974686472617744656C656761746F72526577617264526573706F6E7365", - events: [], - gas_used: "240849", - gas_wanted: "344162", - height: "4141866", - info: "", - logs: [ - { - events: [ - { - attributes: [ - { index: true, key: "amount", value: "33490255uinit" }, - { - index: true, - key: "validator", - value: "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" - }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "withdraw_rewards" - }, - { - attributes: [ - { - index: true, - key: "spender", - value: "init1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ffy0za" - }, - { index: true, key: "amount", value: "33490255uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "coin_spent" - }, - { - attributes: [ - { - index: true, - key: "receiver", - value: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78" - }, - { index: true, key: "amount", value: "33490255uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "coin_received" - }, - { - attributes: [ - { - index: true, - key: "recipient", - value: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78" - }, - { - index: true, - key: "sender", - value: "init1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ffy0za" - }, - { index: true, key: "amount", value: "33490255uinit" }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "transfer" - }, - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgExec" - }, - { index: true, key: "sender", value: "init1testgrantee" }, - { index: true, key: "module", value: "authz" }, - { index: true, key: "msg_index", value: "0" } - ], - type: "message" - } - ], - log: "", - msg_index: 0 - } - ], - raw_log: "", - timestamp: "2023-10-20T10:00:00Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "5000", denom: "uinit" }], - gas_limit: "344162", - granter: "", - payer: "" - }, - signer_infos: [ - { - mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, - public_key: { - "@type": "/cosmos.crypto.secp256k1.PubKey", - key: "test" - }, - sequence: "1" - } - ], - tip: null - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.authz.v1beta1.MsgExec", - grantee: "init1testgrantee", - msgs: [ - { - "@type": - "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", - delegator_address: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78", - validator_address: - "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" - } - ] - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: ["test"] - }, - txhash: "TESTWITHDRAW123" -}; - -export const mockApiResponsesForWithdrawReward = { - GET: { - "/initia/move/v1/accounts/0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f/resources": - { - pagination: { next_key: null, total: "0" }, - resources: [ - { - address: - "0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f", - move_resource: - '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x8aef69782640e3fd5c44ddbe9c84777a6dd3cf5b","version":"1"}}', - raw_bytes: - "AAAAAAAAAAAAAAAAiu9peCZA4/1cRN2+nIR3em3Tz1sAAQAAAAAAAAA=", - struct_tag: "0x1::object::ObjectCore" - }, - { - address: - "0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f", - move_resource: - '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"0","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', - raw_bytes: "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdkAAA==", - struct_tag: "0x1::fungible_asset::FungibleStore" - } - ] - }, - "/initia/move/v1/accounts/0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686/resources": - { - pagination: { next_key: null, total: "0" }, - resources: [ - { - address: - "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", - move_resource: - '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x8aef69782640e3fd5c44ddbe9c84777a6dd3cf5b","version":"3"}}', - raw_bytes: - "AAAAAAAAAAAAAACKr2l4JkDj/VxE3b6chHd6bdPPWwADAAAAAgAAAA==", - struct_tag: "0x1::object::ObjectCore" - } - ] - }, - "/initia/move/v1/metadata/0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": - { - metadata: { - decimals: 6, - denom: "uinit", - symbol: "INIT" - } - }, - "/initia/mstaking/v1/validators/initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4": - { - validator: { - commission: { - commission_rates: { - max_change_rate: "0.010000000000000000", - max_rate: "0.200000000000000000", - rate: "0.050000000000000000" - } - }, - description: { - details: "", - identity: "1F41B95A84CFA4B6", - moniker: "Inertia Foundation | 🎁 Delegation Drop 🎁", - security_contact: "", - website: "https://inrt.fi" - }, - operator_address: "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" - } - } - }, - POST: {} -}; - -export const mockMsgExecWithUndelegate = { - code: 0, - codespace: "", - data: "122B0A292F696E697469612E6D7374616B696E672E76312E4D7367556E64656C6567617465526573706F6E7365", - events: [], - gas_used: "240849", - gas_wanted: "344162", - height: "4141866", - info: "", - logs: [ - { - events: [ - { - attributes: [ - { - index: true, - key: "validator", - value: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" - }, - { index: true, key: "amount", value: "50000uinit" }, - { - index: true, - key: "completion_time", - value: "2024-11-03T10:00:00Z" - }, - { index: true, key: "msg_index", value: "0" }, - { index: true, key: "authz_msg_index", value: "0" } - ], - type: "unbond" - }, - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgExec" - }, - { index: true, key: "sender", value: "init1testgrantee" }, - { index: true, key: "module", value: "authz" }, - { index: true, key: "msg_index", value: "0" } - ], - type: "message" - } - ], - log: "", - msg_index: 0 - } - ], - raw_log: "", - timestamp: "2023-10-20T10:00:00Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "5000", denom: "uinit" }], - gas_limit: "344162", - granter: "", - payer: "" - }, - signer_infos: [ - { - mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, - public_key: { - "@type": "/cosmos.crypto.secp256k1.PubKey", - key: "test" - }, - sequence: "1" - } - ], - tip: null - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.authz.v1beta1.MsgExec", - grantee: "init1testgrantee", - msgs: [ - { - "@type": "/initia.mstaking.v1.MsgUndelegate", - amount: [{ amount: "50000", denom: "uinit" }], - delegator_address: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", - validator_address: - "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" - } - ] - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: ["test"] - }, - txhash: "TESTUNDELEGATE123" -}; - -export const mockApiResponsesForUndelegate = mockApiResponsesForDelegate; diff --git a/src/tests/cosmos/move/authz.fixture.ts b/src/tests/cosmos/move/authz.fixture.ts index f48a48b..6fb1646 100644 --- a/src/tests/cosmos/move/authz.fixture.ts +++ b/src/tests/cosmos/move/authz.fixture.ts @@ -1425,3 +1425,531 @@ export const mockAuthzRevokeTransaction = { }, txhash: "TEST_AUTHZ_REVOKE_HASH" }; + +// Fixtures for testing MsgExec with various message types + +export const mockMsgExecWithDelegate = { + code: 0, + codespace: "", + data: "12290A272F696E697469612E6D7374616B696E672E76312E4D736744656C6567617465526573706F6E7365", + events: [], + gas_used: "240849", + gas_wanted: "344162", + height: "4141866", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { + index: true, + key: "validator", + value: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + }, + { + index: true, + key: "delegator", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { index: true, key: "amount", value: "100000uinit" }, + { + index: true, + key: "new_shares", + value: "100000.000000000000000000uinit" + }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "delegate" + }, + { + attributes: [ + { + index: true, + key: "spender", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { index: true, key: "amount", value: "100000uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + index: true, + key: "receiver", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { index: true, key: "amount", value: "100000uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { + index: true, + key: "recipient", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { + index: true, + key: "sender", + value: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm" + }, + { index: true, key: "amount", value: "100000uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { index: true, key: "sender", value: "init1testgrantee" }, + { index: true, key: "module", value: "authz" }, + { index: true, key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2023-10-20T10:00:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "5000", denom: "uinit" }], + gas_limit: "344162", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "1" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1testgrantee", + msgs: [ + { + "@type": "/initia.mstaking.v1.MsgDelegate", + amount: [{ amount: "100000", denom: "uinit" }], + delegator_address: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", + validator_address: + "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TESTDELEGATE123" +}; + +export const mockApiResponsesForDelegate = { + GET: { + "/initia/move/v1/accounts/0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x4fea76427b8345861e80a3540a8a9d936fd39391","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAAT+p2QnuDRYYegKNUCoqdk2/Tk5EAAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0x2bb7235591022997f63404a8dbfd94175806ff5644ea7ccc61a15eaac9c8674d", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"109114080715522","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdkCYwUaPWMAAAA=", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/accounts/0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0xb395c9f2e84f75b1686fcdc62f8cb62a572b7820","version":"3"}}', + raw_bytes: + "AAAAAAAAAAAAAACzlcny6E91sWhu/cYvjLYqVyt4IAADAAAAAgAAAA==", + struct_tag: "0x1::object::ObjectCore" + } + ] + }, + "/initia/move/v1/metadata/0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": + { + metadata: { + decimals: 6, + denom: "uinit", + symbol: "INIT" + } + }, + "/initia/mstaking/v1/validators/initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66": + { + validator: { + commission: { + commission_rates: { + max_change_rate: "0.010000000000000000", + max_rate: "0.200000000000000000", + rate: "0.100000000000000000" + } + }, + description: { + details: "Provides secure validation services for dPoS networks", + identity: "8957C5091FBF4192", + moniker: "B-Harvest", + security_contact: "contact@bharvest.io", + website: "https://bharvest.io" + }, + operator_address: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + } + } + }, + POST: {} +}; + +export const mockMsgExecWithWithdrawReward = { + code: 0, + codespace: "", + data: "12410A3F2F636F736D6F732E646973747269627574696F6E2E763162657461312E4D7367576974686472617744656C656761746F72526577617264526573706F6E7365", + events: [], + gas_used: "240849", + gas_wanted: "344162", + height: "4141866", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { index: true, key: "amount", value: "33490255uinit" }, + { + index: true, + key: "validator", + value: "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" + }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "withdraw_rewards" + }, + { + attributes: [ + { + index: true, + key: "spender", + value: "init1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ffy0za" + }, + { index: true, key: "amount", value: "33490255uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_spent" + }, + { + attributes: [ + { + index: true, + key: "receiver", + value: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78" + }, + { index: true, key: "amount", value: "33490255uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "coin_received" + }, + { + attributes: [ + { + index: true, + key: "recipient", + value: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78" + }, + { + index: true, + key: "sender", + value: "init1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ffy0za" + }, + { index: true, key: "amount", value: "33490255uinit" }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { index: true, key: "sender", value: "init1testgrantee" }, + { index: true, key: "module", value: "authz" }, + { index: true, key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2023-10-20T10:00:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "5000", denom: "uinit" }], + gas_limit: "344162", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "1" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1testgrantee", + msgs: [ + { + "@type": + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + delegator_address: "init13thkj7pxgr3l6hzymklfeprh0fka8n6mzckr78", + validator_address: + "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TESTWITHDRAW123" +}; + +export const mockApiResponsesForWithdrawReward = { + GET: { + "/initia/move/v1/accounts/0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x8aef69782640e3fd5c44ddbe9c84777a6dd3cf5b","version":"1"}}', + raw_bytes: + "AAAAAAAAAAAAAAAAiu9peCZA4/1cRN2+nIR3em3Tz1sAAQAAAAAAAAA=", + struct_tag: "0x1::object::ObjectCore" + }, + { + address: + "0x1f4590c6fe1596a76c2d3414bc1ab4e8b4c4a5a25853625abff6684e580a634f", + move_resource: + '{"type":"0x1::fungible_asset::FungibleStore","data":{"balance":"0","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdkAAA==", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/initia/move/v1/accounts/0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686/resources": + { + pagination: { next_key: null, total: "0" }, + resources: [ + { + address: + "0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686", + move_resource: + '{"type":"0x1::object::ObjectCore","data":{"allow_ungated_transfer":false,"owner":"0x8aef69782640e3fd5c44ddbe9c84777a6dd3cf5b","version":"3"}}', + raw_bytes: + "AAAAAAAAAAAAAACKr2l4JkDj/VxE3b6chHd6bdPPWwADAAAAAgAAAA==", + struct_tag: "0x1::object::ObjectCore" + } + ] + }, + "/initia/move/v1/metadata/0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": + { + metadata: { + decimals: 6, + denom: "uinit", + symbol: "INIT" + } + }, + "/initia/mstaking/v1/validators/initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4": + { + validator: { + commission: { + commission_rates: { + max_change_rate: "0.010000000000000000", + max_rate: "0.200000000000000000", + rate: "0.050000000000000000" + } + }, + description: { + details: "", + identity: "1F41B95A84CFA4B6", + moniker: "Inertia Foundation | 🎁 Delegation Drop 🎁", + security_contact: "", + website: "https://inrt.fi" + }, + operator_address: "initvaloper1gs7kwd5jm8ghnvx4z973aqcenfcej6ykpuydc4" + } + } + }, + POST: {} +}; + +export const mockMsgExecWithUndelegate = { + code: 0, + codespace: "", + data: "122B0A292F696E697469612E6D7374616B696E672E76312E4D7367556E64656C6567617465526573706F6E7365", + events: [], + gas_used: "240849", + gas_wanted: "344162", + height: "4141866", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { + index: true, + key: "validator", + value: "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + }, + { index: true, key: "amount", value: "50000uinit" }, + { + index: true, + key: "completion_time", + value: "2024-11-03T10:00:00Z" + }, + { index: true, key: "msg_index", value: "0" }, + { index: true, key: "authz_msg_index", value: "0" } + ], + type: "unbond" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgExec" + }, + { index: true, key: "sender", value: "init1testgrantee" }, + { index: true, key: "module", value: "authz" }, + { index: true, key: "msg_index", value: "0" } + ], + type: "message" + } + ], + log: "", + msg_index: 0 + } + ], + raw_log: "", + timestamp: "2023-10-20T10:00:00Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "5000", denom: "uinit" }], + gas_limit: "344162", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { single: { mode: "SIGN_MODE_DIRECT" } }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "test" + }, + sequence: "1" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.authz.v1beta1.MsgExec", + grantee: "init1testgrantee", + msgs: [ + { + "@type": "/initia.mstaking.v1.MsgUndelegate", + amount: [{ amount: "50000", denom: "uinit" }], + delegator_address: "init1kw2unuhgfa6mz6r0ehrzlr9k9ftjk7pql8u5fm", + validator_address: + "initvaloper1cmlx2pqfgt2kpshe2fmc40epzvg699eqv3ax66" + } + ] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: ["test"] + }, + txhash: "TESTUNDELEGATE123" +}; + +export const mockApiResponsesForUndelegate = mockApiResponsesForDelegate; diff --git a/src/tests/cosmos/move/authz.test.ts b/src/tests/cosmos/move/authz.test.ts index aa5d75b..201f798 100644 --- a/src/tests/cosmos/move/authz.test.ts +++ b/src/tests/cosmos/move/authz.test.ts @@ -5,22 +5,20 @@ import { setupMockApi } from "@/tests/_shared/helpers"; -import { - mockApiResponsesForDelegate, - mockApiResponsesForUndelegate, - mockApiResponsesForWithdrawReward, - mockMsgExecWithDelegate, - mockMsgExecWithUndelegate, - mockMsgExecWithWithdrawReward -} from "./authz-message-types.fixture"; import { mockApiResponsesForAuthzExec, + mockApiResponsesForDelegate, mockApiResponsesForRealTx, mockApiResponsesForRealTx2, + mockApiResponsesForUndelegate, + mockApiResponsesForWithdrawReward, mockAuthzGrantTransaction, mockAuthzRevokeTransaction, mockMsgExecGranteeExecutesForGranter, + mockMsgExecWithDelegate, mockMsgExecWithMultiple, + mockMsgExecWithUndelegate, + mockMsgExecWithWithdrawReward, mockRealMsgExecTransaction, mockRealMsgExecWithDifferentAddresses } from "./authz.fixture"; diff --git a/src/tests/cosmos/move/feegrant.fixture.ts b/src/tests/cosmos/move/feegrant.fixture.ts new file mode 100644 index 0000000..2f45492 --- /dev/null +++ b/src/tests/cosmos/move/feegrant.fixture.ts @@ -0,0 +1,329 @@ +export const mockMsgGrantAllowance = { + code: 0, + codespace: "", + data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" + }, + { + index: true, + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "module", + value: "feegrant" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "grantee", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "set_feegrant" + } + ], + gas_used: "61281", + gas_wanted: "88254", + height: "9304783", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:42:01Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1324", denom: "uinit" }], + gas_limit: "88254", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2025-11-03T09:51:56.513Z", + spend_limit: [] + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_FEEGRANT_GRANT_ALLOWANCE_HASH" +}; + +export const mockMsgGrantAllowanceWithSpendLimit = { + code: 0, + codespace: "", + data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + } + ], + gas_used: "61281", + gas_wanted: "88254", + height: "9304783", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:42:01Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1324", denom: "uinit" }], + gas_limit: "88254", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2026-01-01T00:00:00.000Z", + spend_limit: [{ amount: "1000000", denom: "uinit" }] + }, + grantee: "init1abc123", + granter: "init1xyz789" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_FEEGRANT_WITH_SPEND_LIMIT_HASH" +}; + +export const mockMsgRevokeAllowance = { + code: 0, + codespace: "", + data: "12350A332F636F736D6F732E6665656772616E742E763162657461312E4D73675265766F6B65416C6C6F77616E6365526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.feegrant.v1beta1.MsgRevokeAllowance" + }, + { + index: true, + key: "sender", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "module", + value: "feegrant" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "granter", + value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + index: true, + key: "grantee", + value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "revoke_feegrant" + } + ], + gas_used: "50587", + gas_wanted: "73283", + height: "9304830", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:43:45Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "1099", denom: "uinit" }], + gas_limit: "73283", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.feegrant.v1beta1.MsgRevokeAllowance", + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "TEST_FEEGRANT_REVOKE_ALLOWANCE_HASH" +}; + +export const mockMixedFeegrantAuthzTransaction = { + code: 0, + codespace: "", + data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E736512280A262F636F736D6F732E617574687A2E763162657461312E4D73674772616E74526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/cosmos.authz.v1beta1.MsgGrant" + }, + { + index: true, + key: "msg_index", + value: "1" + } + ], + type: "message" + } + ], + gas_used: "122562", + gas_wanted: "176508", + height: "9304783", + info: "", + logs: [], + raw_log: "", + timestamp: "2025-11-03T09:42:01Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [{ amount: "2648", denom: "uinit" }], + gas_limit: "176508", + granter: "", + payer: "" + }, + signer_infos: [] + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", + allowance: { + "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", + expiration: "2025-11-03T09:51:56.513Z", + spend_limit: [] + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + }, + { + "@type": "/cosmos.authz.v1beta1.MsgGrant", + grant: { + authorization: { + "@type": "/cosmos.authz.v1beta1.GenericAuthorization", + msg: "/cosmos.bank.v1beta1.MsgSend" + }, + expiration: "2025-11-03T09:51:56.513Z" + }, + grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", + granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [] + }, + txhash: "D2A8706153B5E0323F499A13A5D2E4EEF53D6BACF9BBC1B08D8E161EE42B8828" +}; diff --git a/src/tests/cosmos/move/feegrant.test.ts b/src/tests/cosmos/move/feegrant.test.ts index a72a70e..1cd1485 100644 --- a/src/tests/cosmos/move/feegrant.test.ts +++ b/src/tests/cosmos/move/feegrant.test.ts @@ -5,6 +5,13 @@ import { setupMockApi } from "@/tests/_shared/helpers"; +import { + mockMixedFeegrantAuthzTransaction, + mockMsgGrantAllowance, + mockMsgGrantAllowanceWithSpendLimit, + mockMsgRevokeAllowance +} from "./feegrant.fixture"; + jest.mock("axios"); const decoder = initialize(); @@ -16,101 +23,11 @@ describe("Feegrant Messages", () => { describe("MsgGrantAllowance", () => { it("should decode MsgGrantAllowance correctly", async () => { - const mockTransaction = { - code: 0, - codespace: "", - data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E7365", - events: [ - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" - }, - { - index: true, - key: "sender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "module", - value: "feegrant" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - }, - { - attributes: [ - { - index: true, - key: "granter", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "grantee", - value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "set_feegrant" - } - ], - gas_used: "61281", - gas_wanted: "88254", - height: "9304783", - info: "", - logs: [], - raw_log: "", - timestamp: "2025-11-03T09:42:01Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "1324", denom: "uinit" }], - gas_limit: "88254", - granter: "", - payer: "" - }, - signer_infos: [] - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", - allowance: { - "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", - expiration: "2025-11-03T09:51:56.513Z", - spend_limit: [] - }, - grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", - granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: [] - }, - txhash: "TEST_FEEGRANT_GRANT_ALLOWANCE_HASH" - }; - setupMockApi(mockedAxios, {}); - const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + const decoded = await decoder.decodeCosmosTransaction( + mockMsgGrantAllowance + ); expect(decoded.messages).toHaveLength(1); expect(decoded.messages[0].decodedMessage).toEqual({ @@ -130,71 +47,11 @@ describe("Feegrant Messages", () => { }); it("should decode MsgGrantAllowance with spend limit correctly", async () => { - const mockTransaction = { - code: 0, - codespace: "", - data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E7365", - events: [ - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - } - ], - gas_used: "61281", - gas_wanted: "88254", - height: "9304783", - info: "", - logs: [], - raw_log: "", - timestamp: "2025-11-03T09:42:01Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "1324", denom: "uinit" }], - gas_limit: "88254", - granter: "", - payer: "" - }, - signer_infos: [] - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", - allowance: { - "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", - expiration: "2026-01-01T00:00:00.000Z", - spend_limit: [{ amount: "1000000", denom: "uinit" }] - }, - grantee: "init1abc123", - granter: "init1xyz789" - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: [] - }, - txhash: "TEST_FEEGRANT_WITH_SPEND_LIMIT_HASH" - }; - setupMockApi(mockedAxios, {}); - const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + const decoded = await decoder.decodeCosmosTransaction( + mockMsgGrantAllowanceWithSpendLimit + ); expect(decoded.messages).toHaveLength(1); expect(decoded.messages[0].decodedMessage).toEqual({ @@ -216,96 +73,11 @@ describe("Feegrant Messages", () => { describe("MsgRevokeAllowance", () => { it("should decode MsgRevokeAllowance correctly", async () => { - const mockTransaction = { - code: 0, - codespace: "", - data: "12350A332F636F736D6F732E6665656772616E742E763162657461312E4D73675265766F6B65416C6C6F77616E6365526573706F6E7365", - events: [ - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.feegrant.v1beta1.MsgRevokeAllowance" - }, - { - index: true, - key: "sender", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "module", - value: "feegrant" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - }, - { - attributes: [ - { - index: true, - key: "granter", - value: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - index: true, - key: "grantee", - value: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "revoke_feegrant" - } - ], - gas_used: "50587", - gas_wanted: "73283", - height: "9304830", - info: "", - logs: [], - raw_log: "", - timestamp: "2025-11-03T09:43:45Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "1099", denom: "uinit" }], - gas_limit: "73283", - granter: "", - payer: "" - }, - signer_infos: [] - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.feegrant.v1beta1.MsgRevokeAllowance", - grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", - granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: [] - }, - txhash: "TEST_FEEGRANT_REVOKE_ALLOWANCE_HASH" - }; - setupMockApi(mockedAxios, {}); - const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + const decoded = await decoder.decodeCosmosTransaction( + mockMsgRevokeAllowance + ); expect(decoded.messages).toHaveLength(1); expect(decoded.messages[0].decodedMessage).toEqual({ @@ -322,99 +94,11 @@ describe("Feegrant Messages", () => { describe("Mixed transactions", () => { it("should decode transaction with both MsgGrantAllowance and MsgGrant", async () => { - const mockTransaction = { - code: 0, - codespace: "", - data: "12340A322F636F736D6F732E6665656772616E742E763162657461312E4D73674772616E74416C6C6F77616E6365526573706F6E736512280A262F636F736D6F732E617574687A2E763162657461312E4D73674772616E74526573706F6E7365", - events: [ - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.feegrant.v1beta1.MsgGrantAllowance" - }, - { - index: true, - key: "msg_index", - value: "0" - } - ], - type: "message" - }, - { - attributes: [ - { - index: true, - key: "action", - value: "/cosmos.authz.v1beta1.MsgGrant" - }, - { - index: true, - key: "msg_index", - value: "1" - } - ], - type: "message" - } - ], - gas_used: "122562", - gas_wanted: "176508", - height: "9304783", - info: "", - logs: [], - raw_log: "", - timestamp: "2025-11-03T09:42:01Z", - tx: { - "@type": "/cosmos.tx.v1beta1.Tx", - auth_info: { - fee: { - amount: [{ amount: "2648", denom: "uinit" }], - gas_limit: "176508", - granter: "", - payer: "" - }, - signer_infos: [] - }, - body: { - extension_options: [], - memo: "", - messages: [ - { - "@type": "/cosmos.feegrant.v1beta1.MsgGrantAllowance", - allowance: { - "@type": "/cosmos.feegrant.v1beta1.BasicAllowance", - expiration: "2025-11-03T09:51:56.513Z", - spend_limit: [] - }, - grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", - granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - }, - { - "@type": "/cosmos.authz.v1beta1.MsgGrant", - grant: { - authorization: { - "@type": "/cosmos.authz.v1beta1.GenericAuthorization", - msg: "/cosmos.bank.v1beta1.MsgSend" - }, - expiration: "2025-11-03T09:51:56.513Z" - }, - grantee: "init1rw34mgv2y626996n2ccpl6lfctk43v7azmarvg", - granter: "init1ryrg0mha5stezucvajy3mne8a74uhgmygdlnxp" - } - ], - non_critical_extension_options: [], - timeout_height: "0" - }, - signatures: [] - }, - txhash: - "D2A8706153B5E0323F499A13A5D2E4EEF53D6BACF9BBC1B08D8E161EE42B8828" - }; - setupMockApi(mockedAxios, {}); - const decoded = await decoder.decodeCosmosTransaction(mockTransaction); + const decoded = await decoder.decodeCosmosTransaction( + mockMixedFeegrantAuthzTransaction + ); expect(decoded.messages).toHaveLength(2);