Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 80 additions & 22 deletions src/rumi_protocol_backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use ic_cdk_macros::{init, post_upgrade, query, update};
use rumi_protocol_backend::{
event::Event,
logs::INFO,
numeric::{ICUSD, UsdIcp},
numeric::{ICUSD, UsdIcp, UsdCkBtc},
state::{read_state, replace_state, Mode, State},
vault::{CandidVault, OpenVaultSuccess, VaultArg},
vault::{CandidVault, OpenVaultSuccess, VaultArg, CollateralType},
Fees, GetEventsArg, ProtocolArg, ProtocolError, ProtocolStatus, SuccessWithFee,
};
use rumi_protocol_backend::logs::DEBUG;
Expand All @@ -23,7 +23,6 @@ use rumi_protocol_backend::LiquidityStatus;
use candid_parser::utils::CandidSource;
use candid_parser::utils::service_equal;


#[cfg(feature = "self_check")]
fn ok_or_die(result: Result<(), String>) {
if let Err(msg) = result {
Expand Down Expand Up @@ -81,9 +80,15 @@ fn validate_mode() -> Result<(), ProtocolError> {
}

fn setup_timers() {
// Existing ICP rate fetching timer
ic_cdk_timers::set_timer_interval(rumi_protocol_backend::xrc::FETCHING_ICP_RATE_INTERVAL, || {
ic_cdk::spawn(rumi_protocol_backend::xrc::fetch_icp_rate())
});

// New ckBTC rate fetching timer
ic_cdk_timers::set_timer_interval(rumi_protocol_backend::xrc::FETCHING_CKBTC_RATE_INTERVAL, || {
ic_cdk::spawn(rumi_protocol_backend::xrc::fetch_ckbtc_rate())
});
}

fn main() {}
Expand Down Expand Up @@ -156,7 +161,13 @@ fn get_protocol_status() -> ProtocolStatus {
.unwrap_or(UsdIcp::from(Decimal::ZERO))
.to_f64(),
last_icp_timestamp: s.last_icp_timestamp.unwrap_or(0),
last_ckbtc_rate: s
.last_ckbtc_rate
.unwrap_or(UsdCkBtc::from(Decimal::ZERO))
.to_f64(),
last_ckbtc_timestamp: s.last_ckbtc_timestamp.unwrap_or(0),
total_icp_margin: s.total_icp_margin_amount().to_u64(),
total_ckbtc_margin: s.total_ckbtc_margin_amount().to_u64(),
total_icusd_borrowed: s.total_borrowed_icusd_amount().to_u64(),
total_collateral_ratio: s.total_collateral_ratio.to_f64(),
mode: s.mode,
Expand Down Expand Up @@ -235,7 +246,9 @@ fn get_vaults(target: Option<Principal>) -> Vec<CandidVault> {
owner: vault.owner,
borrowed_icusd_amount: vault.borrowed_icusd_amount.to_u64(),
icp_margin_amount: vault.icp_margin_amount.to_u64(),
ckbtc_margin_amount: vault.ckbtc_margin_amount.to_u64(),
vault_id: vault.vault_id,
collateral_type: vault.collateral_type,
}
})
.collect(),
Expand All @@ -248,7 +261,9 @@ fn get_vaults(target: Option<Principal>) -> Vec<CandidVault> {
owner: vault.owner,
borrowed_icusd_amount: vault.borrowed_icusd_amount.to_u64(),
icp_margin_amount: vault.icp_margin_amount.to_u64(),
ckbtc_margin_amount: vault.ckbtc_margin_amount.to_u64(),
vault_id: vault.vault_id,
collateral_type: vault.collateral_type,
})
.collect::<Vec<CandidVault>>()
}),
Expand All @@ -275,9 +290,9 @@ fn get_redemption_rate() -> f64 {

#[candid_method(update)]
#[update]
async fn open_vault(icp_margin: u64) -> Result<OpenVaultSuccess, ProtocolError> {
async fn open_vault(collateral_amount: u64, collateral_type: CollateralType) -> Result<OpenVaultSuccess, ProtocolError> {
validate_call()?;
check_postcondition(rumi_protocol_backend::vault::open_vault(icp_margin).await)
check_postcondition(rumi_protocol_backend::vault::open_vault(collateral_amount, collateral_type).await)
}

#[candid_method(update)]
Expand Down Expand Up @@ -338,31 +353,45 @@ async fn liquidate_vault(vault_id: u64) -> Result<SuccessWithFee, ProtocolError>
fn get_liquidatable_vaults() -> Vec<CandidVault> {
read_state(|s| {
let current_icp_rate = s.last_icp_rate.unwrap_or(UsdIcp::from(dec!(0.0)));
let current_ckbtc_rate = s.last_ckbtc_rate.unwrap_or(UsdCkBtc::from(dec!(0.0)));

if current_icp_rate.to_f64() == 0.0 {
if current_icp_rate.to_f64() == 0.0 && current_ckbtc_rate.to_f64() == 0.0 {
return vec![];
}

s.vault_id_to_vaults
.values()
.filter(|vault| {
let ratio = rumi_protocol_backend::compute_collateral_ratio(vault, current_icp_rate);
let ratio = match vault.collateral_type {
CollateralType::ICP => {
if current_icp_rate.to_f64() == 0.0 { return false; }
rumi_protocol_backend::compute_collateral_ratio(vault, current_icp_rate, CollateralType::ICP)
},
CollateralType::CkBTC => {
if current_ckbtc_rate.to_f64() == 0.0 { return false; }
rumi_protocol_backend::compute_collateral_ratio(vault, current_ckbtc_rate, CollateralType::CkBTC)
}
};
ratio < s.mode.get_minimum_liquidation_collateral_ratio()
})
.map(|vault| {
let collateral_ratio = rumi_protocol_backend::compute_collateral_ratio(vault, current_icp_rate);
let collateral_ratio = match vault.collateral_type {
CollateralType::ICP => rumi_protocol_backend::compute_collateral_ratio(vault, current_icp_rate, CollateralType::ICP),
CollateralType::CkBTC => rumi_protocol_backend::compute_collateral_ratio(vault, current_ckbtc_rate, CollateralType::CkBTC),
};
CandidVault {
owner: vault.owner,
borrowed_icusd_amount: vault.borrowed_icusd_amount.to_u64(),
icp_margin_amount: vault.icp_margin_amount.to_u64(),
ckbtc_margin_amount: vault.ckbtc_margin_amount.to_u64(),
vault_id: vault.vault_id,
collateral_type: vault.collateral_type,
}
})
.collect::<Vec<CandidVault>>()
})
}


// Liquidity related operations
#[candid_method(update)]
#[update]
Expand Down Expand Up @@ -457,21 +486,38 @@ fn http_request(req: HttpRequest) -> HttpResponse {
"ICP rate.",
)?;

w.encode_gauge(
"rumi_ckbtc_rate",
s.last_ckbtc_rate.unwrap_or(UsdCkBtc::from(dec!(0))).to_f64(),
"ckBTC rate.",
)?;

let total_icp_dec = Decimal::from_u64(s.total_icp_margin_amount().0)
.expect("failed to construct decimal from u64")
/ dec!(100_000_000);

let total_ckbtc_dec = Decimal::from_u64(s.total_ckbtc_margin_amount().0)
.expect("failed to construct decimal from u64")
/ dec!(100_000_000);

w.encode_gauge(
"icp_total_ICP_margin",
total_icp_dec.to_f64().unwrap(),
"Total ICP Margin.",
)?;

w.encode_gauge(
"ICP_total_tvl",
(total_icp_dec * s.last_icp_rate.unwrap_or(UsdIcp::from(dec!(0))).0)
.to_f64()
.unwrap(),
"ckbtc_total_CKBTC_margin",
total_ckbtc_dec.to_f64().unwrap(),
"Total ckBTC Margin.",
)?;

let total_tvl = (total_icp_dec * s.last_icp_rate.unwrap_or(UsdIcp::from(dec!(0))).0)
+ (total_ckbtc_dec * s.last_ckbtc_rate.unwrap_or(UsdCkBtc::from(dec!(0))).0);

w.encode_gauge(
"total_tvl",
total_tvl.to_f64().unwrap(),
"Total TVL.",
)?;

Expand All @@ -486,7 +532,7 @@ fn http_request(req: HttpRequest) -> HttpResponse {
)?;

w.encode_gauge(
"ICP_total_collateral_ratio",
"total_collateral_ratio",
s.total_collateral_ratio.to_f64(),
"TCR.",
)?;
Expand Down Expand Up @@ -597,14 +643,27 @@ async fn recover_pending_transfer(vault_id: u64) -> Result<bool, ProtocolError>
});

if let Some(transfer) = transfer_opt {
let icp_transfer_fee = read_state(|s| s.icp_ledger_fee);
let transfer_fee = match transfer.collateral_type {
CollateralType::ICP => read_state(|s| s.icp_ledger_fee),
CollateralType::CkBTC => read_state(|s| s.ckbtc_ledger_fee),
};

match crate::management::transfer_icp(
transfer.margin - icp_transfer_fee,
transfer.owner,
)
.await
{
let result = match transfer.collateral_type {
CollateralType::ICP => {
crate::management::transfer_icp(
transfer.margin - transfer_fee,
transfer.owner,
).await
},
CollateralType::CkBTC => {
crate::management::transfer_ckbtc(
transfer.margin - transfer_fee,
transfer.owner,
).await
}
};

match result {
Ok(block_index) => {
mutate_state(|s| crate::event::record_margin_transfer(s, vault_id, block_index));
Ok(true)
Expand Down Expand Up @@ -637,7 +696,6 @@ fn check_candid_interface_compatibility() {
}
}


fn check_service_compatible(
new_name: &str,
new: CandidSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
let protocolStatus = {
mode: 'GeneralAvailability',
totalIcpMargin: 0,
totalCkbtcMargin: 0,
totalIcusdBorrowed: 0,
lastIcpRate: 0,
lastIcpTimestamp: 0,
lastCkbtcRate: 0,
lastCkbtcTimestamp: 0,
totalCollateralRatio: 0
};

Expand All @@ -27,9 +30,12 @@
protocolStatus = {
mode: status.mode || 'GeneralAvailability',
totalIcpMargin: Number(status.totalIcpMargin || 0),
totalCkbtcMargin: Number(status.totalCkbtcMargin || 0),
totalIcusdBorrowed: Number(status.totalIcusdBorrowed || 0),
lastIcpRate: Number(status.lastIcpRate || 0),
lastIcpTimestamp: Number(status.lastIcpTimestamp || 0),
lastCkbtcRate: Number(status.lastCkbtcRate || 0),
lastCkbtcTimestamp: Number(status.lastCkbtcTimestamp || 0),
totalCollateralRatio: Number(status.totalCollateralRatio || 0)
};

Expand Down Expand Up @@ -67,9 +73,11 @@
});

$: icpValueInUsd = protocolStatus.totalIcpMargin * protocolStatus.lastIcpRate;
$: ckbtcValueInUsd = protocolStatus.totalCkbtcMargin * protocolStatus.lastCkbtcRate;
$: totalCollateralUsd = icpValueInUsd + ckbtcValueInUsd;
$: collateralPercent = protocolStatus.totalIcusdBorrowed > 0
? protocolStatus.totalCollateralRatio * 100
: protocolStatus.totalIcpMargin > 0
: (protocolStatus.totalIcpMargin > 0 || protocolStatus.totalCkbtcMargin > 0)
? Infinity
: 0;

Expand All @@ -93,32 +101,48 @@
}[protocolStatus.mode] || 'text-gray-500';
</script>

<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Total Collateral Value -->
<div class="stat-card">
<div class="text-sm text-gray-400">Total Collateral (ICP)</div>
<div class="text-xl font-bold">{formatNumber(protocolStatus.totalIcpMargin)} ICP</div>
<div class="text-sm text-gray-400">β‰ˆ ${formatNumber(icpValueInUsd)}</div>
</div>

<div class="stat-card">
<div class="text-sm text-gray-400">Total icUSD Borrowed</div>
<div class="text-xl font-bold">{formatNumber(protocolStatus.totalIcusdBorrowed)} icUSD</div>
<div class="text-sm text-gray-400">Total Collateral Value</div>
<div class="text-xl font-bold">${formatNumber(totalCollateralUsd)}</div>
<div class="text-xs text-gray-500 mt-1">
ICP: {formatNumber(protocolStatus.totalIcpMargin)} (${formatNumber(icpValueInUsd)})
</div>
<div class="text-xs text-gray-500">
ckBTC: {formatNumber(protocolStatus.totalCkbtcMargin, 8)} (${formatNumber(ckbtcValueInUsd)})
</div>
</div>

<!-- Current Prices -->
<div class="stat-card">
<div class="text-sm text-gray-400">Current ICP Price</div>
<div class="text-xl font-bold">
<div class="text-sm text-gray-400">Current Asset Prices</div>
<div class="text-lg font-bold">
{#if isLoading}
<div class="animate-pulse bg-gray-700 h-6 w-24 rounded"></div>
{:else}
${formatNumber(protocolStatus.lastIcpRate)}
<div class="flex flex-col gap-1">
<div class="text-sm">ICP: ${formatNumber(protocolStatus.lastIcpRate, 2)}</div>
<div class="text-sm">ckBTC: ${formatNumber(protocolStatus.lastCkbtcRate, 0)}</div>
</div>
{/if}
</div>
</div>


<!-- Borrowed and Ratios -->
<div class="stat-card">
<div class="text-sm text-gray-400">Total Collateral Ratio</div>
<div class="text-xl font-bold">{formattedCollateralPercent}%</div>
<div class="text-sm text-gray-400">Protocol Metrics</div>
<div class="flex flex-col gap-1">
<div class="text-sm font-semibold">
icUSD Borrowed: {formatNumber(protocolStatus.totalIcusdBorrowed)}
</div>
<div class="text-sm">
Collateral Ratio: {formattedCollateralPercent}%
</div>
<div class="text-xs text-gray-500 mt-1">
Mode: <span class="{modeColor}">{modeDisplay}</span>
</div>
</div>
</div>
</div>

Expand Down
Loading