Repository: chainflip-io/chainflip-backend
Pull request #5711: feat: add RPC to return all current vault addresses
Author: login: Janislav
Created: 2025-03-07T11:21:50Z | Merged: 2025-03-13T15:15:00Z

# Pull Request

Ticket: PRO-2109

## Checklist

Please conduct a thorough self-review before opening the PR.

- [x] I am confident that the code works.
- [x] I have written sufficient tests.
- [ ] I have written and tested required migrations.
- [ ] I have updated documentation where appropriate.

## Summary

Adds a new endpoint to return:
- The Ethereum vault address
- The Arbitrum vault address
- All (pervious current addresses) for private broker channels in a vector (AccountId, EncodedAddress)


Diff:
diff --git a/api/bin/chainflip-broker-api/src/main.rs b/api/bin/chainflip-broker-api/src/main.rs
index 3812d0ce0f1..3536e36f22f 100644
--- a/api/bin/chainflip-broker-api/src/main.rs
+++ b/api/bin/chainflip-broker-api/src/main.rs
@@ -7,7 +7,8 @@ use chainflip_api::{
 	self,
 	primitives::{
 		state_chain_runtime::runtime_apis::{
-			ChainAccounts, ChannelActionType, TransactionScreeningEvents, VaultSwapDetails,
+			ChainAccounts, ChannelActionType, TransactionScreeningEvents, VaultAddresses,
+			VaultSwapDetails,
 		},
 		AccountRole, AffiliateDetails, Affiliates, Asset, BasisPoints, CcmChannelMetadata,
 		DcaParameters,
@@ -156,6 +157,9 @@ pub trait Rpc {
 		&self,
 		affiliate_account_id: AccountId32,
 	) -> RpcResult<WithdrawFeesDetail>;
+
+	#[method(name = "get_vault_addresses", aliases = ["broker_getVaultAddresses"])]
+	async fn vault_addresses(&self) -> RpcResult<VaultAddresses>;
 }
 
 pub struct RpcServerImpl {
@@ -342,6 +346,10 @@ impl RpcServer for RpcServerImpl {
 	) -> RpcResult<WithdrawFeesDetail> {
 		Ok(self.api.broker_api().affiliate_withdrawal_request(affiliate_account_id).await?)
 	}
+
+	async fn vault_addresses(&self) -> RpcResult<VaultAddresses> {
+		Ok(self.api.raw_client().cf_vault_addresses(None).await?)
+	}
 }
 
 #[derive(Parser, Debug, Clone, Default)]
diff --git a/state-chain/custom-rpc/src/lib.rs b/state-chain/custom-rpc/src/lib.rs
index 715cabf46d9..69110506655 100644
--- a/state-chain/custom-rpc/src/lib.rs
+++ b/state-chain/custom-rpc/src/lib.rs
@@ -64,7 +64,7 @@ use state_chain_runtime::{
 		ChannelActionType, CustomRuntimeApi, DispatchErrorWithMessage, ElectoralRuntimeApi,
 		FailingWitnessValidators, FeeTypes, LiquidityProviderBoostPoolInfo, LiquidityProviderInfo,
 		RuntimeApiPenalty, SimulatedSwapInformation, TransactionScreeningEvents, ValidatorInfo,
-		VaultSwapDetails,
+		VaultAddresses, VaultSwapDetails,
 	},
 	safe_mode::RuntimeSafeMode,
 	Hash, NetworkFee, SolanaInstance,
@@ -1055,6 +1055,12 @@ pub trait CustomApi {
 		&self,
 		at: Option<state_chain_runtime::Hash>,
 	) -> RpcResult<Vec<(state_chain_runtime::AccountId, ChannelActionType, ChainAccounts)>>;
+
+	#[method(name = "get_vault_addresses")]
+	fn cf_vault_addresses(
+		&self,
+		at: Option<state_chain_runtime::Hash>,
+	) -> RpcResult<VaultAddresses>;
 }
 
 /// An RPC extension for the state chain node.
@@ -1321,6 +1327,7 @@ where
 		cf_get_open_deposit_channels(account_id: Option<state_chain_runtime::AccountId>) -> ChainAccounts,
 		cf_affiliate_details(broker: state_chain_runtime::AccountId, affiliate: Option<state_chain_runtime::AccountId>) -> Vec<(state_chain_runtime::AccountId, AffiliateDetails)>,
 		cf_all_open_deposit_channels() -> Vec<(state_chain_runtime::AccountId, ChannelActionType, ChainAccounts)>,
