diff --git a/README.md b/README.md index 845b8ba..bb38397 100644 --- a/README.md +++ b/README.md @@ -352,6 +352,19 @@ 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 +- `/cosmos.authz.v1beta1.MsgGrant` - Grant authorization to another account +- `/cosmos.authz.v1beta1.MsgRevoke` - Revoke a previously granted authorization + +#### 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` diff --git a/src/decoder-registry.ts b/src/decoder-registry.ts index 317be90..91d1c66 100644 --- a/src/decoder-registry.ts +++ b/src/decoder-registry.ts @@ -1,7 +1,16 @@ import * as Decoders from "./decoders"; import { MessageDecoder } from "./interfaces"; +const cosmosAuthDecoders: MessageDecoder[] = [ + Decoders.authzExecDecoder, + Decoders.authzGrantDecoder, + Decoders.authzRevokeDecoder, + Decoders.feegrantGrantAllowanceDecoder, + Decoders.feegrantRevokeAllowanceDecoder +]; + export const cosmosEvmMessageDecoders: MessageDecoder[] = [ + ...cosmosAuthDecoders, Decoders.sendDecoder, Decoders.finalizeTokenDepositDecoder, Decoders.initiateTokenWithdrawalDecoder, @@ -12,6 +21,7 @@ export const cosmosEvmMessageDecoders: MessageDecoder[] = [ ]; export const cosmosWasmMessageDecoders: MessageDecoder[] = [ + ...cosmosAuthDecoders, Decoders.sendDecoder, Decoders.initiateTokenWithdrawalDecoder, Decoders.finalizeTokenDepositDecoder, @@ -28,6 +38,7 @@ export const cosmosWasmMessageDecoders: MessageDecoder[] = [ ]; export const cosmosMoveMessageDecoders: MessageDecoder[] = [ + ...cosmosAuthDecoders, Decoders.claimMinitswapDecoder, Decoders.delegateDecoder, Decoders.delegateLockedDecoder, diff --git a/src/decoder.ts b/src/decoder.ts index cfebc4f..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,28 +219,13 @@ 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, msg_index: messageIndex }; - 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( @@ -258,7 +241,8 @@ export class TxDecoder { effectiveLog, this.apiClient, txResponse, - vm + vm, + this._getDecodersForVm.bind(this) ); } catch (e) { console.error(e); @@ -266,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", @@ -293,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 }; @@ -312,6 +286,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 2b00e4d..ba4db41 100644 --- a/src/decoders/cosmos/index.ts +++ b/src/decoders/cosmos/index.ts @@ -1,5 +1,7 @@ +export * from "./authz"; export * from "./bank"; export * from "./distribution"; +export * from "./feegrant"; export * from "./move"; export * from "./mstaking"; export * from "./wasm"; diff --git a/src/interfaces/cosmos.ts b/src/interfaces/cosmos.ts index a3c86aa..b28614b 100644 --- a/src/interfaces/cosmos.ts +++ b/src/interfaces/cosmos.ts @@ -49,6 +49,11 @@ export type WasmDecodedMessage = // Cosmos L1 messages (from src/decoders/cosmos/ root level and protocols) export type CosmosDecodedMessage = + | DecodedAuthzExecMessage + | DecodedAuthzGrantMessage + | DecodedAuthzRevokeMessage + | DecodedFeegrantGrantAllowanceMessage + | DecodedFeegrantRevokeAllowanceMessage | DecodedFinalizeTokenDepositMessage | DecodedFinalizeTokenWithdrawalMessage | DecodedIbcFtReceiveMessage @@ -65,6 +70,56 @@ export type DecodedMessage = | MoveDecodedMessage | WasmDecodedMessage; +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 3a3cebf..63014a0 100644 --- a/src/message-types.ts +++ b/src/message-types.ts @@ -25,6 +25,15 @@ export const SUPPORTED_MESSAGE_TYPES = { MsgIbcNftTransfer: "/ibc.applications.nft_transfer.v1.MsgTransfer", 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", + // wasm messages MsgExecuteContract: "/cosmwasm.wasm.v1.MsgExecuteContract", MsgInstantiateContract: "/cosmwasm.wasm.v1.MsgInstantiateContract" 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 fe04073..45bfb36 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.fixture.ts b/src/tests/cosmos/move/authz.fixture.ts new file mode 100644 index 0000000..6fb1646 --- /dev/null +++ b/src/tests/cosmos/move/authz.fixture.ts @@ -0,0 +1,1955 @@ +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":"1000000","frozen":false,"metadata":{"inner":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9"}}}', + raw_bytes: + "jkczvavPfUr8PRTw3UbJv1L7D86eS5lsk54ZW4vIkdlAQg8AAAAAAA==", + struct_tag: "0x1::fungible_asset::FungibleStore" + } + ] + }, + "/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: {} +}; + +// 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: {} +}; + +// 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: {} +}; + +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" +}; + +// 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 new file mode 100644 index 0000000..201f798 --- /dev/null +++ b/src/tests/cosmos/move/authz.test.ts @@ -0,0 +1,396 @@ +import { + initialize, + mockedAxios, + resetMockApi, + setupMockApi +} from "@/tests/_shared/helpers"; + +import { + mockApiResponsesForAuthzExec, + mockApiResponsesForDelegate, + mockApiResponsesForRealTx, + mockApiResponsesForRealTx2, + mockApiResponsesForUndelegate, + mockApiResponsesForWithdrawReward, + mockAuthzGrantTransaction, + mockAuthzRevokeTransaction, + mockMsgExecGranteeExecutesForGranter, + mockMsgExecWithDelegate, + mockMsgExecWithMultiple, + mockMsgExecWithUndelegate, + mockMsgExecWithWithdrawReward, + mockRealMsgExecTransaction, + mockRealMsgExecWithDifferentAddresses +} from "./authz.fixture"; + +jest.mock("axios"); + +const decoder = initialize(); + +describe("Authz Messages", () => { + beforeEach(() => { + resetMockApi(mockedAxios); + }); + + describe("MsgExec", () => { + 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 () => { + 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 () => { + 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 () => { + setupMockApi(mockedAxios, mockApiResponsesForAuthzExec); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgExecGranteeExecutesForGranter + ); + + 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( + "init1grantee" + ); + 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") { + expect(innerMessage.data.from).toBe("init1granter"); + expect(innerMessage.data.to).toBe("init1recipient"); + expect(innerMessage.data.coins).toEqual([ + { amount: "500", denom: "uinit" } + ]); + } + } + }); + }); + + describe("MsgGrant", () => { + it("should decode MsgGrant correctly", async () => { + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction( + mockAuthzGrantTransaction + ); + + 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 () => { + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction( + mockAuthzRevokeTransaction + ); + + 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.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 new file mode 100644 index 0000000..1cd1485 --- /dev/null +++ b/src/tests/cosmos/move/feegrant.test.ts @@ -0,0 +1,138 @@ +import { + initialize, + mockedAxios, + resetMockApi, + setupMockApi +} from "@/tests/_shared/helpers"; + +import { + mockMixedFeegrantAuthzTransaction, + mockMsgGrantAllowance, + mockMsgGrantAllowanceWithSpendLimit, + mockMsgRevokeAllowance +} from "./feegrant.fixture"; + +jest.mock("axios"); + +const decoder = initialize(); + +describe("Feegrant Messages", () => { + beforeEach(() => { + resetMockApi(mockedAxios); + }); + + describe("MsgGrantAllowance", () => { + it("should decode MsgGrantAllowance correctly", async () => { + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgGrantAllowance + ); + + 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 () => { + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgGrantAllowanceWithSpendLimit + ); + + 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 () => { + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction( + mockMsgRevokeAllowance + ); + + 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 () => { + setupMockApi(mockedAxios, {}); + + const decoded = await decoder.decodeCosmosTransaction( + mockMixedFeegrantAuthzTransaction + ); + + 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 + }); + }); + }); +}); diff --git a/src/tests/cosmos/move/nft/nft-burn.fixture.ts b/src/tests/cosmos/move/nft/nft-burn.fixture.ts index d146fc0..cdd9a5b 100644 --- a/src/tests/cosmos/move/nft/nft-burn.fixture.ts +++ b/src/tests/cosmos/move/nft/nft-burn.fixture.ts @@ -303,6 +303,11 @@ export const mockMsgNftBurn = { export const mockApiResponsesForNftBurn = { GET: { + "/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" },