Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement IBC Callbacks Adapter #160

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open

Conversation

NotJeremyLiu
Copy link
Member

@NotJeremyLiu NotJeremyLiu commented Mar 3, 2025

This PR

  1. Creates a Skip2 package which provides types and methods using Cosmwasm 2
  2. Adds an IBC Callbacks adapter that can receive IBC destination and source callbacks, and dispatch IBC transfers with multiple encodings
  3. Adds logic in the entrypoint to distribute and emit events when handling Eureka fees

Highlights on interfaces / interactions

Events emitted on eureka fee:

        response = response
            .add_message(eureka_fee_bank_send)
            .add_attribute("action", "dispatch_eureka_fee_bank_send")
            .add_attribute("eureka_fee_denom", eureka_fee.coin.denom.clone())
            .add_attribute("eureka_fee_amount", eureka_fee.coin.amount)
            .add_attribute("eureka_fee_receiver", eureka_fee.receiver.clone());

Adds an optional encoding and EurekaFee to the IbcInfo:

#[cw_serde]
#[derive(Default)]
pub struct EurekaFee {
    pub coin: Coin,
    pub receiver: String,
    pub timeout_timestamp: u64,
}

// The IbcInfo struct defines the information for an IBC transfer standardized across all IBC Transfer Adapter contracts.
#[cw_serde]
pub struct IbcInfo {
    pub source_channel: String,
    pub receiver: String,
    pub fee: Option<IbcFee>, // Only used in Neutron
    pub memo: String,
    pub recover_address: String,
    pub encoding: Option<String>,
    pub eureka_fee: Option<EurekaFee>,
}

Note

  1. Deprecated fields are allowed in the ibc callbacks adapter so that it is portable to both cw 1 and cw 2 chains

TODOs

  1. Add unit tests (can be in a follow-up PR)

@NotJeremyLiu NotJeremyLiu changed the title IBC Callbacks Adapter Implement IBC Callbacks Adapter Mar 3, 2025
Copy link
Contributor

@dhfang dhfang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really good work, thank you sir 🙏

let resp: MsgTransferResponse = MsgTransferResponse::decode(
sub_msg_response
.data
.ok_or(ContractError::MissingResponseData)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what conditions could this happen in? should we clear IN_PROGRESS_RECOVER_ADDRESS and IN_PROGRESS_CHANNEL_ID?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a chain failure if there's no data in the sub_msg_response. The contract is expecting this data field to always be here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MsgTransferResponse data is just the sequence number back from the transfer ibc moduke, so no data here = no sequence given back which is a transfer module critical failure

let recv_denom = get_recv_denom(msg.packet, packet_data.denom.clone());

// Decode the memo to get the contract address and message to execute
let (contract_addr, msg) = get_contract_addr_and_msg_from_ibc_hooks_memo(packet_data.memo)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So iiuc, on packet receipt the callback handler on the entrypoint contract gets invoked, which then executes the wasm hooks payload at the target address, which in our case will also be the entrypoint contract?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this callbacks adapter is the receiver of the destination callback (which is this adapter contract).

In the wasm field in the memo the contract addr is the entrypoint addr (which is already what we do in msg building), so this destination callback logic on the callbacks adapter will decode + call the entry point contract with the payload + tokens.

Then from the entrypoint contract it all looks normal

ACK_ID_TO_RECOVER_ADDRESS.remove(deps.storage, ack_id);

// Get all coins from contract's balance, which will be the
// failed ibc transfer coin and any leftover dust on the contract
Copy link
Contributor

@dhfang dhfang Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if a previous user's funds are on the contract due to a failure in the source callback to refund, this refund logic seems like it gives another user an avenue to extract those funds. the source callback is not that complex right now that i'm super worried about this, but I could see us making it more complex in the future. or some other developer extends the contract in a way that funds are stored on the contract without full context here.

this is why i dont really like that the application can't revert the timeout or acknowledgement transaction. it makes it harder to reason about the state transitions of the application. perhaps make sense to store the amount and denom along with the other recovery information?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah so rn this logic is mirror'd across all of our ibc adapters right now. So this is feature parity with the way we handle other timeouts/errors

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember now why we did it this way, it was because on neutron when there's ibc fees as well we refund those so it's multiple denoms so we wanted to send back multiple denoms

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed here: a2e7202

);

// Require that the packet was successfully received
// TODO: This fails due to the msg.ack.data having extra bytes then just the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh are you trying to address this pre audit?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

potentially, i need to figure out this extra bytes issue, so if I can resolve today then down to add this logic in today for the audit

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed here: 19c94ba

Discussed with IBC team that for v2 it gets proto marshaled, leading to the extra bytes. So keeping the string comparison as the check for now

amount: Uint128::from_str(&packet_data.amount)?,
};

// @NotJeremyLiu TODO: Turn this into a sub msg and figure out what the recovery case is here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you trying to address this pre audit?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah so rn the logic is that if the underlying call fails, the tx will fail.

So we only want to impl this sub_msg logic if we want a failure to do something else (like refund to a given address on the chain). But we have a case where if we can't pull a recovery addr from the memo some failure case anyways.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants