1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use crate::rpc::error::ServerError;
use crate::rpc::types::ApiTipsetKey;
use crate::rpc::types::*;
use crate::rpc::{ApiPaths, Ctx, Permission, RpcMethod};
use crate::shim::actors::multisig::MultisigExt;
use crate::shim::actors::MultisigActorStateLoad as _;
use crate::shim::{address::Address, econ::TokenAmount};
use fil_actor_interface::multisig;
use fvm_ipld_blockstore::Blockstore;
use num_bigint::BigInt;

pub enum MsigGetAvailableBalance {}

impl RpcMethod<2> for MsigGetAvailableBalance {
    const NAME: &'static str = "Filecoin.MsigGetAvailableBalance";
    const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
    const API_PATHS: ApiPaths = ApiPaths::V1;
    const PERMISSION: Permission = Permission::Read;

    type Params = (Address, ApiTipsetKey);
    type Ok = TokenAmount;

    async fn handle(
        ctx: Ctx<impl Blockstore>,
        (address, ApiTipsetKey(tsk)): Self::Params,
    ) -> Result<Self::Ok, ServerError> {
        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
        let height = ts.epoch();
        let actor = ctx
            .state_manager
            .get_required_actor(&address, *ts.parent_state())?;
        let actor_balance = TokenAmount::from(&actor.balance);
        let ms = multisig::State::load(ctx.store(), actor.code, actor.state)?;
        let locked_balance = ms.locked_balance(height)?.into();
        let avail_balance = &actor_balance - locked_balance;
        Ok(avail_balance)
    }
}

pub enum MsigGetPending {}

impl RpcMethod<2> for MsigGetPending {
    const NAME: &'static str = "Filecoin.MsigGetPending";
    const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
    const API_PATHS: ApiPaths = ApiPaths::V1;
    const PERMISSION: Permission = Permission::Read;

    type Params = (Address, ApiTipsetKey);
    type Ok = Vec<Transaction>;

    async fn handle(
        ctx: Ctx<impl Blockstore>,
        (address, ApiTipsetKey(tsk)): Self::Params,
    ) -> Result<Self::Ok, ServerError> {
        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
        let ms: multisig::State = ctx
            .state_manager
            .get_actor_state_from_address(&ts, &address)?;
        let txns = ms
            .get_pending_txn(ctx.store())?
            .iter()
            .map(|txn| Transaction {
                id: txn.id,
                to: txn.to.into(),
                value: txn.value.clone().into(),
                method: txn.method,
                params: txn.params.clone(),
                approved: txn.approved.iter().map(|item| item.into()).collect(),
            })
            .collect();
        Ok(txns)
    }
}

pub enum MsigGetVested {}
impl RpcMethod<3> for MsigGetVested {
    const NAME: &'static str = "Filecoin.MsigGetVested";
    const PARAM_NAMES: [&'static str; 3] = ["address", "start_tsk", "end_tsk"];
    const API_PATHS: ApiPaths = ApiPaths::V1;
    const PERMISSION: Permission = Permission::Read;

    type Params = (Address, ApiTipsetKey, ApiTipsetKey);
    type Ok = BigInt;

    async fn handle(
        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
        (addr, ApiTipsetKey(start_tsk), ApiTipsetKey(end_tsk)): Self::Params,
    ) -> Result<Self::Ok, ServerError> {
        let start_ts = ctx
            .chain_store()
            .load_required_tipset_or_heaviest(&start_tsk)?;
        let end_ts = ctx
            .chain_store()
            .load_required_tipset_or_heaviest(&end_tsk)?;

        match start_ts.epoch().cmp(&end_ts.epoch()) {
            std::cmp::Ordering::Greater => Err(ServerError::internal_error(
                "start tipset is after end tipset",
                None,
            )),
            std::cmp::Ordering::Equal => Ok(BigInt::from(0)),
            std::cmp::Ordering::Less => {
                let ms: multisig::State = ctx
                    .state_manager
                    .get_actor_state_from_address(&end_ts, &addr)?;
                let start_lb: TokenAmount = ms.locked_balance(start_ts.epoch())?.into();
                let end_lb: TokenAmount = ms.locked_balance(end_ts.epoch())?.into();
                Ok(start_lb.atto() - end_lb.atto())
            }
        }
    }
}

pub enum MsigGetVestingSchedule {}
impl RpcMethod<2> for MsigGetVestingSchedule {
    const NAME: &'static str = "Filecoin.MsigGetVestingSchedule";
    const PARAM_NAMES: [&'static str; 2] = ["address", "tsk"];
    const API_PATHS: ApiPaths = ApiPaths::V1;
    const PERMISSION: Permission = Permission::Read;

    type Params = (Address, ApiTipsetKey);
    type Ok = MsigVesting;

    async fn handle(
        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
        (addr, ApiTipsetKey(tsk)): Self::Params,
    ) -> Result<Self::Ok, ServerError> {
        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
        let ms: multisig::State = ctx.state_manager.get_actor_state_from_address(&ts, &addr)?;
        Ok(ms.get_vesting_schedule()?)
    }
}