Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into chore/unify-dependencies
Browse files Browse the repository at this point in the history
* origin/main:
  feat: broker can encode btc smart contract call (#5329)
  chore: localnet recreate script can use defaults (#5338)
  feat: witnessing btc smart contract swaps (#5331)
  feat: Solana CCM fallback (#5316)
  fix: scale types for pending ceremonies (#5286)
  chore: Prune historical values in Validator pallet (#5292)
  feat: expose deposit transaction hash from ingress-egress-tracker (#5320)

# Conflicts:
#	Cargo.lock
#	engine/src/witness/btc/smart_contract.rs
  • Loading branch information
syan095 committed Oct 23, 2024
2 parents b8cd08a + df8771a commit 8e66a27
Show file tree
Hide file tree
Showing 58 changed files with 1,392 additions and 380 deletions.
48 changes: 47 additions & 1 deletion api/bin/chainflip-broker-api/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use cf_utilities::{
health::{self, HealthCheckOptions},
rpc::NumberOrHex,
task_scope::{task_scope, Scope},
try_parse_number_or_hex,
};
use chainflip_api::{
self,
primitives::{AccountRole, Affiliates, Asset, BasisPoints, CcmChannelMetadata, DcaParameters},
settings::StateChain,
AccountId32, AddressString, BrokerApi, OperatorApi, RefundParameters, StateChainApi,
SwapDepositAddress, WithdrawFeesDetail,
SwapDepositAddress, SwapPayload, WithdrawFeesDetail,
};
use clap::Parser;
use custom_rpc::to_rpc_error;
Expand Down Expand Up @@ -48,6 +50,20 @@ pub trait Rpc {
asset: Asset,
destination_address: AddressString,
) -> RpcResult<WithdrawFeesDetail>;

#[method(name = "request_swap_parameter_encoding", aliases = ["broker_requestSwapParameterEncoding"])]
async fn request_swap_parameter_encoding(
&self,
source_asset: Asset,
destination_asset: Asset,
destination_address: AddressString,
broker_commission: BasisPoints,
retry_duration: u32,
min_output_amount: NumberOrHex,
dca_parameters: Option<DcaParameters>,
boost_fee: Option<BasisPoints>,
affiliate_fees: Option<Affiliates<AccountId32>>,
) -> RpcResult<SwapPayload>;
}

pub struct RpcServerImpl {
Expand Down Expand Up @@ -120,6 +136,36 @@ impl RpcServer for RpcServerImpl {
.await
.map_err(to_rpc_error)?)
}

async fn request_swap_parameter_encoding(
&self,
source_asset: Asset,
destination_asset: Asset,
destination_address: AddressString,
broker_commission: BasisPoints,
retry_duration: u32,
min_output_amount: NumberOrHex,
dca_parameters: Option<DcaParameters>,
boost_fee: Option<BasisPoints>,
affiliate_fees: Option<Affiliates<AccountId32>>,
) -> RpcResult<SwapPayload> {
Ok(self
.api
.broker_api()
.request_swap_parameter_encoding(
source_asset,
destination_asset,
destination_address,
retry_duration,
try_parse_number_or_hex(min_output_amount).map_err(to_rpc_error)?,
boost_fee,
dca_parameters,
broker_commission,
affiliate_fees,
)
.await
.map_err(to_rpc_error)?)
}
}

#[derive(Parser, Debug, Clone, Default)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: api/bin/chainflip-ingress-egress-tracker/src/witnessing/state_chain.rs
assertion_line: 487
expression: "store.storage.get(format!(\"deposit:Polkadot:{}\", format!\n (\"0x{}\", hex ::\n encode(polkadot_account_id.aliased_ref()))).as_str()).unwrap()"
---
[{"amount":"0x64","asset":{"asset":"DOT","chain":"Polkadot"},"deposit_chain_block_height":1}]
[{"amount":"0x64","asset":{"asset":"DOT","chain":"Polkadot"},"deposit_chain_block_height":1,"deposit_details":{"extrinsic_index":1}}]
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: api/bin/chainflip-ingress-egress-tracker/src/witnessing/state_chain.rs
assertion_line: 497
expression: "store.storage.get(format!(\"deposit:Ethereum:{}\",\n eth_address_str2.to_lowercase()).as_str()).unwrap()"
---
[{"amount":"0x64","asset":{"asset":"ETH","chain":"Ethereum"},"deposit_chain_block_height":1}]
[{"amount":"0x64","asset":{"asset":"ETH","chain":"Ethereum"},"deposit_chain_block_height":1,"deposit_details":null}]
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: api/bin/chainflip-ingress-egress-tracker/src/witnessing/state_chain.rs
assertion_line: 521
expression: "store.storage.get(format!(\"deposit:Ethereum:{}\",\n eth_address_str1.to_lowercase()).as_str()).unwrap()"
---
[{"amount":"0x64","asset":{"asset":"ETH","chain":"Ethereum"},"deposit_chain_block_height":1},{"amount":"0x1e8480","asset":{"asset":"ETH","chain":"Ethereum"},"deposit_chain_block_height":1}]
[{"amount":"0x64","asset":{"asset":"ETH","chain":"Ethereum"},"deposit_chain_block_height":1,"deposit_details":null},{"amount":"0x1e8480","asset":{"asset":"ETH","chain":"Ethereum"},"deposit_chain_block_height":1,"deposit_details":null}]
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: api/bin/chainflip-ingress-egress-tracker/src/witnessing/state_chain.rs
assertion_line: 483
expression: "store.storage.get(format!(\"deposit:Ethereum:{}\",\n eth_address_str1.to_lowercase()).as_str()).unwrap()"
---
[{"amount":"0x64","asset":{"asset":"ETH","chain":"Ethereum"},"deposit_chain_block_height":1}]
[{"amount":"0x64","asset":{"asset":"ETH","chain":"Ethereum"},"deposit_chain_block_height":1,"deposit_details":null}]
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
};
use cf_chains::{
address::ToHumanreadableAddress,
dot::PolkadotTransactionId,
dot::{PolkadotExtrinsicIndex, PolkadotTransactionId},
evm::{SchnorrVerificationComponents, H256},
AnyChain, Arbitrum, Bitcoin, Chain, Ethereum, Polkadot,
};
Expand Down Expand Up @@ -58,6 +58,15 @@ enum TransactionId {
Arbitrum { signature: SchnorrVerificationComponents },
}

#[derive(Serialize)]
#[serde(untagged)]
enum DepositDetails {
Bitcoin { tx_id: H256, vout: u32 },
Ethereum { tx_hashes: Vec<H256> },
Polkadot { extrinsic_index: PolkadotExtrinsicIndex },
Arbitrum { tx_hashes: Vec<H256> },
}

#[derive(Serialize)]
#[serde(untagged)]
enum WitnessInformation {
Expand All @@ -67,6 +76,7 @@ enum WitnessInformation {
deposit_address: String,
amount: NumberOrHex,
asset: cf_chains::assets::any::Asset,
deposit_details: Option<DepositDetails>,
},
Broadcast {
#[serde(skip_serializing)]
Expand Down Expand Up @@ -121,6 +131,10 @@ impl From<DepositInfo<Ethereum>> for WitnessInformation {
deposit_address: hex_encode_bytes(value.deposit_address.as_bytes()),
amount: value.amount.into(),
asset: value.asset.into(),
deposit_details: value
.deposit_details
.tx_hashes
.map(|tx_hashes| DepositDetails::Ethereum { tx_hashes }),
}
}
}
Expand All @@ -132,6 +146,10 @@ impl From<DepositInfo<Bitcoin>> for WitnessInformation {
deposit_address: value.deposit_address.to_humanreadable(network),
amount: value.amount.into(),
asset: value.asset.into(),
deposit_details: Some(DepositDetails::Bitcoin {
tx_id: value.deposit_details.utxo_id.tx_id,
vout: value.deposit_details.utxo_id.vout,
}),
}
}
}
Expand All @@ -143,6 +161,9 @@ impl From<DepositInfo<Polkadot>> for WitnessInformation {
deposit_address: hex_encode_bytes(value.deposit_address.aliased_ref()),
amount: value.amount.into(),
asset: value.asset.into(),
deposit_details: Some(DepositDetails::Polkadot {
extrinsic_index: value.deposit_details,
}),
}
}
}
Expand All @@ -154,6 +175,10 @@ impl From<DepositInfo<Arbitrum>> for WitnessInformation {
deposit_address: hex_encode_bytes(value.deposit_address.as_bytes()),
amount: value.amount.into(),
asset: value.asset.into(),
deposit_details: value
.deposit_details
.tx_hashes
.map(|tx_hashes| DepositDetails::Arbitrum { tx_hashes }),
}
}
}
Expand Down Expand Up @@ -208,15 +233,8 @@ where
deposit_witnesses,
block_height,
}) =>
for witness in deposit_witnesses as Vec<DepositWitness<Polkadot>> {
store
.save_to_array(&WitnessInformation::from((
witness,
block_height,
chainflip_network,
)))
.await?;
},
save_deposit_witnesses(store, deposit_witnesses, block_height, chainflip_network)
.await?,
ArbitrumIngressEgress(IngressEgressCall::process_deposits {
deposit_witnesses,
block_height,
Expand Down
86 changes: 83 additions & 3 deletions api/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ use anyhow::{anyhow, bail, Context, Result};
use async_trait::async_trait;
use cf_chains::{
address::{try_from_encoded_address, EncodedAddress},
btc::smart_contract_encoding::{
encode_swap_params_in_nulldata_utxo, SharedCfParameters, UtxoEncodedData,
},
dot::PolkadotAccountId,
evm::to_evm_address,
sol::SolAddress,
CcmChannelMetadata, ChannelRefundParametersGeneric, ForeignChain, ForeignChainAddress,
};
pub use cf_primitives::{AccountRole, Affiliates, Asset, BasisPoints, ChannelId, SemVer};
use cf_primitives::{BlockNumber, DcaParameters, NetworkEnvironment, Price};
use cf_primitives::{AssetAmount, BlockNumber, DcaParameters, NetworkEnvironment, Price};
use pallet_cf_account_roles::MAX_LENGTH_FOR_VANITY_NAME;
use pallet_cf_governance::ExecutionMode;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -107,6 +110,11 @@ pub async fn request_block(
.ok_or_else(|| anyhow!("unknown block hash"))
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SwapPayload {
Bitcoin { nulldata_utxo: Bytes },
}

pub struct StateChainApi {
pub state_chain_client: Arc<StateChainClient>,
}
Expand Down Expand Up @@ -321,8 +329,8 @@ impl AddressString {
.map_err(|_| anyhow!("Failed to parse address"))
}

pub fn from_encoded_address(address: &EncodedAddress) -> Self {
Self(address.to_string())
pub fn from_encoded_address<T: std::borrow::Borrow<EncodedAddress>>(address: T) -> Self {
Self(address.borrow().to_string())
}
}

Expand Down Expand Up @@ -515,6 +523,78 @@ pub trait BrokerApi: SignedExtrinsicApi + StorageApi + Sized + Send + Sync + 'st
self.simple_submission_with_dry_run(pallet_cf_swapping::Call::deregister_as_broker {})
.await
}

async fn request_swap_parameter_encoding(
&self,
input_asset: Asset,
output_asset: Asset,
output_address: AddressString,
retry_duration: BlockNumber,
min_output_amount: AssetAmount,
boost_fee: Option<BasisPoints>,
dca_parameters: Option<DcaParameters>,
broker_commission: BasisPoints,
affiliate_fees: Option<Affiliates<AccountId32>>,
) -> Result<SwapPayload> {
// Check if safe mode is active
let block_hash = self.base_rpc_api().latest_finalized_block_hash().await?;
let safe_mode = self
.storage_value::<pallet_cf_environment::RuntimeSafeMode<state_chain_runtime::Runtime>>(
block_hash,
)
.await?;
if !safe_mode.swapping.swaps_enabled {
bail!("Safe mode is active. Swaps are disabled.");
}

// Validate params
frame_support::ensure!(
broker_commission == 0 && affiliate_fees.map_or(true, |fees| fees.is_empty()),
anyhow!("Broker/Affi fees are not yet supported for vault swaps. Request a deposit address or remove the broker fees.")
);
self.base_rpc_api()
.validate_refund_params(retry_duration, Some(block_hash))
.await?;
if let Some(params) = dca_parameters.as_ref() {
self.base_rpc_api()
.validate_dca_params(
params.number_of_chunks,
params.chunk_interval,
Some(block_hash),
)
.await?;
}

// Encode swap
match ForeignChain::from(input_asset) {
ForeignChain::Bitcoin => {
let params = UtxoEncodedData {
output_asset,
output_address: output_address
.try_parse_to_encoded_address(output_asset.into())?,
parameters: SharedCfParameters {
retry_duration: retry_duration.try_into()?,
min_output_amount,
number_of_chunks: dca_parameters
.as_ref()
.map(|params| params.number_of_chunks)
.unwrap_or(1)
.try_into()?,
chunk_interval: dca_parameters
.as_ref()
.map(|params| params.chunk_interval)
.unwrap_or(2)
.try_into()?,
boost_fee: boost_fee.unwrap_or_default().try_into()?,
},
};
Ok(SwapPayload::Bitcoin {
nulldata_utxo: encode_swap_params_in_nulldata_utxo(params).raw().into(),
})
},
_ => bail!("Unsupported input asset"),
}
}
}

#[async_trait]
Expand Down
3 changes: 3 additions & 0 deletions bouncer/shared/swapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ function newSolanaCfParameters(maxAccounts: number) {
cfReceiver.is_writable ? 1 : 0,
]);

