Skip to main content

forest/rpc/methods/
msig.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use crate::rpc::error::ServerError;
5use crate::rpc::types::ApiTipsetKey;
6use crate::rpc::types::*;
7use crate::rpc::{ApiPaths, Ctx, Permission, RpcMethod};
8use crate::shim::actors::MultisigActorStateLoad as _;
9use crate::shim::actors::multisig;
10use crate::shim::{address::Address, econ::TokenAmount};
11use enumflags2::BitFlags;
12use fvm_ipld_blockstore::Blockstore;
13use num_bigint::BigInt;
14
15pub enum MsigGetAvailableBalance {}
16
17impl RpcMethod<2> for MsigGetAvailableBalance {
18    const NAME: &'static str = "Filecoin.MsigGetAvailableBalance";
19    const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
20    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
21    const PERMISSION: Permission = Permission::Read;
22
23    type Params = (Address, ApiTipsetKey);
24    type Ok = TokenAmount;
25
26    async fn handle(
27        ctx: Ctx<impl Blockstore>,
28        (address, ApiTipsetKey(tsk)): Self::Params,
29        _: &http::Extensions,
30    ) -> Result<Self::Ok, ServerError> {
31        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
32        let height = ts.epoch();
33        let actor = ctx
34            .state_manager
35            .get_required_actor(&address, *ts.parent_state())?;
36        let actor_balance = TokenAmount::from(&actor.balance);
37        let ms = multisig::State::load(ctx.store(), actor.code, actor.state)?;
38        let locked_balance = ms.locked_balance(height)?;
39        let avail_balance = &actor_balance - locked_balance;
40        Ok(avail_balance)
41    }
42}
43
44pub enum MsigGetPending {}
45
46impl RpcMethod<2> for MsigGetPending {
47    const NAME: &'static str = "Filecoin.MsigGetPending";
48    const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
49    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
50    const PERMISSION: Permission = Permission::Read;
51
52    type Params = (Address, ApiTipsetKey);
53    type Ok = Vec<Transaction>;
54
55    async fn handle(
56        ctx: Ctx<impl Blockstore>,
57        (address, ApiTipsetKey(tsk)): Self::Params,
58        _: &http::Extensions,
59    ) -> Result<Self::Ok, ServerError> {
60        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
61        let ms: multisig::State = ctx
62            .state_manager
63            .get_actor_state_from_address(&ts, &address)?;
64        let txns = ms
65            .get_pending_txn(ctx.store())?
66            .into_iter()
67            .map(|txn| Transaction {
68                id: txn.id,
69                to: txn.to,
70                value: txn.value,
71                method: txn.method,
72                params: txn.params,
73                approved: txn.approved,
74            })
75            .collect();
76        Ok(txns)
77    }
78}
79
80pub enum MsigGetVested {}
81impl RpcMethod<3> for MsigGetVested {
82    const NAME: &'static str = "Filecoin.MsigGetVested";
83    const PARAM_NAMES: [&'static str; 3] = ["address", "start_tsk", "end_tsk"];
84    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
85    const PERMISSION: Permission = Permission::Read;
86
87    type Params = (Address, ApiTipsetKey, ApiTipsetKey);
88    type Ok = BigInt;
89
90    async fn handle(
91        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
92        (addr, ApiTipsetKey(start_tsk), ApiTipsetKey(end_tsk)): Self::Params,
93        _: &http::Extensions,
94    ) -> Result<Self::Ok, ServerError> {
95        let start_ts = ctx
96            .chain_store()
97            .load_required_tipset_or_heaviest(&start_tsk)?;
98        let end_ts = ctx
99            .chain_store()
100            .load_required_tipset_or_heaviest(&end_tsk)?;
101
102        match start_ts.epoch().cmp(&end_ts.epoch()) {
103            std::cmp::Ordering::Greater => Err(ServerError::internal_error(
104                "start tipset is after end tipset",
105                None,
106            )),
107            std::cmp::Ordering::Equal => Ok(BigInt::from(0)),
108            std::cmp::Ordering::Less => {
109                let ms: multisig::State = ctx
110                    .state_manager
111                    .get_actor_state_from_address(&end_ts, &addr)?;
112                let start_lb = ms.locked_balance(start_ts.epoch())?;
113                let end_lb = ms.locked_balance(end_ts.epoch())?;
114                Ok(start_lb.atto() - end_lb.atto())
115            }
116        }
117    }
118}
119
120pub enum MsigGetVestingSchedule {}
121impl RpcMethod<2> for MsigGetVestingSchedule {
122    const NAME: &'static str = "Filecoin.MsigGetVestingSchedule";
123    const PARAM_NAMES: [&'static str; 2] = ["address", "tsk"];
124    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
125    const PERMISSION: Permission = Permission::Read;
126
127    type Params = (Address, ApiTipsetKey);
128    type Ok = MsigVesting;
129
130    async fn handle(
131        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
132        (addr, ApiTipsetKey(tsk)): Self::Params,
133        _: &http::Extensions,
134    ) -> Result<Self::Ok, ServerError> {
135        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
136        let ms: multisig::State = ctx.state_manager.get_actor_state_from_address(&ts, &addr)?;
137        Ok(ms.get_vesting_schedule()?)
138    }
139}