Skip to content

Commit

Permalink
add hooks to call bridge escrow program
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruvja committed Oct 24, 2024
1 parent ba1046e commit 88aa357
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 9 deletions.
9 changes: 7 additions & 2 deletions solana/solana-ibc/programs/solana-ibc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ pub const WSOL_ADDRESS: &str = "So11111111111111111111111111111111111111112";
pub const MINIMUM_FEE_ACCOUNT_BALANCE: u64 =
solana_program::native_token::LAMPORTS_PER_SOL;

pub const BRIDGE_ESCROW_PROGRAM_ID: &str =
"AhfoGVmS19tvkEG2hBuZJ1D6qYEjyFmXZ1qPoFD6H4Mj";
pub const HOOK_TOKEN_ADDRESS: &str =
"0x36dd1bfe89d409f869fabbe72c3cf72ea8b460f6";

declare_id!("2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT");

#[cfg(not(feature = "mocks"))]
Expand Down Expand Up @@ -472,8 +477,8 @@ pub mod solana_ibc {
/// doesnt exists.
///
/// Would panic if it doesnt match the one that is in the packet
pub fn send_transfer(
ctx: Context<SendTransfer>,
pub fn send_transfer<'a, 'info>(
ctx: Context<'a, 'a, 'a, 'info, SendTransfer<'info>>,
hashed_full_denom: CryptoHash,
msg: ibc::MsgTransfer,
) -> Result<()> {
Expand Down
5 changes: 5 additions & 0 deletions solana/solana-ibc/programs/solana-ibc/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@ pub struct TransferAccounts<'a> {
pub mint_authority: Option<AccountInfo<'a>>,
pub token_program: Option<AccountInfo<'a>>,
pub fee_collector: Option<AccountInfo<'a>>,
/// Contains the list of accounts required for the hooks
/// if present
pub remaining_accounts: Vec<AccountInfo<'a>>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -544,6 +547,7 @@ macro_rules! from_ctx {
};
($ctx:expr, with accounts) => {{
let accounts = &$ctx.accounts;
let remaining_accounts = &$ctx.remaining_accounts;
let accounts = TransferAccounts {
sender: Some(accounts.sender.as_ref().to_account_info()),
receiver: accounts
Expand Down Expand Up @@ -574,6 +578,7 @@ macro_rules! from_ctx {
.fee_collector
.as_deref()
.map(ToAccountInfo::to_account_info),
remaining_accounts: remaining_accounts.to_vec()
};
$crate::storage::from_ctx!($ctx, accounts = accounts)
}};
Expand Down
119 changes: 112 additions & 7 deletions solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::result::Result;
use std::str;
use std::str::{self, FromStr};

use anchor_lang::prelude::*;
use serde::{Deserialize, Serialize};
use spl_token::solana_program::instruction::Instruction;
use spl_token::solana_program::program::invoke;

use crate::ibc;
use crate::ibc::apps::transfer::types::packet::PacketData;
use crate::ibc::apps::transfer::types::proto::transfer::v2::FungibleTokenPacketData;
use crate::storage::IbcStorage;
use crate::{ibc, BRIDGE_ESCROW_PROGRAM_ID, HOOK_TOKEN_ADDRESS};

pub(crate) mod impls;

Expand Down Expand Up @@ -142,12 +144,115 @@ impl ibc::Module for IbcStorage<'_, '_> {
.into_bytes(),
..packet.clone()
};
let (extras, ack) = ibc::apps::transfer::module::on_recv_packet_execute(
self,
&maybe_ft_packet,
);
let ack_status = str::from_utf8(ack.as_bytes())
let (extras, mut ack) =
ibc::apps::transfer::module::on_recv_packet_execute(
self,
&maybe_ft_packet,
);
let cloned_ack = ack.clone();
let ack_status = str::from_utf8(cloned_ack.as_bytes())
.expect("Invalid acknowledgement string");
let status = serde_json::from_slice::<ibc::AcknowledgementStatus>(
ack.as_bytes(),
);
let success = if let Ok(status) = status {
status.is_successful()
} else {
ack = ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::AckDeserialization.into(),
)
.into();
false
};
fn call_bridge_escrow(
accounts: &[AccountInfo],
data: Vec<u8>,
) -> Result<(), ibc::AcknowledgementStatus> {
// Perform hooks
let data = match serde_json::from_slice::<PacketData>(&data) {
Ok(data) => data,
Err(_) => {
return Err(ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::PacketDataDeserialization
.into(),
)
.into());

Check failure on line 179 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus`

error: useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus` --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:175:32 | 175 | return Err(ibc::AcknowledgementStatus::error( | ________________________________^ 176 | | ibc::TokenTransferError::PacketDataDeserialization 177 | | .into(), 178 | | ) 179 | | .into()); | |___________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `-D clippy::useless-conversion` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_conversion)]` help: consider removing `.into()` | 175 ~ return Err(ibc::AcknowledgementStatus::error( 176 + ibc::TokenTransferError::PacketDataDeserialization 177 + .into(), 178 ~ )); |
}
};
// The hook would only be called if the transferred token is the one we are interested in
if data.token.denom.base_denom.as_str() == HOOK_TOKEN_ADDRESS {
// The memo is a string and the structure is as follow:
// "<number of accounts>,<AccountKey1> ..... <AccountKeyN>,<intent_id>,<memo>"
//
// The relayer would parse the memo and pass the relevant accounts
// The intent_id and memo needs to be stripped
let memo = data.memo.as_ref();
let (accounts_size, rest) = memo.split_once(",").ok_or(

Check failure on line 190 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

single-character string constant used as pattern

error: single-character string constant used as pattern --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:190:61 | 190 | let (accounts_size, rest) = memo.split_once(",").ok_or( | ^^^ help: consider using a `char`: `','` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern = note: `-D clippy::single-char-pattern` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::single_char_pattern)]`
ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::Other(
"Invalid memo".to_string(),
)
.into(),
)
.into(),

Check failure on line 197 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus`

error: useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus` --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:191:21 | 191 | / ibc::AcknowledgementStatus::error( 192 | | ibc::TokenTransferError::Other( 193 | | "Invalid memo".to_string(), 194 | | ) 195 | | .into(), 196 | | ) 197 | | .into(), | |___________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion help: consider removing `.into()` | 191 ~ ibc::AcknowledgementStatus::error( 192 + ibc::TokenTransferError::Other( 193 + "Invalid memo".to_string(), 194 + ) 195 + .into(), 196 ~ ), |
)?;
// This is the 8 byte discriminant since the program is written in
// anchor. it is hash of "<namespace>:<function_name>" which is
// "global:on_receive_transfer" respectively.
let instruction_discriminant: Vec<u8> =
vec![149, 112, 68, 208, 4, 206, 248, 125];
let values = rest.split(",").collect::<Vec<&str>>();