+		cf_vault_addresses() -> VaultAddresses,
 	}
 
 	pass_through_and_flatten! {
@@ -2172,6 +2179,8 @@ where
 mod test {
 	use std::collections::BTreeSet;
 
+	use cf_chains::address::EncodedAddress;
+
 	use super::*;
 	use cf_chains::{assets::sol, btc::ScriptPubkey};
 	use cf_primitives::{
@@ -2535,4 +2544,14 @@ mod test {
 		})
 		.unwrap());
 	}
+
+	#[test]
+	fn test_vault_addresses_custom_rpc() {
+		let val: VaultAddresses = VaultAddresses {
+			ethereum: EncodedAddress::Eth([0; 20]),
+			arbitrum: EncodedAddress::Arb([1; 20]),
+			bitcoin: vec![(ID_1.clone(), EncodedAddress::Btc(Vec::new()))],
+		};
+		insta::assert_json_snapshot!(val);
+	}
 }
diff --git a/state-chain/custom-rpc/src/snapshots/custom_rpc__test__vault_addresses_custom_rpc.snap b/state-chain/custom-rpc/src/snapshots/custom_rpc__test__vault_addresses_custom_rpc.snap
new file mode 100644
index 00000000000..7934ecf30c2
--- /dev/null
+++ b/state-chain/custom-rpc/src/snapshots/custom_rpc__test__vault_addresses_custom_rpc.snap
@@ -0,0 +1,63 @@
+---
+source: state-chain/custom-rpc/src/lib.rs
+expression: val
+snapshot_kind: text
+---
+{
+  "ethereum": {
+    "Eth": [
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0,
+      0
+    ]
+  },
+  "arbitrum": {
+    "Arb": [
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1,
+      1
+    ]
+  },
+  "bitcoin": [
+    [
+      "5C62Ck4UrFPiBtoCmeSrgF7x9yv9mn38446dhCpsi2mLHiFT",
+      {
+        "Btc": []
+      }
+    ]
+  ]
+}
diff --git a/state-chain/runtime/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs
index d5cbfb6aa19..177fb162563 100644
--- a/state-chain/runtime/src/chainflip.rs
+++ b/state-chain/runtime/src/chainflip.rs
@@ -65,6 +65,7 @@ use cf_chains::{
 use cf_primitives::{
 	chains::assets, AccountRole, Asset, BasisPoints, Beneficiaries, ChannelId, DcaParameters,
 };
+
 use cf_traits::{
 	AccountInfo, AccountRoleRegistry, BackupRewardsNotifier, BlockEmissions,
 	BroadcastAnyChainGovKey, Broadcaster, Chainflip, CommKeyBroadcaster, DepositApi, EgressApi,
diff --git a/state-chain/runtime/src/chainflip/address_derivation/btc.rs b/state-chain/runtime/src/chainflip/address_derivation/btc.rs
index 273bec6b290..be45de4c9a8 100644
--- a/state-chain/runtime/src/chainflip/address_derivation/btc.rs
+++ b/state-chain/runtime/src/chainflip/address_derivation/btc.rs
@@ -1,13 +1,45 @@
 use super::AddressDerivation;
 use crate::{BitcoinThresholdSigner, Environment, EpochKey, String};
 use cf_chains::{
-	address::{AddressDerivationApi, AddressDerivationError},
-	btc::deposit_address::DepositAddress,
+	address::{AddressDerivationApi, AddressDerivationError, EncodedAddress},
+	btc::{deposit_address::DepositAddress, AggKey, ScriptPubkey},
 	Bitcoin, Chain,
 };
 use cf_primitives::ChannelId;
 use cf_traits::KeyProvider;
 
+pub struct BitcoinPrivateBrokerDepositAddresses<Address> {
+	pub previous: Option<Address>,
+	pub current: Address,
+}
+
+impl<A> BitcoinPrivateBrokerDepositAddresses<A> {
+	pub fn map_address<B, F>(self, f: F) -> BitcoinPrivateBrokerDepositAddresses<B>
+	where
+		F: Fn(A) -> B,
+	{
+		BitcoinPrivateBrokerDepositAddresses {
+			previous: self.previous.map(&f),
+			current: f(self.current),
+		}
+	}
+}
+
+impl BitcoinPrivateBrokerDepositAddresses<ScriptPubkey> {
+	pub fn with_encoded_addresses(self) -> BitcoinPrivateBrokerDepositAddresses<EncodedAddress> {
+		self.map_address(|script_pubkey| {
+			EncodedAddress::from_chain_account::<Bitcoin>(
+				script_pubkey,
+				Environment::network_environment(),
+			)
+		})
+	}
+
+	pub fn current_address(&self) -> String {
+		self.current.to_address(&Environment::network_environment().into())
+	}
+}
+
 impl AddressDerivationApi<Bitcoin> for AddressDerivation {
 	fn generate_address(
 		source_asset: <Bitcoin as Chain>::ChainAsset,
@@ -46,18 +78,23 @@ impl AddressDerivationApi<Bitcoin> for AddressDerivation {
 
 /// ONLY FOR USE IN RPC CALLS.
 ///
-/// Derives the BTC vault deposit address from the private channel id.
+/// Derives the current and previous BTC vault deposit addresses from the private channel id.
 /// Note: This function will **panic** if the private channel id is out of bounds or if there is
 /// no active epoch key for Bitcoin.
-pub(crate) fn derive_btc_vault_deposit_address(private_channel_id: u64) -> String {
-	let EpochKey { key, .. } = BitcoinThresholdSigner::active_epoch_key()
-		.expect("We should always have a key for the current epoch.");
-	DepositAddress::new(
-		key.current,
-		private_channel_id.try_into().expect("Private channel id out of bounds."),
-	)
-	.script_pubkey()
-	.to_address(&Environment::network_environment().into())
+///
+/// Note: If there is no previous key, we return `None` for the previous address.
+pub fn derive_btc_vault_deposit_addresses(
+	private_channel_id: u64,
+) -> BitcoinPrivateBrokerDepositAddresses<ScriptPubkey> {
+	let EpochKey { key: AggKey { previous, current }, .. } =
+		BitcoinThresholdSigner::active_epoch_key()
+			.expect("We should always have a key for the current epoch.");
+
+	let private_channel_id: u32 =
+		private_channel_id.try_into().expect("Private channel id out of bounds.");
+
+	BitcoinPrivateBrokerDepositAddresses { previous, current }
+		.map_address(|key| DepositAddress::new(key, private_channel_id).script_pubkey())
 }
 
 #[test]
diff --git a/state-chain/runtime/src/chainflip/vault_swaps.rs b/state-chain/runtime/src/chainflip/vault_swaps.rs
index eea67535647..0cf0b7100e1 100644
--- a/state-chain/runtime/src/chainflip/vault_swaps.rs
+++ b/state-chain/runtime/src/chainflip/vault_swaps.rs
@@ -1,6 +1,6 @@
 use crate::{
 	chainflip::{
-		address_derivation::btc::derive_btc_vault_deposit_address, AddressConverter,
+		address_derivation::btc::derive_btc_vault_deposit_addresses, AddressConverter,
 		ChainAddressConverter, EvmEnvironment, SolEnvironment,
 	},
 	runtime_apis::{DispatchErrorWithMessage, EvmVaultSwapDetails, VaultSwapDetails},
@@ -95,7 +95,7 @@ pub fn bitcoin_vault_swap(
 
 	Ok(VaultSwapDetails::Bitcoin {
 		nulldata_payload: encode_swap_params_in_nulldata_payload(params),
-		deposit_address: derive_btc_vault_deposit_address(private_channel_id),
+		deposit_address: derive_btc_vault_deposit_addresses(private_channel_id).current_address(),
 	})
 }
 
diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs
index 548579cf178..62d63a2c223 100644
--- a/state-chain/runtime/src/lib.rs
+++ b/state-chain/runtime/src/lib.rs
@@ -11,7 +11,9 @@ pub mod test_runner;
 mod weights;
 use crate::{
 	chainflip::{
-		address_derivation::btc::derive_btc_vault_deposit_address,
+		address_derivation::btc::{
+			derive_btc_vault_deposit_addresses, BitcoinPrivateBrokerDepositAddresses,
+		},
 		calculate_account_apy,
 		solana_elections::{
 			SolanaChainTrackingProvider, SolanaEgressWitnessingTrigger, SolanaIngress,
@@ -30,7 +32,7 @@ use crate::{
 		BoostPoolDetails, BrokerInfo, CcmData, ChannelActionType, DispatchErrorWithMessage,
 		FailingWitnessValidators, FeeTypes, LiquidityProviderBoostPoolInfo, LiquidityProviderInfo,
 		RuntimeApiPenalty, SimulateSwapAdditionalOrder, SimulatedSwapInformation,
-		TransactionScreeningEvents, ValidatorInfo, VaultSwapDetails,
+		TransactionScreeningEvents, ValidatorInfo, VaultAddresses, VaultSwapDetails,
 	},
 };
 use cf_amm::{
@@ -2009,7 +2011,7 @@ impl_runtime_apis! {
 					(asset, AssetBalances::get_balance(&account_id, asset))
 				).collect(),
 				btc_vault_deposit_address: BrokerPrivateBtcChannels::<Runtime>::get(&account_id)
-					.map(derive_btc_vault_deposit_address),
+					.map(|channel| derive_btc_vault_deposit_addresses(channel).current_address()),
 				affiliates: pallet_cf_swapping::AffiliateAccountDetails::<Runtime>::iter_prefix(&account_id).collect(),
 				bond: account_info.bond()
 			}
@@ -2529,6 +2531,21 @@ impl_runtime_apis! {
 				pallet_cf_swapping::AffiliateAccountDetails::<Runtime>::iter_prefix(&broker).collect()
 			}
 		}
+
+		fn cf_vault_addresses() -> VaultAddresses {
+			VaultAddresses {
+				ethereum: EncodedAddress::Eth(Environment::eth_vault_address().into()),
+				arbitrum: EncodedAddress::Arb(Environment::arb_vault_address().into()),
+				bitcoin: BrokerPrivateBtcChannels::<Runtime>::iter()
+					.flat_map(|(account_id, channel_id)| {
+						let BitcoinPrivateBrokerDepositAddresses { previous, current } = derive_btc_vault_deposit_addresses(channel_id)
+							.with_encoded_addresses();
+						previous.into_iter().chain(core::iter::once(current))
+							.map(move |address| (account_id.clone(), address))
+					})
+					.collect(),
+			}
+		}
 	}
 
 
diff --git a/state-chain/runtime/src/runtime_apis.rs b/state-chain/runtime/src/runtime_apis.rs
index 2200771dc3c..d80f997afe6 100644
--- a/state-chain/runtime/src/runtime_apis.rs
+++ b/state-chain/runtime/src/runtime_apis.rs
@@ -344,6 +344,13 @@ pub struct TransactionScreeningEvents {
 	pub arb_events: Vec<BrokerRejectionEventFor<cf_chains::Arbitrum>>,
 }
 
+#[derive(Encode, Decode, TypeInfo, Serialize, Deserialize, Clone)]
+pub struct VaultAddresses {
+	pub ethereum: EncodedAddress,
+	pub arbitrum: EncodedAddress,
+	pub bitcoin: Vec<(AccountId32, EncodedAddress)>,
+}
+
 // READ THIS BEFORE UPDATING THIS TRAIT:
 //
 // ## When changing an existing method:
@@ -518,6 +525,7 @@ decl_runtime_apis!(
 			affiliate: Option<AccountId32>,
 		) -> Vec<(AccountId32, AffiliateDetails)>;
 		fn cf_all_open_deposit_channels() -> Vec<(AccountId32, ChannelActionType, ChainAccounts)>;
+		fn cf_vault_addresses() -> VaultAddresses;
 	}
 );
 


Reviews:
user: login: dandanlen
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T09:06:08Z
user: login: dandanlen
body: One other thing: we return EncodedAddress from the custom rpc layer, but AddressString from the Broker API. It would be better to be consistent and use the same in both APIs. Since (afaik) the deposit monitor currently uses `EncodedAddress` so let's stick with that.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T17:24:36Z
user: login: MxmUrw
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T16:38:32Z
user: login: dandanlen
body: LGTM, thanks. I'm going to make some small refactors before merging.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-13T13:59:50Z

Review comments:
diff_hunk: @@ -76,6 +76,13 @@ pub enum GetOpenDepositChannelsQuery {
 	Mine,
 }
 
+#[derive(Serialize, Deserialize, Clone)]
+pub struct VaultAddresses {
+	ethereum: String,
+	arbitrum: String,
+	bitcoin: String,
path: api/bin/chainflip-broker-api/src/main.rs
user: login: dandanlen
body: For bitcoin there should be multiple addresses: each broker can have one private channel and each private channel needs two addresses: one the current and one for the previous epoch's key. 
created_at: 2025-03-10T09:04:39Z
author_association: COLLABORATOR
diff_hunk: @@ -49,7 +49,7 @@ impl AddressDerivationApi<Bitcoin> for AddressDerivation {
 /// Derives the BTC vault deposit address from the private channel id.
 /// Note: This function will **panic** if the private channel id is out of bounds or if there is
 /// no active epoch key for Bitcoin.
-pub(crate) fn derive_btc_vault_deposit_address(private_channel_id: u64) -> String {
+pub fn derive_btc_vault_deposit_address(private_channel_id: u64) -> String {
path: state-chain/runtime/src/chainflip/address_derivation/btc.rs
user: login: dandanlen
body: We might have to alter this method to allow us to generate the previous key too. The deposit monitor will need to track this. 
created_at: 2025-03-10T09:05:54Z
author_association: COLLABORATOR
diff_hunk: @@ -2529,6 +2529,13 @@ impl_runtime_apis! {
 				pallet_cf_swapping::AffiliateAccountDetails::<Runtime>::iter_prefix(&broker).collect()
 			}
 		}
+
+		fn cf_vault_addresses() -> (EncodedAddress, EncodedAddress, EncodedAddress) {
+			let eth_vault_address = EncodedAddress::Eth(Environment::eth_vault_address().into());
+			let arb_vault_address = EncodedAddress::Arb(Environment::arb_vault_address().into());
+			let btc_vault_address = EncodedAddress::Btc(derive_btc_vault_deposit_address(0).into());
path: state-chain/runtime/src/lib.rs
user: login: dandanlen
body: Here: we can't just use 0, we need to use the private channels of each broker. 
created_at: 2025-03-10T09:06:01Z
author_association: COLLABORATOR
diff_hunk: @@ -46,18 +46,36 @@ impl AddressDerivationApi<Bitcoin> for AddressDerivation {
 
 /// ONLY FOR USE IN RPC CALLS.
 ///
-/// Derives the BTC vault deposit address from the private channel id.
+/// Derives the current and previous BTC vault deposit addresses from the private channel id.
 /// Note: This function will **panic** if the private channel id is out of bounds or if there is
 /// no active epoch key for Bitcoin.
-pub(crate) fn derive_btc_vault_deposit_address(private_channel_id: u64) -> String {
+///
+/// Returns a tuple of the previous and current BTC vault deposit addresses.
+/// Note: If there is no previous key, the current address is returned for both the previous and
+/// current addresses.
path: state-chain/runtime/src/chainflip/address_derivation/btc.rs
user: login: dandanlen
body: This is IMO not the most intuitive behaviour, why not be explicit and make one optional. It can look like the AggKey struct with current/previous. 
created_at: 2025-03-10T16:21:07Z
author_association: COLLABORATOR
diff_hunk: @@ -46,18 +46,36 @@ impl AddressDerivationApi<Bitcoin> for AddressDerivation {
 
 /// ONLY FOR USE IN RPC CALLS.
 ///
-/// Derives the BTC vault deposit address from the private channel id.
+/// Derives the current and previous BTC vault deposit addresses from the private channel id.
 /// Note: This function will **panic** if the private channel id is out of bounds or if there is
 /// no active epoch key for Bitcoin.
-pub(crate) fn derive_btc_vault_deposit_address(private_channel_id: u64) -> String {
+///
+/// Returns a tuple of the previous and current BTC vault deposit addresses.
+/// Note: If there is no previous key, the current address is returned for both the previous and
+/// current addresses.
+pub fn derive_btc_vault_deposit_addresses(private_channel_id: u64) -> (String, String) {
 	let EpochKey { key, .. } = BitcoinThresholdSigner::active_epoch_key()
 		.expect("We should always have a key for the current epoch.");
-	DepositAddress::new(
+
+	let current = DepositAddress::new(
 		key.current,
 		private_channel_id.try_into().expect("Private channel id out of bounds."),
 	)
 	.script_pubkey()
-	.to_address(&Environment::network_environment().into())
+	.to_address(&Environment::network_environment().into());
path: state-chain/runtime/src/chainflip/address_derivation/btc.rs
user: login: dandanlen
body: nit: we call `Environment::network_environment().into()` twice, could just put it in a variable.
created_at: 2025-03-10T16:22:02Z
author_association: COLLABORATOR
diff_hunk: @@ -46,18 +46,36 @@ impl AddressDerivationApi<Bitcoin> for AddressDerivation {
 
 /// ONLY FOR USE IN RPC CALLS.
 ///
-/// Derives the BTC vault deposit address from the private channel id.
+/// Derives the current and previous BTC vault deposit addresses from the private channel id.
 /// Note: This function will **panic** if the private channel id is out of bounds or if there is
 /// no active epoch key for Bitcoin.
-pub(crate) fn derive_btc_vault_deposit_address(private_channel_id: u64) -> String {
+///
+/// Returns a tuple of the previous and current BTC vault deposit addresses.
+/// Note: If there is no previous key, the current address is returned for both the previous and
+/// current addresses.
+pub fn derive_btc_vault_deposit_addresses(private_channel_id: u64) -> (String, String) {
 	let EpochKey { key, .. } = BitcoinThresholdSigner::active_epoch_key()
 		.expect("We should always have a key for the current epoch.");
-	DepositAddress::new(
+
+	let current = DepositAddress::new(
 		key.current,
 		private_channel_id.try_into().expect("Private channel id out of bounds."),
 	)
 	.script_pubkey()
-	.to_address(&Environment::network_environment().into())
+	.to_address(&Environment::network_environment().into());
+
+	let previous = if let Some(previous) = key.previous {
+		DepositAddress::new(
+			previous,
+			private_channel_id.try_into().expect("Private channel id out of bounds."),
path: state-chain/runtime/src/chainflip/address_derivation/btc.rs
user: login: dandanlen
body: Same with `private_channel_id.try_into().expect("Private channel id out of bounds.")`, it's duplicated. 
created_at: 2025-03-10T16:22:47Z
author_association: COLLABORATOR
diff_hunk: @@ -95,7 +95,7 @@ pub fn bitcoin_vault_swap(
 
 	Ok(VaultSwapDetails::Bitcoin {
 		nulldata_payload: encode_swap_params_in_nulldata_payload(params),
-		deposit_address: derive_btc_vault_deposit_address(private_channel_id),
+		deposit_address: derive_btc_vault_deposit_addresses(private_channel_id).1,
path: state-chain/runtime/src/chainflip/vault_swaps.rs
user: login: dandanlen
body: Maybe there's a better way to ensure that this is correct? Right now I have to read the implementation of `derive_btc_vault_deposit_addresses` to know what the `.1` represents. 

*edit: see my other comment re. returning a struct instead of a tuple*
created_at: 2025-03-10T17:06:25Z
author_association: COLLABORATOR
diff_hunk: @@ -2529,6 +2529,23 @@ impl_runtime_apis! {
 				pallet_cf_swapping::AffiliateAccountDetails::<Runtime>::iter_prefix(&broker).collect()
 			}
 		}
+
+		fn cf_vault_addresses() -> VaultAddresses<EncodedAddress> {
+			// By passing 0 we get the acutal vault address.
+			let (previous, current) = derive_btc_vault_deposit_addresses(0);
path: state-chain/runtime/src/lib.rs
user: login: dandanlen
body: I'm pretty sure the vault (index 0) isn't monitored by the engines. 
created_at: 2025-03-10T17:07:58Z
author_association: COLLABORATOR
diff_hunk: @@ -344,6 +344,47 @@ pub struct TransactionScreeningEvents {
 	pub arb_events: Vec<BrokerRejectionEventFor<cf_chains::Arbitrum>>,
 }
 
+/// Struct that represents the BTC deposit addresses for a given private channel id.
+#[derive(Encode, Decode, TypeInfo, Serialize, Deserialize, Clone)]
+pub struct BtcDepositAddresses<AddressType> {
+	pub previous: AddressType,
+	pub current: AddressType,
+}
+
+#[derive(Encode, Decode, TypeInfo, Serialize, Deserialize, Clone)]
+pub struct VaultAddresses<AddressType> {
+	pub ethereum_vault: AddressType,
+	pub arbitrum_vault: AddressType,
+	pub bitcoin_vault: BtcDepositAddresses<AddressType>,
+	pub bitcoin_deposit_addresses: BTreeMap<AccountId32, BtcDepositAddresses<AddressType>>,
path: state-chain/runtime/src/runtime_apis.rs
user: login: dandanlen
body: As noted above, there is no `bitcoin_vault`, we only witness the broker private channel addresses. I don't think the deposit monitor cares about who owns them either, or which is previous/current (maybe @MxmUrw can confirm).

I think we can simplify to this:

```
struct VaultAddresses<AddressType> {
	pub ethereum: AddressType,
	pub arbitrum: AddressType,
	pub bitcoin: Vec<AddressType>,
}
```

(also, nit: we don't need the `Type` suffix. We already know it's a type.)


*edit: Actually, thinking about it again, I can see why the deposit monitor might need to know the owner - in that case I would suggest Vec<(AccountId, AddressType)>.*
created_at: 2025-03-10T17:13:21Z
author_association: COLLABORATOR
diff_hunk: @@ -156,6 +157,9 @@ pub trait Rpc {
 		&self,
 		affiliate_account_id: AccountId32,
 	) -> RpcResult<WithdrawFeesDetail>;
+
+	#[method(name = "vault_addresses", aliases = ["broker_getVaultAddresses"])]
path: api/bin/chainflip-broker-api/src/main.rs
user: login: dandanlen
body: Would suggest either this:

```
#[method(name = "vault_addresses", aliases = ["broker_VaultAddresses"])]
```

Or this:

```
#[method(name = "get_vault_addresses", aliases = ["broker_getVaultAddresses"])]
```

created_at: 2025-03-10T17:15:39Z
author_association: COLLABORATOR
diff_hunk: @@ -344,6 +344,47 @@ pub struct TransactionScreeningEvents {
 	pub arb_events: Vec<BrokerRejectionEventFor<cf_chains::Arbitrum>>,
 }
 
+/// Struct that represents the BTC deposit addresses for a given private channel id.
+#[derive(Encode, Decode, TypeInfo, Serialize, Deserialize, Clone)]
+pub struct BtcDepositAddresses<AddressType> {
+	pub previous: AddressType,
+	pub current: AddressType,
+}
+
+#[derive(Encode, Decode, TypeInfo, Serialize, Deserialize, Clone)]
+pub struct VaultAddresses<AddressType> {
+	pub ethereum_vault: AddressType,
+	pub arbitrum_vault: AddressType,
+	pub bitcoin_vault: BtcDepositAddresses<AddressType>,
+	pub bitcoin_deposit_addresses: BTreeMap<AccountId32, BtcDepositAddresses<AddressType>>,
path: state-chain/runtime/src/runtime_apis.rs
user: login: MxmUrw
body: Yep, the deposit monitor needs to know the owner in order to be able to filter the addresses according to it's configuration.
created_at: 2025-03-11T16:38:32Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-07T11:21:59Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are disabled on this repository.
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
The changes add new functionality for retrieving vault addresses across multiple components. A new `VaultAddresses` struct and its related asynchronous RPC method are introduced in the broker API. In parallel, a custom RPC method `cf_vault_addresses` is added to the state chain for retrieving encoded vault addresses, with an accompanying public change for BTC address derivation. Additionally, runtime APIs have been extended to include the new vault address retrieval method, allowing clients to query Ethereum, Arbitrum, and Bitcoin vault details.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `api/bin/chainflip-broker-api/src/main.rs` | Added `VaultAddresses` struct with `ethereum`, `arbitrum`, and `bitcoin` fields; implemented asynchronous `vault_addresses` method in the `Rpc` trait and its implementation in `RpcServerImpl`. |
| `state-chain/custom-rpc/src/lib.rs` | Introduced the `cf_vault_addresses` method in the `CustomApi` trait (with the attribute `#[method(name = "vault_addresses")]`) that returns a tuple of three `EncodedAddress` values; updated imports to include `EncodedAddress`. |
| `state-chain/runtime/src/lib.rs`, <br> `state-chain/runtime/src/runtime_apis.rs` | Added a new runtime API function `cf_vault_addresses` to return vault addresses for Ethereum, Arbitrum, and Bitcoin, and declared it in the `CustomRuntimeApi` trait. |
| `state-chain/runtime/src/chainflip/address_derivation/btc.rs` | Changed the visibility of the `derive_btc_vault_deposit_address` function from `pub(crate)` to `pub`. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant C as Client
    participant RS as RpcServerImpl
    participant AC as API Client

    C->>RS: Call vault_addresses()
    RS->>AC: Retrieve vault addresses
    AC-->>RS: Return (ethereum, arbitrum, bitcoin)
    RS-->>C: Respond with VaultAddresses struct
```

```mermaid
sequenceDiagram
    participant C as Client
    participant CA as CustomApi
    participant RT as Runtime API
    participant ENV as Environment/Address Derivation

    C->>CA: Call cf_vault_addresses(at)
    CA->>RT: Invoke cf_vault_addresses()
    RT->>ENV: Get Ethereum address (eth_vault_address)
    RT->>ENV: Get Arbitrum address (arb_vault_address)
    RT->>ENV: Call derive_btc_vault_deposit_address(0)
    ENV-->>RT: Return respective addresses
    RT-->>CA: Respond with tuple (EncodedAddress, EncodedAddress, EncodedAddress)
    CA-->>C: Return vault addresses
```

## Suggested reviewers
- ahasna
- martin-chainflip
- dandanlen

## Poem
> I'm a rabbit in a code-filled glade,  
> Vault addresses hop in the light and shade.  
> Ethereum, Arbitrum, Bitcoin align,  
> In joyful bytes they truly shine.  
> Leaping through changes with a happy rhyme,  
> Celebrating each commit, one hop at a time!  
> 🐇✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=chainflip-io/chainflip-backend&utm_content=5711):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: codecov[bot]
created_at: 2025-03-07T11:30:58Z
body: ## [Codecov](https://app.codecov.io/gh/chainflip-io/chainflip-backend/pull/5711?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chainflip-io) Report
Attention: Patch coverage is `18.64407%` with `48 lines` in your changes missing coverage. Please review.
| [Files with missing lines](https://app.codecov.io/gh/chainflip-io/chainflip-backend/pull/5711?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chainflip-io) | Patch % | Lines |
|---|---|---|
| [...in/runtime/src/chainflip/address\_derivation/btc.rs](https://app.codecov.io/gh/chainflip-io/chainflip-backend/pull/5711?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chainflip-io#diff-c3RhdGUtY2hhaW4vcnVudGltZS9zcmMvY2hhaW5mbGlwL2FkZHJlc3NfZGVyaXZhdGlvbi9idGMucnM=) | 0% | [32 Missing :warning: ](https://app.codecov.io/gh/chainflip-io/chainflip-backend/pull/5711?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chainflip-io) |
| [state-chain/runtime/src/lib.rs](https://app.codecov.io/gh/chainflip-io/chainflip-backend/pull/5711?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chainflip-io#diff-c3RhdGUtY2hhaW4vcnVudGltZS9zcmMvbGliLnJz) | 6% | [0 Missing and 15 partials :warning: ](https://app.codecov.io/gh/chainflip-io/chainflip-backend/pull/5711?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chainflip-io) |
| [state-chain/runtime/src/chainflip/vault\_swaps.rs](https://app.codecov.io/gh/chainflip-io/chainflip-backend/pull/5711?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chainflip-io#diff-c3RhdGUtY2hhaW4vcnVudGltZS9zcmMvY2hhaW5mbGlwL3ZhdWx0X3N3YXBzLnJz) | 0% | [1 Missing :warning: ](https://app.codecov.io/gh/chainflip-io/chainflip-backend/pull/5711?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chainflip-io) |

:loudspeaker: Thoughts on this report? [Let us know!](https://github.com/codecov/feedback/issues/255)
author_association: NONE
user: login: Janislav
created_at: 2025-03-07T15:47:22Z
body: @coderabbitai review
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-03-07T15:47:28Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>
author_association: NONE
user: login: Janislav
created_at: 2025-03-13T09:46:32Z
body: @dandanlen I added all of your points.
author_association: CONTRIBUTOR
user: login: dandanlen
created_at: 2025-03-13T14:02:42Z
body: @MxmUrw does the interface look ok to you? 
author_association: COLLABORATOR
user: login: MxmUrw
created_at: 2025-03-13T14:18:12Z
body: > @MxmUrw does the interface look ok to you?

Yes, looks good!
author_association: CONTRIBUTOR