const fallbackAddrBytes = new PublicKey(getContractAddress('Solana', 'FALLBACK')).toBytes();

const remainingAccounts = [];
const numRemainingAccounts = Math.floor(Math.random() * maxAccounts);

Expand All @@ -104,6 +106,7 @@ function newSolanaCfParameters(maxAccounts: number) {
// Inserted by the codec::Encode
4 * remainingAccounts.length,
...remainingAccounts.flatMap((account) => Array.from(account)),
...fallbackAddrBytes,
]);

return arrayToHexString(cfParameters);
Expand Down
10 changes: 8 additions & 2 deletions bouncer/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ export function getContractAddress(chain: Chain, contract: string): string {
return '8pBPaVfTAcjLeNfC187Fkvi9b1XEFhRNJ95BQXXVksmH';
case 'SWAP_ENDPOINT':
return '35uYgHdfZQT4kHkaaXQ6ZdCkK5LFrsk43btTLbGCRCNT';
case 'FALLBACK':
/// 3wDSVR6YSRDFiWdwsnZZRjAKHKvsmb4fouYVqoBt5yd4vSrY7aQdvtLJKMvEb3AMWGD5fxunfdotvwPwnSChWMWx
return 'CFf51BPWnybvgbZrxy61s4SCCvEohBC7achsPLuoACUG';
default:
throw new Error(`Unsupported contract: ${contract}`);
}
Expand Down Expand Up @@ -723,7 +726,7 @@ export async function observeSolanaCcmEvent(
const remainingAccountSize = publicKeySize + 1;

// Extra byte for the encoded length
const remainingAccountsBytes = cfParameters.slice(remainingAccountSize + 1);
const remainingAccountsBytes = cfParameters.slice(remainingAccountSize + 1, -publicKeySize);

const remainingAccounts = [];
const remainingIsWritable = [];
Expand All @@ -737,7 +740,10 @@ export async function observeSolanaCcmEvent(
remainingIsWritable.push(Boolean(isWritable));
}

return { remainingAccounts, remainingIsWritable };
// fallback account
const fallbackAccount = cfParameters.slice(-publicKeySize);

return { remainingAccounts, remainingIsWritable, fallbackAccount };
}

const connection = getSolConnection();
Expand Down
Loading

0 comments on commit 8e66a27

Please sign in to comment.