Check failure on line 204 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

single-character string constant used as pattern

error: single-character string constant used as pattern --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:204:41 | 204 | let values = rest.split(",").collect::<Vec<&str>>(); | ^^^ help: consider using a `char`: `','` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
let (_passed_accounts, ix_data) =
values.split_at(accounts_size.parse::<usize>().unwrap());
let intent_id = ix_data.get(0).ok_or(

Check failure on line 207 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

accessing first element with `ix_data.get(0)`

error: accessing first element with `ix_data.get(0)` --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:207:33 | 207 | let intent_id = ix_data.get(0).ok_or( | ^^^^^^^^^^^^^^ help: try: `ix_data.first()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#get_first = note: `-D clippy::get-first` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::get_first)]`
ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::Other(
"Invalid memo".to_string(),
)
.into(),
)
.into(),

Check failure on line 214 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus`

error: useless conversion to the same type: `ibc::core::channel::types::acknowledgement::AcknowledgementStatus` --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:208:21 | 208 | / ibc::AcknowledgementStatus::error( 209 | | ibc::TokenTransferError::Other( 210 | | "Invalid memo".to_string(), 211 | | ) 212 | | .into(), 213 | | ) 214 | | .into(), | |___________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion help: consider removing `.into()` | 208 ~ ibc::AcknowledgementStatus::error( 209 + ibc::TokenTransferError::Other( 210 + "Invalid memo".to_string(), 211 + ) 212 + .into(), 213 ~ ), |
)?;
let memo = ix_data[1..].join(",");
let mut instruction_data = instruction_discriminant;
instruction_data.extend_from_slice(&intent_id.as_bytes());

Check failure on line 218 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

this expression creates a reference which is immediately dereferenced by the compiler

error: this expression creates a reference which is immediately dereferenced by the compiler --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:218:52 | 218 | instruction_data.extend_from_slice(&intent_id.as_bytes()); | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `intent_id.as_bytes()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `-D clippy::needless-borrow` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]`
instruction_data.extend_from_slice(memo.as_bytes());

let bridge_escrow_program_id =
Pubkey::from_str(BRIDGE_ESCROW_PROGRAM_ID).unwrap();

let account_metas = accounts
.iter()
.map(|account| AccountMeta {
pubkey: *account.key,
is_signer: account.is_signer,
is_writable: account.is_writable,
})
.collect::<Vec<AccountMeta>>();
let instruction = Instruction::new_with_bytes(
bridge_escrow_program_id,
&instruction_data,
account_metas,
);

invoke(&instruction, accounts).map_err(|err| {
return ibc::AcknowledgementStatus::error(
ibc::TokenTransferError::Other(err.to_string()).into(),
);

Check failure on line 241 in solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded `return` statement

error: unneeded `return` statement --> solana/solana-ibc/programs/solana-ibc/src/transfer/mod.rs:239:21 | 239 | / return ibc::AcknowledgementStatus::error( 240 | | ibc::TokenTransferError::Other(err.to_string()).into(), 241 | | ); | |_____________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return = note: `-D clippy::needless-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_return)]` help: remove `return` | 239 ~ ibc::AcknowledgementStatus::error( 240 + ibc::TokenTransferError::Other(err.to_string()).into(), 241 ~ ) |
})?;
msg!("Hook: Bridge escrow call successful");
}
Ok(())
}

if success {
let store = self.borrow();
let accounts = &store.accounts.remaining_accounts;
let result = call_bridge_escrow(accounts, maybe_ft_packet.data);
if let Err(status) = result {
ack = status.into();
}
}
msg!("ibc::Packet acknowledgement: {}", ack_status);
(extras, ack)
}
Expand Down

0 comments on commit 88aa357

Please sign in to comment.