diff --git a/api/api_full.go b/api/api_full.go index a8176559d..1a3850cc6 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -351,6 +351,9 @@ type FullNode interface { // can issue. It takes the deal size and verified status as parameters. StateDealProviderCollateralBounds(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (DealCollateralBounds, error) + // StateCirculatingSupply returns the circulating supply of Filecoin at the given tipset + StateCirculatingSupply(context.Context, types.TipSetKey) (abi.TokenAmount, error) + // MethodGroup: Msig // The Msig methods are used to interact with multisig wallets on the // filecoin network diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 9b888335e..bc3b72a17 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -177,6 +177,7 @@ type FullNodeStruct struct { StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"` StateVerifiedClientStatus func(context.Context, address.Address, types.TipSetKey) (*verifreg.DataCap, error) `perm:"read"` StateDealProviderCollateralBounds func(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (api.DealCollateralBounds, error) `perm:"read"` + StateCirculatingSupply func(context.Context, types.TipSetKey) (abi.TokenAmount, error) `perm:"read"` MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"` MsigCreate func(context.Context, uint64, []address.Address, abi.ChainEpoch, types.BigInt, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"` @@ -791,6 +792,10 @@ func (c *FullNodeStruct) StateDealProviderCollateralBounds(ctx context.Context, return c.Internal.StateDealProviderCollateralBounds(ctx, size, verified, tsk) } +func (c *FullNodeStruct) StateCirculatingSupply(ctx context.Context, tsk types.TipSetKey) (abi.TokenAmount, error) { + return c.Internal.StateCirculatingSupply(ctx, tsk) +} + func (c *FullNodeStruct) MsigGetAvailableBalance(ctx context.Context, a address.Address, tsk types.TipSetKey) (types.BigInt, error) { return c.Internal.MsigGetAvailableBalance(ctx, a, tsk) } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index d1ad13420..6806ae021 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -693,3 +693,30 @@ func MinerHasMinPower(ctx context.Context, sm *StateManager, addr address.Addres return ps.MinerNominalPowerMeetsConsensusMinimum(sm.ChainStore().Store(ctx), addr) } + +func GetCirculatingSupply(ctx context.Context, sm *StateManager, ts *types.TipSet) (abi.TokenAmount, error) { + if ts == nil { + ts = sm.cs.GetHeaviestTipSet() + } + + r := store.NewChainRand(sm.cs, ts.Cids(), ts.Height()) + vmopt := &vm.VMOpts{ + StateBase: ts.ParentState(), + Epoch: ts.Height(), + Rand: r, + Bstore: sm.cs.Blockstore(), + Syscalls: sm.cs.VMSys(), + VestedCalc: sm.GetVestedFunds, + BaseFee: ts.Blocks()[0].ParentBaseFee, + } + vmi, err := vm.NewVM(vmopt) + if err != nil { + return abi.NewTokenAmount(0), err + } + + uvm := &vm.UnsafeVM{vmi} + + rt := uvm.MakeRuntime(ctx, &types.Message{From: builtin.InitActorAddr, GasLimit: 10000000}, builtin.InitActorAddr, 0, 0, 0) + + return rt.TotalFilCircSupply(), nil +} diff --git a/cli/state.go b/cli/state.go index 5f6400554..3072d4c01 100644 --- a/cli/state.go +++ b/cli/state.go @@ -55,6 +55,7 @@ var stateCmd = &cli.Command{ statePledgeCollateralCmd, stateListActorsCmd, stateListMinersCmd, + stateCircSupplyCmd, stateGetActorCmd, stateLookupIDCmd, stateReplaySetCmd, @@ -1518,3 +1519,31 @@ func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, er } return buf.Bytes(), nil } + +var stateCircSupplyCmd = &cli.Command{ + Name: "circulating-supply", + Usage: "Get the current circulating supply of filecoin", + Action: func(cctx *cli.Context) error { + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := ReqContext(cctx) + + ts, err := LoadTipSet(ctx, cctx, api) + if err != nil { + return err + } + + circ, err := api.StateCirculatingSupply(ctx, ts.Key()) + if err != nil { + return err + } + + fmt.Println(types.FIL(circ)) + + return nil + }, +} diff --git a/node/impl/full/state.go b/node/impl/full/state.go index ff8d9def1..5495581f1 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -1184,3 +1184,12 @@ func (a *StateAPI) StateDealProviderCollateralBounds(ctx context.Context, size a Max: max, }, nil } + +func (a *StateAPI) StateCirculatingSupply(ctx context.Context, tsk types.TipSetKey) (abi.TokenAmount, error) { + ts, err := a.Chain.GetTipSetFromKey(tsk) + if err != nil { + return abi.TokenAmount{}, xerrors.Errorf("loading tipset %s: %w", tsk, err) + } + + return stmgr.GetCirculatingSupply(ctx, a.StateManager, ts) +}