Skip to content

Commit

Permalink
feat: boost support for vault swaps
Browse files Browse the repository at this point in the history
  • Loading branch information
msgmaxim committed Nov 21, 2024
1 parent 8e1619a commit 69a23d6
Show file tree
Hide file tree
Showing 13 changed files with 1,053 additions and 544 deletions.
47 changes: 28 additions & 19 deletions engine/src/witness/arb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ use cf_chains::{
Arbitrum, CcmDepositMetadata,
};
use cf_primitives::{
chains::assets::arb::Asset as ArbAsset, Asset, AssetAmount, Beneficiary, EpochIndex,
chains::assets::arb::Asset as ArbAsset, Asset, AssetAmount, Beneficiary, ChannelId, EpochIndex,
};
use cf_utilities::task_scope::Scope;
use futures_core::Future;
use itertools::Itertools;
use pallet_cf_ingress_egress::VaultDepositWitness;
use sp_core::H160;

use crate::{
Expand Down Expand Up @@ -185,7 +186,10 @@ impl super::evm::vault::IngressCallBuilder for ArbCallBuilder {
type Chain = Arbitrum;

fn vault_swap_request(
block_height: u64,
source_asset: Asset,
deposit_address: cf_chains::eth::Address,
channel_id: ChannelId,
deposit_amount: AssetAmount,
destination_asset: Asset,
destination_address: EncodedAddress,
Expand All @@ -195,24 +199,29 @@ impl super::evm::vault::IngressCallBuilder for ArbCallBuilder {
) -> state_chain_runtime::RuntimeCall {
state_chain_runtime::RuntimeCall::ArbitrumIngressEgress(
pallet_cf_ingress_egress::Call::vault_swap_request {
input_asset: source_asset.try_into().expect("invalid asset for chain"),
output_asset: destination_asset,
deposit_amount,
destination_address,
deposit_metadata,
tx_id,
deposit_details: Box::new(DepositDetails { tx_hashes: Some(vec![tx_id]) }),
broker_fee: vault_swap_parameters.broker_fee,
affiliate_fees: vault_swap_parameters
.affiliate_fees
.into_iter()
.map(|entry| Beneficiary { account: entry.affiliate.into(), bps: entry.fee.into() })
.collect_vec()
.try_into()
.expect("runtime supports at least as many affiliates as we allow in cf_parameters encoding"),
boost_fee: vault_swap_parameters.boost_fee.into(),
dca_params: vault_swap_parameters.dca_params,
refund_params: Box::new(vault_swap_parameters.refund_params),
block_height,
deposits: vec![VaultDepositWitness {
input_asset: source_asset.try_into().expect("invalid asset for chain"),
output_asset: destination_asset,
deposit_amount,
destination_address,
deposit_metadata,
tx_id,
deposit_details: DepositDetails { tx_hashes: Some(vec![tx_id]) },
broker_fee: vault_swap_parameters.broker_fee,
affiliate_fees: vault_swap_parameters
.affiliate_fees
.into_iter()
.map(|entry| Beneficiary { account: entry.affiliate.into(), bps: entry.fee.into() })
.collect_vec()
.try_into()
.expect("runtime supports at least as many affiliates as we allow in cf_parameters encoding"),
boost_fee: vault_swap_parameters.boost_fee.into(),
dca_params: vault_swap_parameters.dca_params,
refund_params: vault_swap_parameters.refund_params,
channel_id,
deposit_address,
}],
},
)
}
Expand Down
24 changes: 19 additions & 5 deletions engine/src/witness/btc/deposits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ use itertools::Itertools;
use pallet_cf_ingress_egress::{DepositChannelDetails, DepositWitness};
use state_chain_runtime::BitcoinInstance;

use super::super::common::chunked_chain_source::chunked_by_vault::{
builder::ChunkedByVaultBuilder, private_deposit_channels::BrokerPrivateChannels, ChunkedByVault,
use super::{
super::common::chunked_chain_source::chunked_by_vault::{
builder::ChunkedByVaultBuilder, private_deposit_channels::BrokerPrivateChannels,
ChunkedByVault,
},
vault_swaps::BtcIngressEgressCall,
};
use crate::{
btc::rpc::VerboseTransaction,
Expand Down Expand Up @@ -71,6 +75,7 @@ impl<Inner: ChunkedByVault> ChunkedByVaultBuilder<Inner> {
private_channels.clone().into_iter().map(move |(broker_id, channel_id)| {
(
broker_id,
channel_id,
DepositAddress::new(
key,
channel_id.try_into().expect("BTC channel id must fit in u32"),
Expand All @@ -80,14 +85,23 @@ impl<Inner: ChunkedByVault> ChunkedByVaultBuilder<Inner> {
})
};

for (broker_id, vault_address) in vault_addresses {
for (broker_id, channel_id, vault_address) in vault_addresses {
for tx in &txs {
if let Some(call) = super::vault_swaps::try_extract_vault_swap_call(
if let Some(deposit) = super::vault_swaps::try_extract_vault_swap_witness(
tx,
&vault_address,
channel_id,
&broker_id,
) {
process_call(call.into(), epoch.index).await;
process_call(
BtcIngressEgressCall::vault_swap_request {
block_height: header.index,
deposits: vec![deposit],
}
.into(),
epoch.index,
)
.await;
}
}
}
Expand Down
42 changes: 26 additions & 16 deletions engine/src/witness/btc/vault_swaps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use cf_chains::{
},
ChannelRefundParameters, ForeignChainAddress,
};
use cf_primitives::{AccountId, Beneficiary, DcaParameters};
use cf_primitives::{AccountId, Beneficiary, ChannelId, DcaParameters};
use cf_utilities::SliceToArray;
use codec::Decode;
use itertools::Itertools;
Expand Down Expand Up @@ -77,14 +77,18 @@ fn script_buf_to_script_pubkey(script: &ScriptBuf) -> Option<ScriptPubkey> {
Some(pubkey)
}

type BtcIngressEgressCall =
pub(super) type BtcIngressEgressCall =
pallet_cf_ingress_egress::Call<state_chain_runtime::Runtime, BitcoinInstance>;

pub fn try_extract_vault_swap_call(
type VaultDepositWitness =
pallet_cf_ingress_egress::VaultDepositWitness<state_chain_runtime::Runtime, BitcoinInstance>;

pub fn try_extract_vault_swap_witness(
tx: &VerboseTransaction,
vault_address: &DepositAddress,
channel_id: ChannelId,
broker_id: &AccountId,
) -> Option<BtcIngressEgressCall> {
) -> Option<VaultDepositWitness> {
// A correctly constructed transaction carrying CF swap parameters must have at least 3 outputs:
let [utxo_to_vault, nulldata_utxo, change_utxo, ..] = &tx.vout[..] else {
return None;
Expand Down Expand Up @@ -130,18 +134,18 @@ pub fn try_extract_vault_swap_call(

let tx_id: [u8; 32] = tx.txid.to_byte_array();

Some(BtcIngressEgressCall::vault_swap_request {
Some(VaultDepositWitness {
input_asset: NATIVE_ASSET,
output_asset: data.output_asset,
deposit_amount,
destination_address: data.output_address,
tx_id: H256::from(tx_id),
deposit_details: Box::new(Utxo {
deposit_details: Utxo {
// we require the deposit to be the first UTXO
id: UtxoId { tx_id: tx_id.into(), vout: 0 },
amount: deposit_amount,
deposit_address: vault_address.clone(),
}),
},
deposit_metadata: None, // No ccm for BTC (yet?)
broker_fee: Beneficiary {
account: broker_id.clone(),
Expand All @@ -155,17 +159,19 @@ pub fn try_extract_vault_swap_call(
.collect_vec()
.try_into()
.expect("runtime supports at least as many affiliates as we allow in UTXO encoding"),
refund_params: Box::new(ChannelRefundParameters {
refund_params: ChannelRefundParameters {
retry_duration: data.parameters.retry_duration.into(),
refund_address: ForeignChainAddress::Btc(refund_address),
min_price,
}),
},
dca_params: Some(DcaParameters {
number_of_chunks: data.parameters.number_of_chunks.into(),
chunk_interval: data.parameters.chunk_interval.into(),
}),
// This is only to be checked in the pre-witnessed version
boost_fee: data.parameters.boost_fee.into(),
channel_id,
deposit_address: vault_address.script_pubkey(),
})
}

Expand Down Expand Up @@ -285,19 +291,21 @@ mod tests {
None,
);

const CHANNEL_ID: ChannelId = 7;

assert_eq!(
try_extract_vault_swap_call(&tx, &vault_deposit_address, &BROKER),
Some(BtcIngressEgressCall::vault_swap_request {
try_extract_vault_swap_witness(&tx, &vault_deposit_address, CHANNEL_ID, &BROKER),
Some(VaultDepositWitness {
input_asset: NATIVE_ASSET,
output_asset: MOCK_SWAP_PARAMS.output_asset,
deposit_amount: DEPOSIT_AMOUNT,
destination_address: MOCK_SWAP_PARAMS.output_address.clone(),
tx_id: tx.txid.to_byte_array().into(),
deposit_details: Box::new(Utxo {
deposit_details: Utxo {
id: UtxoId { tx_id: tx.txid.to_byte_array().into(), vout: 0 },
amount: DEPOSIT_AMOUNT,
deposit_address: vault_deposit_address,
}),
deposit_address: vault_deposit_address.clone(),
},
broker_fee: Beneficiary {
account: BROKER,
bps: MOCK_SWAP_PARAMS.parameters.broker_fee.into()
Expand All @@ -307,19 +315,21 @@ mod tests {
bps: MOCK_SWAP_PARAMS.parameters.affiliates[0].fee.into(),
}],
deposit_metadata: None,
refund_params: Box::new(ChannelRefundParameters {
refund_params: ChannelRefundParameters {
retry_duration: MOCK_SWAP_PARAMS.parameters.retry_duration.into(),
refund_address: ForeignChainAddress::Btc(refund_pubkey),
min_price: sqrt_price_to_price(bounded_sqrt_price(
MOCK_SWAP_PARAMS.parameters.min_output_amount.into(),
DEPOSIT_AMOUNT.into(),
)),
}),
},
dca_params: Some(DcaParameters {
number_of_chunks: MOCK_SWAP_PARAMS.parameters.number_of_chunks.into(),
chunk_interval: MOCK_SWAP_PARAMS.parameters.chunk_interval.into(),
}),
boost_fee: MOCK_SWAP_PARAMS.parameters.boost_fee.into(),
deposit_address: vault_deposit_address.script_pubkey(),
channel_id: CHANNEL_ID,
})
);
}
Expand Down
48 changes: 29 additions & 19 deletions engine/src/witness/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ use cf_chains::{
CcmDepositMetadata, Ethereum,
};
use cf_primitives::{
chains::assets::eth::Asset as EthAsset, Asset, AssetAmount, Beneficiary, EpochIndex,
chains::assets::eth::Asset as EthAsset, Asset, AssetAmount, Beneficiary, ChannelId, EpochIndex,
};
use cf_utilities::task_scope::Scope;
use futures_core::Future;
use itertools::Itertools;
use pallet_cf_ingress_egress::VaultDepositWitness;
use sp_core::H160;

use crate::{
Expand Down Expand Up @@ -234,7 +235,10 @@ impl super::evm::vault::IngressCallBuilder for EthCallBuilder {
type Chain = Ethereum;

fn vault_swap_request(
block_height: u64,
source_asset: Asset,
deposit_address: cf_chains::eth::Address,
channel_id: ChannelId,
deposit_amount: AssetAmount,
destination_asset: Asset,
destination_address: EncodedAddress,
Expand All @@ -244,24 +248,30 @@ impl super::evm::vault::IngressCallBuilder for EthCallBuilder {
) -> state_chain_runtime::RuntimeCall {
state_chain_runtime::RuntimeCall::EthereumIngressEgress(
pallet_cf_ingress_egress::Call::vault_swap_request {
input_asset: source_asset.try_into().expect("invalid asset for chain"),
output_asset: destination_asset,
deposit_amount,
destination_address,
deposit_metadata,
tx_id,
deposit_details: Box::new(DepositDetails { tx_hashes: Some(vec![tx_id]) }),
broker_fee: vault_swap_parameters.broker_fee,
affiliate_fees: vault_swap_parameters
.affiliate_fees
.into_iter()
.map(|entry| Beneficiary { account: entry.affiliate.into(), bps: entry.fee.into() })
.collect_vec()
.try_into()
.expect("runtime supports at least as many affiliates as we allow in cf_parameters encoding"),
boost_fee: vault_swap_parameters.boost_fee.into(),
dca_params: vault_swap_parameters.dca_params,
refund_params: Box::new(vault_swap_parameters.refund_params),
block_height,
deposits: vec![ VaultDepositWitness {
input_asset: source_asset.try_into().expect("invalid asset for chain"),
output_asset: destination_asset,
deposit_amount,
destination_address,
deposit_metadata,
tx_id,
deposit_details: DepositDetails { tx_hashes: Some(vec![tx_id]) },
broker_fee: vault_swap_parameters.broker_fee,
affiliate_fees: vault_swap_parameters
.affiliate_fees
.into_iter()
.map(|entry| Beneficiary { account: entry.affiliate.into(), bps: entry.fee.into() })
.collect_vec()
.try_into()
.expect("runtime supports at least as many affiliates as we allow in cf_parameters encoding"),
boost_fee: vault_swap_parameters.boost_fee.into(),
dca_params: vault_swap_parameters.dca_params,
refund_params: vault_swap_parameters.refund_params,
channel_id,
deposit_address,
}
],
},
)
}
Expand Down
Loading

0 comments on commit 69a23d6

Please sign in to comment.