forest/rpc/methods/
msig.rs

1// Copyright 2019-2025 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::actors::multisig::ext::MultisigExt;
11use crate::shim::{address::Address, econ::TokenAmount};
12use enumflags2::BitFlags;
13use fvm_ipld_blockstore::Blockstore;
14use num_bigint::BigInt;
15
16pub enum MsigGetAvailableBalance {}
17
18impl RpcMethod<2> for MsigGetAvailableBalance {
19    const NAME: &'static str = "Filecoin.MsigGetAvailableBalance";
20    const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
21    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
22    const PERMISSION: Permission = Permission::Read;
23
24    type Params = (Address, ApiTipsetKey);
25    type Ok = TokenAmount;
26
27    async fn handle(
28        ctx: Ctx<impl Blockstore>,
29        (address, ApiTipsetKey(tsk)): Self::Params,
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)?.into();
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    ) -> Result<Self::Ok, ServerError> {
59        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
60        let ms: multisig::State = ctx
61            .state_manager
62            .get_actor_state_from_address(&ts, &address)?;
63        let txns = ms
64            .get_pending_txn(ctx.store())?
65            .iter()
66            .map(|txn| Transaction {
67                id: txn.id,
68                to: txn.to.into(),
69                value: txn.value.clone().into(),
70                method: txn.method,
71                params: txn.params.clone(),
72                approved: txn.approved.iter().map(|item| item.into()).collect(),
73            })
74            .collect();
75        Ok(txns)
76    }
77}
78
79pub enum MsigGetVested {}
80impl RpcMethod<3> for MsigGetVested {
81    const NAME: &'static str = "Filecoin.MsigGetVested";
82    const PARAM_NAMES: [&'static str; 3] = ["address", "start_tsk", "end_tsk"];
83    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
84    const PERMISSION: Permission = Permission::Read;
85
86    type Params = (Address, ApiTipsetKey, ApiTipsetKey);
87    type Ok = BigInt;
88
89    async fn handle(
90        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
91        (addr, ApiTipsetKey(start_tsk), ApiTipsetKey(end_tsk)): Self::Params,
92    ) -> Result<Self::Ok, ServerError> {
93        let start_ts = ctx
94            .chain_store()
95            .load_required_tipset_or_heaviest(&start_tsk)?;
96        let end_ts = ctx
97            .chain_store()
98            .load_required_tipset_or_heaviest(&end_tsk)?;
99
100        match start_ts.epoch().cmp(&end_ts.epoch()) {
101            std::cmp::Ordering::Greater => Err(ServerError::internal_error(
102                "start tipset is after end tipset",
103                None,
104            )),
105            std::cmp::Ordering::Equal => Ok(BigInt::from(0)),
106            std::cmp::Ordering::Less => {
107                let ms: multisig::State = ctx
108                    .state_manager
109                    .get_actor_state_from_address(&end_ts, &addr)?;
110                let start_lb: TokenAmount = ms.locked_balance(start_ts.epoch())?.into();
111                let end_lb: TokenAmount = ms.locked_balance(end_ts.epoch())?.into();
112                Ok(start_lb.atto() - end_lb.atto())
113            }
114        }
115    }
116}
117
118pub enum MsigGetVestingSchedule {}
119impl RpcMethod<2> for MsigGetVestingSchedule {
120    const NAME: &'static str = "Filecoin.MsigGetVestingSchedule";
121    const PARAM_NAMES: [&'static str; 2] = ["address", "tsk"];
122    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
123    const PERMISSION: Permission = Permission::Read;
124
125    type Params = (Address, ApiTipsetKey);
126    type Ok = MsigVesting;
127
128    async fn handle(
129        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
130        (addr, ApiTipsetKey(tsk)): Self::Params,
131    ) -> Result<Self::Ok, ServerError> {
132        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
133        let ms: multisig::State = ctx.state_manager.get_actor_state_from_address(&ts, &addr)?;
134        Ok(ms.get_vesting_schedule()?)
135    }
136}