Skip to content

Commit

Permalink
feat(station): display station and upgrader cycle balance on Administ…
Browse files Browse the repository at this point in the history
…ration page (#457)

Co-authored-by: Kepler Vital <[email protected]>
  • Loading branch information
olaszakos and keplervital authored Dec 4, 2024
1 parent 43ebe4c commit 79cdf29
Show file tree
Hide file tree
Showing 16 changed files with 137 additions and 27 deletions.
70 changes: 52 additions & 18 deletions apps/wallet/src/components/settings/StationInfoCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,23 +126,56 @@
</VListItemSubtitle>
</VListItem>
<AuthCheck :privileges="[Privilege.SystemInfo]">
<VListItem v-if="!loadingSystemInfo" class="px-0">
<tempalte v-if="!loadingSystemInfo">
<VListItem class="px-0">
<VListItemTitle class="font-weight-bold">{{
$t(`terms.upgrader_id`)
}}</VListItemTitle>
<VListItemSubtitle v-if="upgraderId"
>{{ upgraderId }}
<VBtn
size="x-small"
variant="text"
:icon="mdiContentCopy"
@click="
copyToClipboard({
textToCopy: upgraderId,
sendNotification: true,
})
"
/>
</VListItemSubtitle>
</VListItem>

<VListItem class="px-0">
<VListItemTitle class="font-weight-bold">{{
$t(`pages.administration.cycle_balances`)
}}</VListItemTitle>
<VListItemSubtitle>
<table style="min-width: 200px">
<tr>
<td>{{ $t('terms.station') }}:</td>
<td class="text-right">
{{ systemInfo ? formatCycles(systemInfo.cycles) : '-' }}
</td>
</tr>
<tr>
<td>{{ $t('terms.upgrader') }}:</td>
<td class="text-right">
{{
systemInfo?.upgrader_cycles?.[0]
? formatCycles(systemInfo.upgrader_cycles[0])
: '-'
}}
</td>
</tr>
</table>
</VListItemSubtitle>
</VListItem>
</tempalte>
<VListItem class="px-0" v-else-if="loadingSystemInfoError">
<VListItemTitle class="font-weight-bold">{{ $t(`terms.upgrader_id`) }}</VListItemTitle>
<VListItemSubtitle v-if="upgraderId"
>{{ upgraderId }}
<VBtn
size="x-small"
variant="text"
:icon="mdiContentCopy"
@click="
copyToClipboard({
textToCopy: upgraderId,
sendNotification: true,
})
"
/>
</VListItemSubtitle>
<VListItemSubtitle v-else-if="loadingSystemInfoError">
<VListItemSubtitle>
<VAlert type="error" variant="tonal" density="compact" class="mb-4 mt-2">
{{ $t('pages.administration.system_info_error') }}
</VAlert>
Expand Down Expand Up @@ -238,6 +271,7 @@ import {
Request,
SystemInfo,
} from '~/generated/station/station.did';
import { formatCycles } from '~/mappers/cycles.mapper';
import { storeUserStationToUserStation } from '~/mappers/stations.mapper';
import { i18n } from '~/plugins/i18n.plugin';
import { services } from '~/plugins/services.plugin';
Expand All @@ -246,9 +280,9 @@ import { useSessionStore } from '~/stores/session.store';
import { useStationStore } from '~/stores/station.store';
import { Privilege } from '~/types/auth.types';
import { copyToClipboard } from '~/utils/app.utils';
import StationInfoForm, { StationInfoModel } from './StationInfoForm.vue';
import { unreachable, variantIs } from '~/utils/helper.utils';
import { hasRequiredPrivilege } from '~/utils/auth.utils';
import { unreachable, variantIs } from '~/utils/helper.utils';
import StationInfoForm, { StationInfoModel } from './StationInfoForm.vue';
const station = useStationStore();
const session = useSessionStore();
Expand Down
6 changes: 4 additions & 2 deletions apps/wallet/src/generated/station/station.did
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ type MonitorExternalCanisterStartInput = record {
// The strategy for funding the canister.
funding_strategy : MonitorExternalCanisterStrategyInput;
// The strategy for obtaining cycles for the funding operation.
cycle_obtain_strategy: opt CycleObtainStrategyInput;
cycle_obtain_strategy : opt CycleObtainStrategyInput;
};

// The operation kind for monitoring an external canister in the station.
Expand Down Expand Up @@ -2033,8 +2033,10 @@ type SystemInfo = record {
version : text;
// The upgrader principal id.
upgrader_id : principal;
// Cycle balance of the canister.
// Cycle balance of the station.
cycles : nat64;
// Cycle balance of the canister.
upgrader_cycles : opt nat64;
// The time at which the canister was last upgraded.
last_upgrade_timestamp : TimestampRFC3339;
// Did the canister successfully fetched randomness from the management canister.
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/generated/station/station.did.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,7 @@ export interface SupportedBlockchain {
}
export interface SystemInfo {
'disaster_recovery' : [] | [DisasterRecovery],
'upgrader_cycles' : [] | [bigint],
'name' : string,
'last_upgrade_timestamp' : TimestampRFC3339,
'raw_rand_successful' : boolean,
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/generated/station/station.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,7 @@ export const idlFactory = ({ IDL }) => {
});
const SystemInfo = IDL.Record({
'disaster_recovery' : IDL.Opt(DisasterRecovery),
'upgrader_cycles' : IDL.Opt(IDL.Nat64),
'name' : IDL.Text,
'last_upgrade_timestamp' : TimestampRFC3339,
'raw_rand_successful' : IDL.Bool,
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/locales/en.locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,7 @@ export default {
cycle_obtain_strategy_disabled:
'WARNING: Station cycle balance top-up disabled. Your station may run out of cycles.',
cycle_obtain_strategy_mint_from_native_token: 'Mint from ICP account',
cycle_balances: 'Cycle Balances',
},
users: {
title: 'Users',
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/locales/fr.locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,7 @@ export default {
system_info_error: 'Erreur lors du chargement des informations système, veuillez réessayer.',
cycle_obtain_strategy_disabled: 'Stratégie de recharge des cycles non définie',
cycle_obtain_strategy_mint_from_native_token: 'Mint depuis le compte ICP',
cycle_balances: 'Solde de Cycles',
},
users: {
title: 'Usagers',
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/locales/pt.locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,7 @@ export default {
'Erro ao carregar as informações do sistema da carteira, por favor, tente novamente.',
cycle_obtain_strategy_disabled: 'AVISO: Recarga de saldo de ciclos da carteira desativada.',
cycle_obtain_strategy_mint_from_native_token: 'Recarregar a partir da conta de ICP',
cycle_balances: 'Saldo de ciclos',
},
user_groups: {
title: 'Grupos de usuários',
Expand Down
28 changes: 27 additions & 1 deletion apps/wallet/src/mappers/cycles.mapper.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { describe, expect, it } from 'vitest';
import { fromCyclesUnit, toCyclesUnit } from '~/mappers/cycles.mapper';
import {
cyclesUnitFromNumber,
formatCycles,
fromCyclesUnit,
toCyclesUnit,
} from '~/mappers/cycles.mapper';
import { CyclesUnit } from '~/types/app.types';

describe('toCyclesUnit', () => {
Expand Down Expand Up @@ -69,3 +74,24 @@ describe('fromCyclesUnit', () => {
expect(() => fromCyclesUnit(1, 'unknown' as CyclesUnit)).toThrow();
});
});

describe('cyclesUnitFromNumber', () => {
it('should return the correct unit based on the number provided', () => {
expect(cyclesUnitFromNumber(1_000_000_000_000n)).toBe(CyclesUnit.Trillion);
expect(cyclesUnitFromNumber(1_000_000_000n)).toBe(CyclesUnit.Billion);
expect(cyclesUnitFromNumber(1_000_000n)).toBe(CyclesUnit.Million);
expect(cyclesUnitFromNumber(1n)).toBe(CyclesUnit.Smallest);
});
});

describe('formatCycles', () => {
it('should return the formatted cycles amount from a value', () => {
expect(formatCycles(1_000_000_000_000n)).toBe(`1 TC`);
expect(formatCycles(1_100_000_000_000n)).toBe(`1.1 TC`);
expect(formatCycles(1_000_000_000n)).toBe(`1 BC`);
expect(formatCycles(1_100_000_000n)).toBe(`1.1 BC`);
expect(formatCycles(1_000_000n)).toBe(`1 MC`);
expect(formatCycles(1_100_000n)).toBe(`1.1 MC`);
expect(formatCycles(1n)).toBe(`1 Cycles`);
});
});
18 changes: 18 additions & 0 deletions apps/wallet/src/mappers/cycles.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,21 @@ export const fromCyclesUnit = (cycles: number, unit: CyclesUnit): bigint => {
return unreachable(unit);
}
};

export const cyclesUnitFromNumber = (cycles: bigint): CyclesUnit => {
if (cycles >= 1_000_000_000_000) {
return CyclesUnit.Trillion;
}
if (cycles >= 1_000_000_000) {
return CyclesUnit.Billion;
}
if (cycles >= 1_000_000) {
return CyclesUnit.Million;
}
return CyclesUnit.Smallest;
};

export const formatCycles = (cycles: bigint): string => {
const unit = cyclesUnitFromNumber(cycles);
return `${toCyclesUnit(cycles, unit)} ${unit}`;
};
2 changes: 1 addition & 1 deletion apps/wallet/src/types/app.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export enum CyclesUnit {
Billion = 'BC',
Million = 'MC',
// Can be used to display cycles in the smallest unit.
Smallest = 'e8s',
Smallest = 'Cycles',
}

export enum TimeUnit {
Expand Down
6 changes: 4 additions & 2 deletions core/station/api/spec.did
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ type MonitorExternalCanisterStartInput = record {
// The strategy for funding the canister.
funding_strategy : MonitorExternalCanisterStrategyInput;
// The strategy for obtaining cycles for the funding operation.
cycle_obtain_strategy: opt CycleObtainStrategyInput;
cycle_obtain_strategy : opt CycleObtainStrategyInput;
};

// The operation kind for monitoring an external canister in the station.
Expand Down Expand Up @@ -2033,8 +2033,10 @@ type SystemInfo = record {
version : text;
// The upgrader principal id.
upgrader_id : principal;
// Cycle balance of the canister.
// Cycle balance of the station.
cycles : nat64;
// Cycle balance of the canister.
upgrader_cycles : opt nat64;
// The time at which the canister was last upgraded.
last_upgrade_timestamp : TimestampRFC3339;
// Did the canister successfully fetched randomness from the management canister.
Expand Down
1 change: 1 addition & 0 deletions core/station/api/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct SystemInfoDTO {
pub version: String,
pub upgrader_id: Principal,
pub cycles: u64,
pub upgrader_cycles: Option<u64>,
pub last_upgrade_timestamp: TimestampRfc3339,
pub raw_rand_successful: bool,
pub disaster_recovery: Option<DisasterRecoveryDTO>,
Expand Down
14 changes: 12 additions & 2 deletions core/station/impl/src/controllers/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
errors::AuthorizationError,
migration,
models::resource::{Resource, SystemResourceAction},
services::{SystemService, INITIALIZING, SYSTEM_SERVICE},
services::{SystemService, CYCLE_MANAGER, INITIALIZING, SYSTEM_SERVICE},
SYSTEM_VERSION,
};
use ic_cdk_macros::{post_upgrade, query, update};
Expand Down Expand Up @@ -137,9 +137,19 @@ impl SystemController {
async fn system_info(&self) -> ApiResult<SystemInfoResponse> {
let system_info = self.system_service.get_system_info();
let cycles = canister_balance();
let upgrader_balance = CYCLE_MANAGER.get_canister(system_info.get_upgrader_canister_id());

Ok(SystemInfoResponse {
system: system_info.to_dto(&cycles, SYSTEM_VERSION),
system: system_info.to_dto(
&cycles,
SYSTEM_VERSION,
upgrader_balance.and_then(|record| {
record
.get_cycles()
.as_ref()
.map(|cycles_balance| cycles_balance.amount as u64)
}),
),
})
}

Expand Down
8 changes: 7 additions & 1 deletion core/station/impl/src/mappers/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ use orbit_essentials::{
use station_api::DisasterRecoveryDTO;

impl SystemInfo {
pub fn to_dto(&self, cycles: &u64, version: &str) -> station_api::SystemInfoDTO {
pub fn to_dto(
&self,
cycles: &u64,
version: &str,
upgrader_cycles: Option<u64>,
) -> station_api::SystemInfoDTO {
station_api::SystemInfoDTO {
name: self.get_name().to_string(),
last_upgrade_timestamp: timestamp_to_rfc3339(&self.get_last_upgrade_timestamp()),
Expand All @@ -23,6 +28,7 @@ impl SystemInfo {
}
}),
cycle_obtain_strategy: (*self.get_cycle_obtain_strategy()).into(),
upgrader_cycles,
}
}
}
5 changes: 5 additions & 0 deletions core/station/impl/src/services/cycle_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::repositories::ACCOUNT_REPOSITORY;
use canfund::api::cmc::IcCyclesMintingCanister;
use canfund::api::ledger::IcLedgerCanister;
use canfund::manager::options::{FundManagerOptions, ObtainCyclesOptions};
use canfund::manager::record::CanisterRecord;
use canfund::manager::RegisterOpts;
use canfund::operations::obtain::MintCycles;
use canfund::FundManager;
Expand Down Expand Up @@ -65,6 +66,10 @@ impl CycleManager {
));
}

pub fn get_canister(&self, canister_id: &CanisterId) -> Option<CanisterRecord> {
FUND_MANAGER.with(|manager| manager.borrow().get_canisters().get(canister_id).cloned())
}

pub fn remove_canister(&self, canister_id: CanisterId) {
FUND_MANAGER.with(|manager| {
manager.borrow_mut().unregister(canister_id);
Expand Down
1 change: 1 addition & 0 deletions core/station/impl/src/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub use external_canister::*;
pub mod permission;

mod cycle_manager;
pub use cycle_manager::*;

mod disaster_recovery;
pub use disaster_recovery::*;
Expand Down

0 comments on commit 79cdf29

Please sign in to comment.