Skip to content

Commit

Permalink
funds-manager: custody-client: Add endpoint for creating gas wallets
Browse files Browse the repository at this point in the history
  • Loading branch information
joeykraut committed Aug 2, 2024
1 parent 0d4dd07 commit 1c53a62
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 27 deletions.
8 changes: 7 additions & 1 deletion funds-manager/funds-manager-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ pub struct WithdrawFundsRequest {

// --- Gas --- //

// Update request body name and documentation
/// The request body for withdrawing gas from custody
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WithdrawGasRequest {
Expand All @@ -89,6 +88,13 @@ pub struct WithdrawGasRequest {
pub destination_address: String,
}

/// The response containing the gas wallet's address
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateGasWalletResponse {
/// The address of the gas wallet
pub address: String,
}

// --- Hot Wallets --- //

/// The request body for creating a hot wallet
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! Handlers for gas wallet operations

use ethers::{
signers::{LocalWallet, Signer},
utils::hex::ToHexExt,
};
use rand::thread_rng;
use tracing::info;

use crate::{error::FundsManagerError, helpers::create_secrets_manager_entry_with_description};

use super::CustodyClient;

impl CustodyClient {
/// Create a new gas wallet
pub(crate) async fn create_gas_wallet(&self) -> Result<String, FundsManagerError> {
// Sample a new ethereum keypair
let keypair = LocalWallet::new(&mut thread_rng());
let address = keypair.address().encode_hex();

// Add the gas wallet to the database
self.add_gas_wallet(&address).await?;

// Store the private key in secrets manager
let secret_name = Self::gas_wallet_secret_name(&address);
let private_key = keypair.signer().to_bytes();
let secret_value = hex::encode(private_key);
let description = "Gas wallet private key for use by Renegade relayers";
create_secrets_manager_entry_with_description(
&secret_name,
&secret_value,
&self.aws_config,
description,
)
.await?;
info!("Created gas wallet with address: {}", address);

Ok(address)
}

/// Get the secret name for a gas wallet's private key
fn gas_wallet_secret_name(address: &str) -> String {
format!("gas-wallet-{}", address)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Manages the custody backend for the funds manager
#![allow(missing_docs)]
pub mod deposit;
pub mod gas_wallets;
mod hot_wallets;
mod queries;
pub mod withdraw;
Expand Down
20 changes: 19 additions & 1 deletion funds-manager/funds-manager-server/src/custody_client/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,30 @@ use diesel_async::RunQueryDsl;
use renegade_util::err_str;
use uuid::Uuid;

use crate::db::models::HotWallet;
use crate::db::models::{GasWallet, HotWallet};
use crate::db::schema::gas_wallets;
use crate::db::schema::hot_wallets;
use crate::error::FundsManagerError;
use crate::CustodyClient;

impl CustodyClient {
// --- Gas Wallets --- //

/// Add a new gas wallet
pub async fn add_gas_wallet(&self, address: &str) -> Result<(), FundsManagerError> {
let mut conn = self.get_db_conn().await?;
let entry = GasWallet::new(address.to_string());
diesel::insert_into(gas_wallets::table)
.values(entry)
.execute(&mut conn)
.await
.map_err(err_str!(FundsManagerError::Db))?;

Ok(())
}

// --- Hot Wallets --- //

/// Get all hot wallets
pub async fn get_all_hot_wallets(&self) -> Result<Vec<HotWallet>, FundsManagerError> {
let mut conn = self.get_db_conn().await?;
Expand Down
9 changes: 9 additions & 0 deletions funds-manager/funds-manager-server/src/db/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,12 @@ pub struct GasWallet {
pub status: String,
pub created_at: SystemTime,
}

impl GasWallet {
/// Construct a new gas wallet
pub fn new(address: String) -> Self {
let id = Uuid::new_v4();
let status = GasWalletStatus::Inactive.to_string();
GasWallet { id, address, peer_id: None, status, created_at: SystemTime::now() }
}
}
61 changes: 40 additions & 21 deletions funds-manager/funds-manager-server/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use crate::error::ApiError;
use crate::Server;
use bytes::Bytes;
use funds_manager_api::{
CreateHotWalletRequest, CreateHotWalletResponse, DepositAddressResponse, FeeWalletsResponse,
HotWalletBalancesResponse, TransferToVaultRequest, WithdrawFeeBalanceRequest,
WithdrawFundsRequest, WithdrawGasRequest, WithdrawToHotWalletRequest,
CreateGasWalletResponse, CreateHotWalletRequest, CreateHotWalletResponse,
DepositAddressResponse, FeeWalletsResponse, HotWalletBalancesResponse, TransferToVaultRequest,
WithdrawFeeBalanceRequest, WithdrawFundsRequest, WithdrawGasRequest,
WithdrawToHotWalletRequest,
};
use itertools::Itertools;
use std::collections::HashMap;
Expand All @@ -21,6 +22,8 @@ pub const GAS_ASSET_NAME: &str = "ETH";
/// The maximum amount of gas that can be withdrawn at a given time
pub const MAX_GAS_WITHDRAWAL_AMOUNT: f64 = 0.1; // 0.1 ETH

// --- Fee Indexing --- //

/// Handler for indexing fees
pub(crate) async fn index_fees_handler(server: Arc<Server>) -> Result<Json, warp::Rejection> {
let mut indexer = server
Expand All @@ -45,6 +48,32 @@ pub(crate) async fn redeem_fees_handler(server: Arc<Server>) -> Result<Json, war
Ok(warp::reply::json(&"Fees redeemed successfully"))
}

/// Handler for getting fee wallets
pub(crate) async fn get_fee_wallets_handler(
_body: Bytes, // no body
server: Arc<Server>,
) -> Result<Json, warp::Rejection> {
let mut indexer = server.build_indexer()?;
let wallets = indexer.fetch_fee_wallets().await?;
Ok(warp::reply::json(&FeeWalletsResponse { wallets }))
}

/// Handler for withdrawing a fee balance
pub(crate) async fn withdraw_fee_balance_handler(
req: WithdrawFeeBalanceRequest,
server: Arc<Server>,
) -> Result<Json, warp::Rejection> {
let mut indexer = server.build_indexer()?;
indexer
.withdraw_fee_balance(req.wallet_id, req.mint)
.await
.map_err(|e| warp::reject::custom(ApiError::InternalError(e.to_string())))?;

Ok(warp::reply::json(&"Fee withdrawal initiated..."))
}

// --- Quoters --- //

/// Handler for withdrawing funds from custody
pub(crate) async fn quoter_withdraw_handler(
withdraw_request: WithdrawFundsRequest,
Expand Down Expand Up @@ -77,6 +106,8 @@ pub(crate) async fn get_deposit_address_handler(
Ok(warp::reply::json(&resp))
}

// --- Gas --- //

/// Handler for withdrawing gas from custody
pub(crate) async fn withdraw_gas_handler(
withdraw_request: WithdrawGasRequest,
Expand All @@ -98,29 +129,17 @@ pub(crate) async fn withdraw_gas_handler(
Ok(warp::reply::json(&format!("Gas withdrawal of {} ETH complete", withdraw_request.amount)))
}

/// Handler for getting fee wallets
pub(crate) async fn get_fee_wallets_handler(
/// Handler for creating a new gas wallet
pub(crate) async fn create_gas_wallet_handler(
_body: Bytes, // no body
server: Arc<Server>,
) -> Result<Json, warp::Rejection> {
let mut indexer = server.build_indexer()?;
let wallets = indexer.fetch_fee_wallets().await?;
Ok(warp::reply::json(&FeeWalletsResponse { wallets }))
let address = server.custody_client.create_gas_wallet().await?;
let resp = CreateGasWalletResponse { address };
Ok(warp::reply::json(&resp))
}

/// Handler for withdrawing a fee balance
pub(crate) async fn withdraw_fee_balance_handler(
req: WithdrawFeeBalanceRequest,
server: Arc<Server>,
) -> Result<Json, warp::Rejection> {
let mut indexer = server.build_indexer()?;
indexer
.withdraw_fee_balance(req.wallet_id, req.mint)
.await
.map_err(|e| warp::reject::custom(ApiError::InternalError(e.to_string())))?;

Ok(warp::reply::json(&"Fee withdrawal initiated..."))
}
// --- Hot Wallets --- //

/// Handler for creating a hot wallet
pub(crate) async fn create_hot_wallet_handler(
Expand Down
16 changes: 12 additions & 4 deletions funds-manager/funds-manager-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ use funds_manager_api::{
WITHDRAW_FEE_BALANCE_ROUTE, WITHDRAW_GAS_ROUTE, WITHDRAW_TO_HOT_WALLET_ROUTE,
};
use handlers::{
create_hot_wallet_handler, get_deposit_address_handler, get_fee_wallets_handler,
get_hot_wallet_balances_handler, index_fees_handler, quoter_withdraw_handler,
redeem_fees_handler, transfer_to_vault_handler, withdraw_fee_balance_handler,
withdraw_from_vault_handler, withdraw_gas_handler,
create_gas_wallet_handler, create_hot_wallet_handler, get_deposit_address_handler,
get_fee_wallets_handler, get_hot_wallet_balances_handler, index_fees_handler,
quoter_withdraw_handler, redeem_fees_handler, transfer_to_vault_handler,
withdraw_fee_balance_handler, withdraw_from_vault_handler, withdraw_gas_handler,
};
use middleware::{identity, with_hmac_auth, with_json_body};
use relayer_client::RelayerClient;
Expand Down Expand Up @@ -340,6 +340,13 @@ async fn main() -> Result<(), Box<dyn Error>> {
.and(with_server(server.clone()))
.and_then(withdraw_gas_handler);

let add_gas_wallet = warp::post()
.and(warp::path("custody"))
.and(warp::path("gas-wallets"))
.and(with_hmac_auth(server.clone()))
.and(with_server(server.clone()))
.and_then(create_gas_wallet_handler);

// --- Hot Wallets --- //

let create_hot_wallet = warp::post()
Expand Down Expand Up @@ -385,6 +392,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
.or(withdraw_custody)
.or(get_deposit_address)
.or(withdraw_gas)
.or(add_gas_wallet)
.or(get_balances)
.or(withdraw_fee_balance)
.or(transfer_to_vault)
Expand Down

0 comments on commit 1c53a62

Please sign in to comment.