From 47ed548eb9ad47f74cba2ebcbb3a4f858bf66ca0 Mon Sep 17 00:00:00 2001 From: Janislav Date: Thu, 12 Dec 2024 11:07:12 +0100 Subject: [PATCH] feat: Add private channel address to account info rpc (#5489) * feature: Added channel address to cf-account-info * refactor: derive address from channel id * refactor: renamed channel_address to deposit_address * chore: renamed deposit_address -> btc_vault_deposit_address * chore: moved btc address derivation to own function * chore: improved comment * chore: refactored cf_broker_info --- state-chain/custom-rpc/src/lib.rs | 33 ++++++++++------- ...ustom_rpc__test__broker_serialization.snap | 5 +-- .../src/chainflip/address_derivation/btc.rs | 18 +++++++++- state-chain/runtime/src/lib.rs | 36 ++++++------------- state-chain/runtime/src/runtime_apis.rs | 2 ++ 5 files changed, 54 insertions(+), 40 deletions(-) diff --git a/state-chain/custom-rpc/src/lib.rs b/state-chain/custom-rpc/src/lib.rs index 3df1c001af..b4cffcda4a 100644 --- a/state-chain/custom-rpc/src/lib.rs +++ b/state-chain/custom-rpc/src/lib.rs @@ -14,9 +14,9 @@ use cf_chains::{ }; use cf_primitives::{ chains::assets::any::{self, AssetMap}, - AccountRole, AffiliateShortId, Affiliates, Asset, AssetAmount, BasisPoints, BlockNumber, - BroadcastId, DcaParameters, EpochIndex, ForeignChain, NetworkEnvironment, SemVer, SwapId, - SwapRequestId, + AccountId, AccountRole, AffiliateShortId, Affiliates, Asset, AssetAmount, BasisPoints, + BlockNumber, BroadcastId, DcaParameters, EpochIndex, ForeignChain, NetworkEnvironment, SemVer, + SwapId, SwapRequestId, }; use cf_utilities::rpc::NumberOrHex; use core::ops::Range; @@ -259,6 +259,8 @@ pub enum RpcAccountInfo { Broker { flip_balance: NumberOrHex, earned_fees: any::AssetMap, + affiliates: Vec<(AffiliateShortId, AccountId)>, + btc_vault_deposit_address: Option, }, LiquidityProvider { balances: any::AssetMap, @@ -289,15 +291,17 @@ impl RpcAccountInfo { Self::Unregistered { flip_balance: balance.into() } } - fn broker(balance: u128, broker_info: BrokerInfo) -> Self { + fn broker(broker_info: BrokerInfo, balance: u128) -> Self { Self::Broker { flip_balance: balance.into(), + btc_vault_deposit_address: broker_info.btc_vault_deposit_address, earned_fees: cf_chains::assets::any::AssetMap::from_iter_or_default( broker_info .earned_fees .iter() .map(|(asset, balance)| (*asset, (*balance).into())), ), + affiliates: broker_info.affiliates, } } @@ -1388,7 +1392,7 @@ where AccountRole::Broker => { let info = api.cf_broker_info(hash, account_id)?; - RpcAccountInfo::broker(balance, info) + RpcAccountInfo::broker(info, balance) }, AccountRole::LiquidityProvider => { let info = api.cf_liquidity_provider_info(hash, account_id)?; @@ -2145,7 +2149,7 @@ mod test { use std::collections::BTreeSet; use super::*; - use cf_chains::assets::sol; + use cf_chains::{assets::sol, btc::ScriptPubkey}; use cf_primitives::{ chains::assets::{any, arb, btc, dot, eth}, FLIPPERINOS_PER_FLIP, @@ -2170,8 +2174,8 @@ mod test { #[test] fn test_broker_serialization() { - insta::assert_snapshot!(serde_json::to_value(RpcAccountInfo::broker( - 0, + use cf_chains::btc::BitcoinNetwork; + let broker = RpcAccountInfo::broker( BrokerInfo { earned_fees: vec![ (Asset::Eth, 0), @@ -2184,10 +2188,15 @@ mod test { (Asset::ArbUsdc, 0), (Asset::Sol, 0), (Asset::SolUsdc, 0), - ] - } - )) - .unwrap()); + ], + btc_vault_deposit_address: Some( + ScriptPubkey::Taproot([1u8; 32]).to_address(&BitcoinNetwork::Testnet), + ), + affiliates: vec![(cf_primitives::AffiliateShortId(1), AccountId32::new([1; 32]))], + }, + 0, + ); + insta::assert_snapshot!(serde_json::to_value(broker).unwrap()); } #[test] diff --git a/state-chain/custom-rpc/src/snapshots/custom_rpc__test__broker_serialization.snap b/state-chain/custom-rpc/src/snapshots/custom_rpc__test__broker_serialization.snap index 36cc2d96b3..1ff3c47ac8 100644 --- a/state-chain/custom-rpc/src/snapshots/custom_rpc__test__broker_serialization.snap +++ b/state-chain/custom-rpc/src/snapshots/custom_rpc__test__broker_serialization.snap @@ -1,5 +1,6 @@ --- source: state-chain/custom-rpc/src/lib.rs -expression: "serde_json::to_value(RpcAccountInfo::broker(0,\n BrokerInfo {\n earned_fees: vec![(Asset::Eth, 0), (Asset::Btc, 0),\n (Asset::Flip, 1000000000000000000), (Asset::Usdc, 0),\n (Asset::Usdt, 0), (Asset::Dot, 0), (Asset::ArbEth, 0),\n (Asset::ArbUsdc, 0), (Asset::Sol, 0), (Asset::SolUsdc, 0),],\n })).unwrap()" +expression: "serde_json::to_value(broker).unwrap()" +snapshot_kind: text --- -{"earned_fees":{"Arbitrum":{"ETH":"0x0","USDC":"0x0"},"Bitcoin":{"BTC":"0x0"},"Ethereum":{"ETH":"0x0","FLIP":"0xde0b6b3a7640000","USDC":"0x0","USDT":"0x0"},"Polkadot":{"DOT":"0x0"},"Solana":{"SOL":"0x0","USDC":"0x0"}},"flip_balance":"0x0","role":"broker"} +{"affiliates":[[1,"5C62Ck4UrFPiBtoCmeSrgF7x9yv9mn38446dhCpsi2mLHiFT"]],"btc_vault_deposit_address":"tb1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsn60vlk","earned_fees":{"Arbitrum":{"ETH":"0x0","USDC":"0x0"},"Bitcoin":{"BTC":"0x0"},"Ethereum":{"ETH":"0x0","FLIP":"0xde0b6b3a7640000","USDC":"0x0","USDT":"0x0"},"Polkadot":{"DOT":"0x0"},"Solana":{"SOL":"0x0","USDC":"0x0"}},"flip_balance":"0x0","role":"broker"} diff --git a/state-chain/runtime/src/chainflip/address_derivation/btc.rs b/state-chain/runtime/src/chainflip/address_derivation/btc.rs index 97908176ea..273bec6b29 100644 --- a/state-chain/runtime/src/chainflip/address_derivation/btc.rs +++ b/state-chain/runtime/src/chainflip/address_derivation/btc.rs @@ -1,5 +1,5 @@ use super::AddressDerivation; -use crate::BitcoinThresholdSigner; +use crate::{BitcoinThresholdSigner, Environment, EpochKey, String}; use cf_chains::{ address::{AddressDerivationApi, AddressDerivationError}, btc::deposit_address::DepositAddress, @@ -44,6 +44,22 @@ impl AddressDerivationApi for AddressDerivation { } } +/// ONLY FOR USE IN RPC CALLS. +/// +/// 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 { + 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()) +} + #[test] fn test_address_generation() { use crate::Runtime; diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index fc321d161e..fd04b53dda 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -11,6 +11,7 @@ pub mod test_runner; mod weights; use crate::{ chainflip::{ + address_derivation::btc::derive_btc_vault_deposit_address, calculate_account_apy, solana_elections::{ SolanaChainTrackingProvider, SolanaEgressWitnessingTrigger, SolanaIngress, @@ -79,7 +80,7 @@ use pallet_cf_pools::{ AskBidMap, AssetPair, HistoricalEarnedFees, OrderId, PoolLiquidity, PoolOrderbook, PoolPriceV1, PoolPriceV2, UnidirectionalPoolDepth, }; -use pallet_cf_swapping::{BatchExecutionError, FeeType, Swap}; +use pallet_cf_swapping::{BatchExecutionError, BrokerPrivateBtcChannels, FeeType, Swap}; use runtime_apis::ChainAccounts; use crate::{chainflip::EvmLimit, runtime_apis::TransactionScreeningEvent}; @@ -1905,11 +1906,14 @@ impl_runtime_apis! { fn cf_broker_info( account_id: AccountId, ) -> BrokerInfo { - let earned_fees = Asset::all().map(|asset| - (asset, AssetBalances::get_balance(&account_id, asset)) - ).collect(); - - BrokerInfo { earned_fees } + BrokerInfo { + earned_fees: Asset::all().map(|asset| + (asset, AssetBalances::get_balance(&account_id, asset)) + ).collect(), + btc_vault_deposit_address: BrokerPrivateBtcChannels::::get(&account_id) + .map(derive_btc_vault_deposit_address), + affiliates: pallet_cf_swapping::AffiliateIdMapping::::iter_prefix(&account_id).collect(), + } } fn cf_account_role(account_id: AccountId) -> Option { @@ -2177,8 +2181,6 @@ impl_runtime_apis! { // Encode swap match ForeignChain::from(source_asset) { ForeignChain::Bitcoin => { - use cf_chains::btc::deposit_address::DepositAddress; - let private_channel_id = pallet_cf_swapping::BrokerPrivateBtcChannels::::get(&broker_id) .ok_or( @@ -2221,25 +2223,9 @@ impl_runtime_apis! { }, }; - let EpochKey { key, .. } = BitcoinThresholdSigner::active_epoch_key() - .expect("We should always have a key for the current epoch."); - let deposit_address = DepositAddress::new( - key.current, - private_channel_id.try_into().map_err( - // TODO: Ensure this can't happen. - |_| { - DispatchErrorWithMessage::Other( - "Private channel id out of bounds.".into(), - ) - }, - )?, - ) - .script_pubkey() - .to_address(&Environment::network_environment().into()); - Ok(VaultSwapDetails::Bitcoin { nulldata_payload: encode_swap_params_in_nulldata_payload(params), - deposit_address, + deposit_address: derive_btc_vault_deposit_address(private_channel_id), }) }, _ => Err(pallet_cf_swapping::Error::::UnsupportedSourceAsset.into()), diff --git a/state-chain/runtime/src/runtime_apis.rs b/state-chain/runtime/src/runtime_apis.rs index 48808c30c3..b8caefcb0d 100644 --- a/state-chain/runtime/src/runtime_apis.rs +++ b/state-chain/runtime/src/runtime_apis.rs @@ -161,6 +161,8 @@ pub struct LiquidityProviderInfo { #[derive(Encode, Decode, Eq, PartialEq, TypeInfo)] pub struct BrokerInfo { pub earned_fees: Vec<(Asset, AssetAmount)>, + pub btc_vault_deposit_address: Option, + pub affiliates: Vec<(AffiliateShortId, AccountId32)>, } /// Struct that represents the estimated output of a Swap.