Multicall contract is designed for performing multiple actions in a matter of one transaction. For example sending tokens to multiple addresses or staking some funds in exchange for another and then sending it to some other cosmos chain via ibc message.
Executes a set of cosmos messages specified in the calls array
{
"multicall": {
"calls": [
{
"msg": {},
"actions": []
}
],
"fallback_address": "<local_fallback_address>",
}
}
Note: theres another execute msg type process_next_call
- it can only be called by the contract itself, otherwise transaction will always be reverted.
Multicall action accepts an array of Call
objects. Each Call
object has two fields - msg
and actions
.
-
msg
field is a valid JSON object of typeCosmosMsg
, so providing JSON that won't serialize intoCosmosMsg
will cause a serialization error and will revert transaction execution process. -
actions
array is a set of instructions that can be performed before serializingmsg
intoCosmosMsg
type and sending it to the node.
Queries bank module contract's balance and replaces received value in the message.
{
"native_balance_fetch": {
"denom": "usquid",
"replacer": "/path/to/field/for/replacement"
}
}
Queries cw20 token contract's balance and replaces received value in the message
{
"cw20_balance_fetch": {
"contract": "squid1...",
"replacer": "/path/to/field/for/replacement"
}
}
Makes a custom query and replaces msg values using data from the query response.
Both [CallAction::NativeBalanceFetch
] & [CallAction::Cw20BalanceFetch
] can be done via this call action type.
query_msg
field must be a valid json message of type [cosmwasm_std::QueryRequest
]
{
"custom_replace_query": {
"query_msg": {},
"replacers": [
{
"response_pointer": "/path/to/query/response/field",
"replacer": "/path/to/field/for/replacement"
}
]
}
}
Enables ibc tracking for sent ibc transfer messages from the multicall contract. Can be passed when sending ibc transfer message. Note: either 'amount' or 'pointer' field must be set, otherwise validation error will be returned.
{
"ibc_tracking": {
"channel": "channel-0",
"denom": "usquid",
"amount": "1" || null,
"amount_pointer": "/path/to/amount/field" || null
}
}
Converts specified field into [Binary
] type
{
"field_to_binary": {
"replacer": "/path/to/field/for/replacement"
}
}
Converts specified field into [Binary
] type encoded using [prost::Message::encode
] method.
Note: since the type of the message should be known to the contract at the moment only ibc transfer and osmosis swap exact amount in is supported.
{
"field_to_proto_binary": {
"replacer": "/path/to/field/for/replacement",
"proto_msg_type": "ibc_transfer" | "osmosis_swap_exact_amt_in"
}
}
Replacer is basically a path from the root of the msg
object to a field. For example here is the valid multicall message for doing a bank send action:
{
"multicall": {
"calls": [
{
"msg": {
"bank": {
"send": {
"to_address": "osmo19he7u694v4ekp6gm489e7skyz7lwdzmsjvduvq",
"amount": [
{
"denom": "uosmo",
"amount": "0"
}
]
}
}
},
"actions": [
{
"native_balance_fetch": {
"denom": "uosmo",
"replacer": "/bank/send/amount/0/amount"
}
}
]
}
]
}
}
replacer
value is equals to a path to amount
field inside bank message. This field will be replaced with fetched contract balance of uosmo
coin. Numbers in the path are equal to an index in the array.
Fallback address is a field that must be set for:
- ibc error recovery when
ibc_tracking
action is enabled - local funds recovery in case of contract execution failure or any funds left on the contract balance after successful execution, e.g. multicall contract was trying to perform a swap and failed because of price change - so instead of forwarding dex error multicall contract will recover all owned funds to the specified fallback address. Note: if fallback address is not set or there is no funds to recover - the contract will forward an error.
{
"multicall": {
"calls": [
{
"msg": {
"stargate": {
"type_url": "/ibc.applications.transfer.v1.MsgTransfer",
"value": {
"source_port": "transfer",
"source_channel": "channel-3",
"token": {
"denom": "ibc/6F34E1BD664C36CE49ACC28E60D62559A5F96C4F9A6CCE4FC5A67B2852E24CFE",
"amount": "0"
},
"sender": "osmo1vmpds4p8grwz54dygeljhq9vffssw5caydyj3heqd02f2seckk3smlug7w",
"receiver": "axelar15t9awn6jnxheckur5vc6dqv6pqlpph0hw24vwf",
"timeout_timestamp": 1693856646000000000,
"memo": "{\"ibc_callback\":\"osmo1vmpds4p8grwz54dygeljhq9vffssw5caydyj3heqd02f2seckk3smlug7w\"}"
}
}
},
"actions": [
{
"native_balance_fetch": {
"denom": "ibc/6F34E1BD664C36CE49ACC28E60D62559A5F96C4F9A6CCE4FC5A67B2852E24CFE",
"replacer": "/stargate/value/token/amount"
}
},
{
"ibc_tracking": {
"channel": "channel-3",
"denom": "ibc/6F34E1BD664C36CE49ACC28E60D62559A5F96C4F9A6CCE4FC5A67B2852E24CFE",
"amount_pointer" "/stargate/value/token/amount"
}
},
{
"field_to_proto_binary": {
"replacer": "/stargate/value",
"proto_msg_type": "ibc_transfer"
}
}
]
}
],
"fallback_address": "osmo1eaztm3pqrkw2xgt0lxppahtx5v5pndmjg6yfrh"
}
}
First the contract will fetch the balance of axlUsdc on osmosis and update the amount field in the message.
Second it will enable ibc tracking mechanism for this call.
IMPORTANT NOTE: ibc_callback
field must be set by the sender, otherwise tracking won't work since the destination chain will not send IBC ACK back to the source chain.
Third - the contract will serialize whole message from the value
field into Binary format and send it.
{
"multicall": {
"calls": [
{
"msg": {
"custom": {
"register_interchain_account": {
"connection_id": "connection-8",
"interchain_account_id": "squid0001"
}
}
},
"actions": []
}
]
}
}
Actions array can also be empty if there is no actions required. In this case the contract will simply proxy provided message.
{
"multicall": {
"calls": [
{
"msg": {
"wasm": {
"execute": {
"contract_addr": "osmo1",
"msg": {
"deposit": {
"on_behalf": "osmo1"
}
},
"funds": [
{
"denom": "uosmo",
"amount": "0"
}
]
}
}
},
"actions": [
{
"native_balance_fetch": {
"denom": "uosmo",
"replacer": "/wasm/execute/funds/0/amount"
}
},
{
"field_to_binary": {
"replacer": "/wasm/execute/msg"
}
}
]
}
]
}
}
First the contract will fetch uosmo
balance and update the funds information for a wasm call.
Second - since msg
field in a wasm call must be a base64 encoded Binary object, but not a JSON object we need to use field_to_binary
action. It will take msg
field at the specified path and convert it into Binary. Resulting message that will be sent will look like this:
{
"wasm": {
"execute": {
"contract_addr": "osmo1",
"msg": "eyJkZXBvc2l0Ijp7Im9uX2JlaGFsZiI6Im9zbW8xIn19",
"funds": [
{
"denom": "uosmo",
"amount": "111111"
}
]
}
}
}
Note: since there is no updates for wasm message field it can be alredy specified as a binary value. Here it is shown for explanaition purposes.