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    ) -> Result<Self::Ok, ServerError> {
30        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
31        let height = ts.epoch();
32        let actor = ctx
33            .state_manager
34            .get_required_actor(&address, *ts.parent_state())?;
35        let actor_balance = TokenAmount::from(&actor.balance);
36        let ms = multisig::State::load(ctx.store(), actor.code, actor.state)?;
37        let locked_balance = ms.locked_balance(height)?;
38        let avail_balance = &actor_balance - locked_balance;
39        Ok(avail_balance)
40    }
41}
42
43pub enum MsigGetPending {}
44
45impl RpcMethod<2> for MsigGetPending {
46    const NAME: &'static str = "Filecoin.MsigGetPending";
47    const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
48    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
49    const PERMISSION: Permission = Permission::Read;
50
51    type Params = (Address, ApiTipsetKey);
52    type Ok = Vec<Transaction>;
53
54    async fn handle(
55        ctx: Ctx<impl Blockstore>,
56        (address, ApiTipsetKey(tsk)): Self::Params,
57    ) -> Result<Self::Ok, ServerError> {
58        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
59        let ms: multisig::State = ctx
60            .state_manager
61            .get_actor_state_from_address(&ts, &address)?;
62        let txns = ms
63            .get_pending_txn(ctx.store())?
64            .into_iter()
65            .map(|txn| Transaction {
66                id: txn.id,
67                to: txn.to,
68                value: txn.value,
69                method: txn.method,
70                params: txn.params,
71                approved: txn.approved,
72            })
73            .collect();
74        Ok(txns)
75    }
76}
77
78pub enum MsigGetVested {}
79impl RpcMethod<3> for MsigGetVested {
80    const NAME: &'static str = "Filecoin.MsigGetVested";
81    const PARAM_NAMES: [&'static str; 3] = ["address", "start_tsk", "end_tsk"];
82    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
83    const PERMISSION: Permission = Permission::Read;
84
85    type Params = (Address, ApiTipsetKey, ApiTipsetKey);
86    type Ok = BigInt;
87
88    async fn handle(
89        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
90        (addr, ApiTipsetKey(start_tsk), ApiTipsetKey(end_tsk)): Self::Params,
91    ) -> Result<Self::Ok, ServerError> {
92        let start_ts = ctx
93            .chain_store()
94            .load_required_tipset_or_heaviest(&start_tsk)?;
95        let end_ts = ctx
96            .chain_store()
97            .load_required_tipset_or_heaviest(&end_tsk)?;
98
99        match start_ts.epoch().cmp(&end_ts.epoch()) {
100            std::cmp::Ordering::Greater => Err(ServerError::internal_error(
101                "start tipset is after end tipset",
102                None,
103            )),
104            std::cmp::Ordering::Equal => Ok(BigInt::from(0)),
105            std::cmp::Ordering::Less => {
106                let ms: multisig::State = ctx
107                    .state_manager
108                    .get_actor_state_from_address(&end_ts, &addr)?;
109                let start_lb = ms.locked_balance(start_ts.epoch())?;
110                let end_lb = ms.locked_balance(end_ts.epoch())?;
111                Ok(start_lb.atto() - end_lb.atto())
112            }
113        }
114    }
115}
116
117pub enum MsigGetVestingSchedule {}
118impl RpcMethod<2> for MsigGetVestingSchedule {
119    const NAME: &'static str = "Filecoin.MsigGetVestingSchedule";
120    const PARAM_NAMES: [&'static str; 2] = ["address", "tsk"];
121    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
122    const PERMISSION: Permission = Permission::Read;
123
124    type Params = (Address, ApiTipsetKey);
125    type Ok = MsigVesting;
126
127    async fn handle(
128        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
129        (addr, ApiTipsetKey(tsk)): Self::Params,
130    ) -> Result<Self::Ok, ServerError> {
131        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
132        let ms: multisig::State = ctx.state_manager.get_actor_state_from_address(&ts, &addr)?;
133        Ok(ms.get_vesting_schedule()?)
134    }
135}