Crates.io | canfund |
lib.rs | canfund |
version | 0.5.0 |
source | src |
created_at | 2024-09-25 13:04:47.959113 |
updated_at | 2024-12-10 17:26:05.486394 |
description | This Rust library provides a simple interface to monitor and add cycles to canisters on the Internet Computer. |
homepage | https://github.com/dfinity/canfund#readme |
repository | https://github.com/dfinity/canfund |
max_upload_size | |
id | 1386308 |
size | 89,229 |
Welcome to canfund
! This library provides automated cycles management for canisters on the Internet Computer (IC). canfund
helps ensure your canisters have sufficient cycles by automating the process of balance checking, funding, and cycle-minting based on configurable rules.
canfund
, according to predefined rules.canfund
periodically checks cycle balances using ICP timers, ensuring your canisters stay adequately funded.canfund
can mint new cycles from ICP.To integrate canfund
into your Internet Computer project, add the library as a dependency:
cargo install `canfund`
Or alternatively manually define the dependency in your Cargo.toml
file:
{
"dependencies": {
"canfund": {
"git": "https://github.com/dfinity/canfund",
"version": "0.1.0"
}
}
}
Then, use canfund
in your canister code:
use canfund;
To use canfund
, configure it with rules for managing cycles for your canisters. The configuration includes:
Target Canisters: Specify the canisters that should be managed.
Funding Rules: Set the thresholds and strategies that trigger additional cycle funding.
Cycle Minting: Enable or disable the minting of cycles from the ICP balance when necessary.
Each canister that you want to fund using canfund
must be registered. During registration, you must specify the method by which the canister's cycle balance will be fetched. canfund
supports two different balance-fetching methods:
FetchCyclesBalanceFromCanisterStatus: Retrieves the cycle balance from the response of an update call to a canister, typically using the canister_status
method on the management canister. It can also work with responses from other canisters, such as a blackhole, or from self-exposed methods on any canister, as long as the response follows the CanisterStatusResponse specification.
This method is only applicable if the funding canister has the required permissions to invoke the target canister’s method, such as being a controller when interacting with the management canister.
// By default, using the management canister (funding canister is a controller)
let fetcher = FetchCyclesBalanceFromCanisterStatus::new();
// Using the blackhole canister (Blackhole is a controller)
let fetcher = FetchCyclesBalanceFromCanisterStatus::new()
.with_proxy(Principal::from_text("e3mmv-5qaaa-aaaah-aadma-cai").unwrap()));
);
// Using a custom method of a canister (method is public)
let fetcher = FetchCyclesBalanceFromCanisterStatus::new()
.with_proxy(Principal::from_text("ml52i-qqaaa-aaaar-qaaba-cai").unwrap())
.with_method("get_canister_status".to_string());
);
Note: This method is currently the only one that accounts for the freezing threshold of the funded canister. As a result, the runtime and threshold-based funding strategies (explained below) are calculated based on when the canister becomes frozen. This behavior does not apply to the Blackhole proxy, as it currently does not return the required idle_cycles_burned_per_day field in its response.
FetchCyclesBalanceFromPrometheusMetrics: Fetches the cycle balance by leveraging Prometheus metrics exposed by the canister through an HTTP endpoint.
let fetcher = FetchCyclesBalanceFromPrometheusMetrics::new(
"/metrics".to_string(), // path
"canister_cycles_balance".to_string(), // metric name
);
To register a canister with selected fetcher
:
fund_manager.register(
Principal::from_text("funded_canister_id").unwrap(),
RegisterOpts::new().with_cycles_fetcher(
Arc::new(fetcher)
),
);
canfund
provides three distinct strategies for funding your canisters:
BelowThreshold (Default): Funds the canister when its cycle balance falls below a predefined threshold.
let strategy = FundStrategy::BelowThreshold(
CyclesThreshold::new()
.with_min_cycles(125_000_000_000)
.with_fund_cycles(250_000_000_000)
);
BelowEstimatedRuntime: Funds the canister based on an estimated runtime in seconds. This strategy calculates the required cycles to keep the canister running for the specified duration.
let strategy = FundStrategy::BelowEstimatedRuntime(
EstimatedRuntime::new()
.with_min_runtime_secs(2 * 24 * 60 * 60) // 2 day
.with_fund_runtime_secs(3 * 24 * 60 * 60) // 3 days
.with_max_runtime_cycles_fund(1_000_000_000_000)
.with_fallback_min_cycles(125_000_000_000)
.with_fallback_fund_cycles(250_000_000_000),
);
Always: Funds the canister at a fixed interval with a specified amount of cycles, regardless of the current balance.
let strategy = FundStrategy::Always(1_000);
canfund
can also be configured to obtain cycles from an ICP account balance if your canister requires more cycles than it currently holds. This is achieved by interacting with the ICP Ledger and the Cycles Minting Canister (CMC).
To enable this feature, you must provide the necessary configuration to allow canfund
to mint cycles. This configuration can also be set for each registered canister to override the global configuration.
let obtain_cycles_config = ObtainCyclesOptions {
obtain_cycles: Arc::new(MintCycles {
ledger: Arc::new(IcLedgerCanister::new(MAINNET_LEDGER_CANISTER_ID)),
cmc: Arc::new(IcCyclesMintingCanister::new(
MAINNET_CYCLES_MINTING_CANISTER_ID,
)),
from_subaccount: Subaccount::from(DEFAULT_SUBACCOUNT),
}),
};
funding_options.with_obtain_cycles_options(Some(obtain_cycles_config));
With this configuration, canfund
will periodically check the ICP balance and mint new cycles as needed to ensure that your canisters remain adequately funded.
canfund
also supports registering a callback function that will be triggered after a funding round is completed. This feature is useful for monitoring and logging purposes, allowing you to capture and read data such as the remaining cycle balances and total cycles deposited per canister.
Example of registering a callback:
let funding_config = FundManagerOptions::new()
.with_funding_callback(Rc::new(|canister_records| {
// custom monitoring || logging logic
})
);
Initialize canfund
with your configuration:
let funding_config = FundManagerOptions::new()
.with_interval_secs(12 * 60 * 60) // check twice a day
.with_strategy(strategy);
fund_manager.with_options(funding_config);
Here's a basic example of using canfund
for automated cycles management:
use canfund::{manager::{options::{CyclesThreshold, FundManagerOptions, FundStrategy}, RegisterOpts}, operations::fetch::FetchCyclesBalanceFromCanisterStatus, FundManager};
#[ic_cdk_macros::init]
fn initialize() {
let mut fund_manager = FundManager::new();
let funding_config = FundManagerOptions::new()
.with_interval_secs(12 * 60 * 60)
.with_strategy(FundStrategy::BelowThreshold(
CyclesThreshold::new()
.with_min_cycles(125_000_000_000)
.with_fund_cycles(250_000_000_000),
));
fund_manager.with_options(funding_config);
fund_manager.register(
Principal::from_text("funded_canister_id").unwrap(),
RegisterOpts::new().with_cycles_fetcher(
Arc::new(FetchCyclesBalanceFromCanisterStatus::new())
),
);
// Note: the funding canister is NOT automatically registered
fund_manager.register(
id(),
RegisterOpts::new(),
);
fund_manager.start();
}
Full examples can be found in the examples folder for simple and advanced funding configurations.
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
This README provides an overview of canfund
, along with installation instructions and examples to help you get started. Contributions and feedback are welcome!