diff --git a/src/constants/module-addresses.ts b/src/constants/module-addresses.ts index 68ae37c..ba3c893 100644 --- a/src/constants/module-addresses.ts +++ b/src/constants/module-addresses.ts @@ -19,6 +19,10 @@ export const CLAMM_MODULE_ADDRESSES = [ "0x6b41bf295bc31cd9bef75a9a5a67e5a8d6749b34a7ab3105808251fa2697823d" // testnet ] as const; +export const SKIP_ENTRY_POINT_MODULE_ADDRESSES = [ + "0xd921c61c7e7d2eead33bdff4c5c2941e66d488f0a15fe7fbc432565dc04b3710" // mainnet +] as const; + export const CLAMM_FARMING_MODULE_ADDRESSES = [ "0xcb2999c70a9b8db7cb473255bb01f956f0726087f08b04ece50844a6d8167351", // mainnet "0xf8ef0cb7c73607b7658524565015ce2aadc45ccf7164e5351959a4d7a1c37753" // testnet diff --git a/src/decoder-registry.ts b/src/decoder-registry.ts index ee59522..c2db54d 100644 --- a/src/decoder-registry.ts +++ b/src/decoder-registry.ts @@ -78,6 +78,7 @@ export const cosmosMoveMessageDecoders: MessageDecoder[] = [ Decoders.redelegateDecoder, Decoders.redelegateLockedDecoder, Decoders.sendDecoder, + Decoders.skipSwapDecoder, Decoders.stableswapDecoder, Decoders.undelegateDecoder, Decoders.undelegateLockedDecoder, diff --git a/src/decoders/cosmos/move/index.ts b/src/decoders/cosmos/move/index.ts index ac13bf9..8e8b9bd 100644 --- a/src/decoders/cosmos/move/index.ts +++ b/src/decoders/cosmos/move/index.ts @@ -4,6 +4,7 @@ export * from "./liquidity"; export * from "./minitswap"; export * from "./nft"; export * from "./object-transfer"; +export * from "./skip"; export * from "./stableswap"; export * from "./staking"; export * from "./usernames"; diff --git a/src/decoders/cosmos/move/skip.ts b/src/decoders/cosmos/move/skip.ts new file mode 100644 index 0000000..944d29b --- /dev/null +++ b/src/decoders/cosmos/move/skip.ts @@ -0,0 +1,70 @@ +import { ApiClient } from "@/api"; +import { DecodedMessage, MessageDecoder } from "@/interfaces"; +import { Log, Message, TxResponse, zMsgSkipSwapAndAction } from "@/schema"; +import { zSkipSwapEvent } from "@/schema"; +import { findAllMoveEvents } from "@/utils"; + +// swap_and_action routes through 0x1 AMMs, so amounts come from their swap +// events: offer of the first hop, return of the last hop. +const SWAP_EVENT_TAGS = [ + "0x1::dex::SwapEvent", + "0x1::minitswap::SwapEvent", + "0x1::stableswap::SwapEvent" +]; + +export const skipSwapDecoder: MessageDecoder = { + check: (message: Message, _log: Log) => + zMsgSkipSwapAndAction.safeParse(message).success, + decode: async ( + message: Message, + log: Log, + apiClient: ApiClient, + _txResponse: TxResponse + ) => { + const parsed = zMsgSkipSwapAndAction.parse(message); + const { sender } = parsed; + + const swapEvents = findAllMoveEvents( + log.events, + SWAP_EVENT_TAGS, + zSkipSwapEvent + ); + const firstSwapEvent = swapEvents[0]; + const lastSwapEvent = swapEvents[swapEvents.length - 1]; + if (!firstSwapEvent || !lastSwapEvent) { + throw new Error("Skip swap event not found"); + } + + const [denomIn, denomOut] = await Promise.all([ + apiClient.findDenomFromMetadataAddr(firstSwapEvent.offer_coin), + apiClient.findDenomFromMetadataAddr(lastSwapEvent.return_coin) + ]); + + if (!denomIn) { + throw new Error( + `Denom in not found for offer coin ${firstSwapEvent.offer_coin}` + ); + } + + if (!denomOut) { + throw new Error( + `Denom out not found for return coin ${lastSwapEvent.return_coin}` + ); + } + + const decodedMessage: DecodedMessage = { + action: "swap", + data: { + amountIn: firstSwapEvent.offer_amount, + amountOut: lastSwapEvent.return_amount, + denomIn, + denomOut, + from: sender + }, + isIbc: false, + isOp: false + }; + + return decodedMessage; + } +}; diff --git a/src/schema/cosmos/events.ts b/src/schema/cosmos/events.ts index cce482a..3628dea 100644 --- a/src/schema/cosmos/events.ts +++ b/src/schema/cosmos/events.ts @@ -13,6 +13,17 @@ export const zSwapEvent = zJsonString.pipe( }) ); +// Shared shape of 0x1::{dex,stableswap,minitswap}::SwapEvent — minitswap lacks +// fee_amount/liquidity_token, so only the common fields are required here. +export const zSkipSwapEvent = zJsonString.pipe( + z.object({ + offer_amount: z.string(), + offer_coin: z.string(), + return_amount: z.string(), + return_coin: z.string() + }) +); + export const zCreateEvent = zJsonString.pipe( z.object({ object: z.string(), diff --git a/src/schema/cosmos/move.ts b/src/schema/cosmos/move.ts index f445b3a..e80f59c 100644 --- a/src/schema/cosmos/move.ts +++ b/src/schema/cosmos/move.ts @@ -5,6 +5,7 @@ import { CLAMM_MODULE_ADDRESSES, DEX_UTILS_MODULE_ADDRESSES, INITIA_VAULT_MODULE_ADDRESSES, + SKIP_ENTRY_POINT_MODULE_ADDRESSES, USERNAME_MODULE_ADDRESSES } from "@/constants"; @@ -88,6 +89,13 @@ export const zMsgMoveStableSwap = zMsgMoveExecute.extend({ module_name: z.literal("stableswap") }); +// Skip entry_point related Move messages +export const zMsgSkipSwapAndAction = zMsgMoveExecute.extend({ + function_name: z.enum(["swap_and_action", "swap_and_action_with_recover"]), + module_address: z.enum(SKIP_ENTRY_POINT_MODULE_ADDRESSES), + module_name: z.literal("entry_point") +}); + // NFT related Move messages const zMsgMoveSimpleMint = zMsgMoveExecute.extend({ function_name: z.literal("mint"), diff --git a/src/tests/cosmos/move/skip/swap.fixture.ts b/src/tests/cosmos/move/skip/swap.fixture.ts new file mode 100644 index 0000000..025b8c8 --- /dev/null +++ b/src/tests/cosmos/move/skip/swap.fixture.ts @@ -0,0 +1,927 @@ +export const mockMsgSkipSwap = { + code: 0, + codespace: "", + data: "12240A222F696E697469612E6D6F76652E76312E4D736745786563757465526573706F6E7365", + events: [ + { + attributes: [ + { + index: true, + key: "sender", + value: "0x1,0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + 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":"0xa86658a064e83a161765a2ca6df6f63458e2d376656555855d52282af4597e9","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"13091"}' + }, + { + index: true, + key: "amount", + value: "13091" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xa86658a064e83a161765a2ca6df6f63458e2d376656555855d52282af4597e9" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawOwnerEvent" + }, + { + index: true, + key: "data", + value: '{"owner":"0xe85690a0c770c63a75655125b83018d6062a5f89"}' + }, + { + index: true, + key: "owner", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + } + ], + 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":"13091"}' + }, + { + index: true, + key: "amount", + value: "13091" + }, + { + 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: "init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3" + }, + { + index: true, + key: "amount", + value: "13091uinit" + } + ], + type: "coin_spent" + }, + { + attributes: [ + { + index: true, + key: "receiver", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { + index: true, + key: "amount", + value: "13091uinit" + } + ], + type: "coin_received" + }, + { + attributes: [ + { + index: true, + key: "recipient", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql" + }, + { + index: true, + key: "sender", + value: "init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3" + }, + { + index: true, + key: "amount", + value: "13091uinit" + } + ], + type: "transfer" + }, + { + attributes: [ + { + index: true, + key: "sender", + value: "init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "fee", + value: "13091uinit" + }, + { + index: true, + key: "fee_payer", + value: "init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3" + } + ], + type: "tx" + }, + { + attributes: [ + { + index: true, + key: "acc_seq", + value: "init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3/48" + } + ], + type: "tx" + }, + { + attributes: [ + { + index: true, + key: "signature", + value: + "Rj5H0R2GSD/6SauUHSyMYdjB9Jg6kb6mz3VW9ZcilAM1o5PiMZiF6NjV/V3ImQ+6MGNpxyJYrLsBFGmKMzQ4wQ==" + } + ], + type: "tx" + }, + { + attributes: [ + { + index: true, + key: "action", + value: "/initia.move.v1.MsgExecute" + }, + { + index: true, + key: "sender", + value: "init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3" + }, + { + index: true, + key: "module", + value: "move" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "message" + }, + { + attributes: [ + { + index: true, + key: "sender", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + index: true, + key: "module_addr", + value: + "0xd921c61c7e7d2eead33bdff4c5c2941e66d488f0a15fe7fbc432565dc04b3710" + }, + { + index: true, + key: "module_name", + value: "entry_point" + }, + { + index: true, + key: "function_name", + value: "swap_and_action_with_recover" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "execute" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: + "0xd921c61c7e7d2eead33bdff4c5c2941e66d488f0a15fe7fbc432565dc04b3710::ack_callback::StoreRecoverAddress" + }, + { + index: true, + key: "data", + value: + '{"callback_id":"8","recover_address":"0xe85690a0c770c63a75655125b83018d6062a5f89","coin_metadata":"0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55"}' + }, + { + index: true, + key: "callback_id", + value: "8" + }, + { + index: true, + key: "coin_metadata", + value: + "0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55" + }, + { + index: true, + key: "recover_address", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "sender", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + index: true, + key: "module_addr", + value: + "0xd921c61c7e7d2eead33bdff4c5c2941e66d488f0a15fe7fbc432565dc04b3710" + }, + { + index: true, + key: "module_name", + value: "entry_point" + }, + { + index: true, + key: "function_name", + value: "swap_and_action" + }, + { + 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":"0xcb75e0437cda5031d908b5ab4c8a0423c4e4db51ed411dab4eb88367243b7811","metadata_addr":"0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55","amount":"500000000"}' + }, + { + index: true, + key: "amount", + value: "500000000" + }, + { + index: true, + key: "metadata_addr", + value: + "0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55" + }, + { + index: true, + key: "store_addr", + value: + "0xcb75e0437cda5031d908b5ab4c8a0423c4e4db51ed411dab4eb88367243b7811" + }, + { + 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":"0xe85690a0c770c63a75655125b83018d6062a5f89"}' + }, + { + index: true, + key: "owner", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + 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":"0x39e454a8b7b1b71b2c0c27bf4ce1fbe234652f2c40f1fc209b023beaa7ece013","metadata_addr":"0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55","amount":"500000000"}' + }, + { + index: true, + key: "amount", + value: "500000000" + }, + { + index: true, + key: "metadata_addr", + value: + "0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55" + }, + { + index: true, + key: "store_addr", + value: + "0x39e454a8b7b1b71b2c0c27bf4ce1fbe234652f2c40f1fc209b023beaa7ece013" + }, + { + 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":"0x543b35a39cfadad3da3c23249c474455d15efd2f94f849473226dee8a3c7a9e1"}' + }, + { + index: true, + key: "owner", + value: + "0x543b35a39cfadad3da3c23249c474455d15efd2f94f849473226dee8a3c7a9e1" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawEvent" + }, + { + index: true, + key: "data", + value: + '{"store_addr":"0xd8d20f9cd225d52f126bd3f973cbb83de3212b1b3dae547f13dd01f593d537b3","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"10185848708"}' + }, + { + index: true, + key: "amount", + value: "10185848708" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xd8d20f9cd225d52f126bd3f973cbb83de3212b1b3dae547f13dd01f593d537b3" + }, + { + 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":"0x543b35a39cfadad3da3c23249c474455d15efd2f94f849473226dee8a3c7a9e1"}' + }, + { + index: true, + key: "owner", + value: + "0x543b35a39cfadad3da3c23249c474455d15efd2f94f849473226dee8a3c7a9e1" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::dex::SwapEvent" + }, + { + index: true, + key: "data", + value: + '{"offer_coin":"0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55","return_coin":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","liquidity_token":"0x543b35a39cfadad3da3c23249c474455d15efd2f94f849473226dee8a3c7a9e1","offer_amount":"500000000","return_amount":"10185848708","fee_amount":"1500000"}' + }, + { + index: true, + key: "fee_amount", + value: "1500000" + }, + { + index: true, + key: "liquidity_token", + value: + "0x543b35a39cfadad3da3c23249c474455d15efd2f94f849473226dee8a3c7a9e1" + }, + { + index: true, + key: "offer_amount", + value: "500000000" + }, + { + index: true, + key: "offer_coin", + value: + "0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55" + }, + { + index: true, + key: "return_amount", + value: "10185848708" + }, + { + index: true, + key: "return_coin", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + 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":"0xa86658a064e83a161765a2ca6df6f63458e2d376656555855d52282af4597e9","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"10185848708"}' + }, + { + index: true, + key: "amount", + value: "10185848708" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xa86658a064e83a161765a2ca6df6f63458e2d376656555855d52282af4597e9" + }, + { + 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":"0xe85690a0c770c63a75655125b83018d6062a5f89"}' + }, + { + index: true, + key: "owner", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "type_tag", + value: "0x1::fungible_asset::WithdrawEvent" + }, + { + index: true, + key: "data", + value: + '{"store_addr":"0xa86658a064e83a161765a2ca6df6f63458e2d376656555855d52282af4597e9","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"10185848708"}' + }, + { + index: true, + key: "amount", + value: "10185848708" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xa86658a064e83a161765a2ca6df6f63458e2d376656555855d52282af4597e9" + }, + { + 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":"0xe85690a0c770c63a75655125b83018d6062a5f89"}' + }, + { + index: true, + key: "owner", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + 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":"0xa86658a064e83a161765a2ca6df6f63458e2d376656555855d52282af4597e9","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"10185848708"}' + }, + { + index: true, + key: "amount", + value: "10185848708" + }, + { + index: true, + key: "metadata_addr", + value: + "0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9" + }, + { + index: true, + key: "store_addr", + value: + "0xa86658a064e83a161765a2ca6df6f63458e2d376656555855d52282af4597e9" + }, + { + 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":"0xe85690a0c770c63a75655125b83018d6062a5f89"}' + }, + { + index: true, + key: "owner", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "move" + }, + { + attributes: [ + { + index: true, + key: "success", + value: "true" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "submsg" + }, + { + attributes: [ + { + index: true, + key: "sender", + value: "0xe85690a0c770c63a75655125b83018d6062a5f89" + }, + { + index: true, + key: "module_addr", + value: + "0xd921c61c7e7d2eead33bdff4c5c2941e66d488f0a15fe7fbc432565dc04b3710" + }, + { + index: true, + key: "module_name", + value: "ack_callback" + }, + { + index: true, + key: "function_name", + value: "recover" + }, + { + index: true, + key: "msg_index", + value: "0" + } + ], + type: "execute" + } + ], + gas_used: "602187", + gas_wanted: "872704", + height: "20109560", + info: "", + logs: [], + raw_log: "", + timestamp: "2026-07-31T03:47:43Z", + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [ + { + amount: "13091", + denom: "uinit" + } + ], + gas_limit: "872704", + granter: "", + payer: "" + }, + signer_infos: [ + { + mode_info: { + single: { + mode: "SIGN_MODE_EIP_191" + } + }, + public_key: { + "@type": "/initia.crypto.v1beta1.ethsecp256k1.PubKey", + key: "A88UL3Q7HkDxPcqV5HjxSIJq+dk48e8ooenW9CT/X/40" + }, + sequence: "48" + } + ], + tip: null + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/initia.move.v1.MsgExecute", + args: [ + "AQA=", + "AA==", + "AGXNHQAAAAA=", + "DEuEXgIAAAA=", + "AQFFbW92ZS81NDNiMzVhMzljZmFkYWQzZGEzYzIzMjQ5YzQ3NDQ1NWQxNWVmZDJmOTRmODQ5NDczMjI2ZGVlOGEzYzdhOWUx", + "AQJEaWJjLzY0OTBBN0VBQjYxMDU5QkZDMUNEREVCMDU5MTdERDcwQkRGM0E2MTE2NTQxNjJBMUE0N0RCOTMwRDQwRDhBRjQFdWluaXQ=", + "AA==", + "6qjgiXdDxxg=", + "K2luaXQxYXB0ZnBneDh3cnJyNWF0OTJ5am1zdnFjNmNyejVodWY5Z3h0ZDM=", + "ASwraW5pdDFhcHRmcGd4OHdycnI1YXQ5MnlqbXN2cWM2Y3J6NWh1ZjlneHRkMw==" + ], + function_name: "swap_and_action_with_recover", + module_address: + "0xd921c61c7e7d2eead33bdff4c5c2941e66d488f0a15fe7fbc432565dc04b3710", + module_name: "entry_point", + sender: "init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3", + type_args: [] + } + ], + non_critical_extension_options: [], + timeout_height: "0" + }, + signatures: [ + "Rj5H0R2GSD/6SauUHSyMYdjB9Jg6kb6mz3VW9ZcilAM1o5PiMZiF6NjV/V3ImQ+6MGNpxyJYrLsBFGmKMzQ4wQ==" + ] + }, + txhash: "F54F0CBAEFB5509845C482C3D7AF01B2CAEB8AA15B36201A00631405F2DA920D" +}; + +export const mockApiResponsesForSkipSwap = { + GET: { + "/initia/move/v1/denom?metadata=0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9": + { + denom: "uinit" + }, + "/initia/move/v1/denom?metadata=0xe0e9394b24e53775d6af87934ac02d73536ad58b7894f6ccff3f5e7c0d548e55": + { + denom: + "ibc/6490A7EAB61059BFC1CDDEB05917DD70BDF3A611654162A1A47DB930D40D8AF4" + } + }, + POST: {} +}; diff --git a/src/tests/cosmos/move/skip/swap.test.ts b/src/tests/cosmos/move/skip/swap.test.ts new file mode 100644 index 0000000..4cbc679 --- /dev/null +++ b/src/tests/cosmos/move/skip/swap.test.ts @@ -0,0 +1,57 @@ +import { + initialize, + mockedAxios, + resetMockApi, + setupMockApi +} from "@/tests/_shared/helpers"; + +import { mockApiResponsesForSkipSwap, mockMsgSkipSwap } from "./swap.fixture"; + +jest.mock("axios"); +const decoder = initialize(); + +const USDC = + "ibc/6490A7EAB61059BFC1CDDEB05917DD70BDF3A611654162A1A47DB930D40D8AF4"; + +describe("Skip Swap and Action Message", () => { + beforeEach(() => { + resetMockApi(mockedAxios); + }); + + it("should decode a skip swap_and_action_with_recover move message correctly", async () => { + setupMockApi(mockedAxios, mockApiResponsesForSkipSwap); + + const decoded = await decoder.decodeCosmosTransaction(mockMsgSkipSwap); + + expect(decoded.messages).toHaveLength(1); + expect(decoded.messages[0].decodedMessage).toEqual({ + action: "swap", + data: { + amountIn: "500000000", + amountOut: "10185848708", + denomIn: USDC, + denomOut: "uinit", + from: "init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3" + }, + isIbc: false, + isOp: false + }); + + const expectedBalanceChanges = { + ft: { + init1aptfpgx8wrrr5at92yjmsvqc6crz5huf9gxtd3: { + uinit: "10185848708", + [USDC]: "-500000000" + }, + init12santguultdd8k3uyvjfc36y2hg4alf0jnuyj3ejym0w3g7848ssynvde3: { + uinit: "-10185848708", + [USDC]: "500000000" + } + }, + object: {}, + vm: "move" + }; + expect(decoded.messages[0].balanceChanges).toEqual(expectedBalanceChanges); + expect(decoded.totalBalanceChanges).toEqual(expectedBalanceChanges); + }); +}); diff --git a/src/utils/find-move-events.ts b/src/utils/find-move-events.ts index 0a76451..827a67e 100644 --- a/src/utils/find-move-events.ts +++ b/src/utils/find-move-events.ts @@ -33,14 +33,15 @@ export const findMoveEvent = >( export const findAllMoveEvents = >( events: Event[], - typeTag: string, + typeTag: string | string[], schema: S ): z.infer[] => { + const typeTags = Array.isArray(typeTag) ? typeTag : [typeTag]; const matchingEvents = events.filter( (event) => event.type === "move" && event.attributes.some( - (attr) => attr.key === "type_tag" && attr.value === typeTag + (attr) => attr.key === "type_tag" && typeTags.includes(attr.value) ) );