2019-07-13 00:41:32 +00:00
package cli
import (
2020-03-04 21:57:23 +00:00
"bufio"
2021-09-29 16:59:51 +00:00
"bytes"
2019-10-08 09:17:03 +00:00
"encoding/hex"
2019-10-08 09:46:36 +00:00
"encoding/json"
2019-07-13 00:41:32 +00:00
"fmt"
2019-10-09 00:22:18 +00:00
"os"
2023-08-21 07:10:47 +00:00
"os/signal"
2019-10-09 00:22:18 +00:00
"strings"
2023-08-21 07:10:47 +00:00
"syscall"
2019-07-13 00:41:32 +00:00
2020-09-29 09:17:23 +00:00
"github.com/urfave/cli/v2"
2023-06-27 19:29:58 +00:00
"golang.org/x/term"
2020-09-29 09:17:23 +00:00
"golang.org/x/xerrors"
2019-12-19 20:13:17 +00:00
"github.com/filecoin-project/go-address"
2020-10-21 00:34:46 +00:00
"github.com/filecoin-project/go-state-types/abi"
2020-09-29 09:17:23 +00:00
"github.com/filecoin-project/go-state-types/big"
2020-09-07 03:49:10 +00:00
"github.com/filecoin-project/go-state-types/crypto"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-state-types/network"
2020-09-29 09:17:23 +00:00
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/lotus/build"
2023-09-19 08:57:35 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin"
2020-12-03 20:47:26 +00:00
"github.com/filecoin-project/lotus/chain/types"
2020-09-29 09:17:23 +00:00
"github.com/filecoin-project/lotus/lib/tablewriter"
2019-07-13 00:41:32 +00:00
)
var walletCmd = & cli . Command {
Name : "wallet" ,
Usage : "Manage wallet" ,
Subcommands : [ ] * cli . Command {
walletNew ,
walletList ,
2019-07-18 20:26:04 +00:00
walletBalance ,
2019-10-08 09:17:03 +00:00
walletExport ,
walletImport ,
2019-10-17 10:18:40 +00:00
walletGetDefault ,
walletSetDefault ,
2020-02-25 21:35:41 +00:00
walletSign ,
2020-02-25 23:17:15 +00:00
walletVerify ,
2020-06-05 23:04:23 +00:00
walletDelete ,
2020-10-23 09:16:29 +00:00
walletMarket ,
2019-07-13 00:41:32 +00:00
} ,
}
var walletNew = & cli . Command {
2019-10-09 20:02:08 +00:00
Name : "new" ,
Usage : "Generate a new key of the given type" ,
2020-03-04 21:46:00 +00:00
ArgsUsage : "[bls|secp256k1 (default secp256k1)]" ,
2019-07-13 00:41:32 +00:00
Action : func ( cctx * cli . Context ) error {
2019-10-03 18:12:30 +00:00
api , closer , err := GetFullNodeAPI ( cctx )
2019-07-13 00:41:32 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer ( )
2019-07-18 23:16:23 +00:00
ctx := ReqContext ( cctx )
2019-07-13 00:41:32 +00:00
2022-02-22 14:35:46 +00:00
afmt := NewAppFmt ( cctx . App )
2019-07-13 00:41:32 +00:00
t := cctx . Args ( ) . First ( )
if t == "" {
2020-01-08 19:16:09 +00:00
t = "secp256k1"
2019-07-13 00:41:32 +00:00
}
2020-10-11 18:12:01 +00:00
nk , err := api . WalletNew ( ctx , types . KeyType ( t ) )
2019-07-13 00:41:32 +00:00
if err != nil {
return err
}
2022-02-22 14:35:46 +00:00
afmt . Println ( nk . String ( ) )
2019-07-13 00:41:32 +00:00
return nil
} ,
}
var walletList = & cli . Command {
Name : "list" ,
Usage : "List wallet address" ,
2020-09-29 09:17:23 +00:00
Flags : [ ] cli . Flag {
& cli . BoolFlag {
Name : "addr-only" ,
Usage : "Only print addresses" ,
Aliases : [ ] string { "a" } ,
} ,
2020-10-23 12:42:19 +00:00
& cli . BoolFlag {
Name : "id" ,
Usage : "Output ID addresses" ,
Aliases : [ ] string { "i" } ,
} ,
& cli . BoolFlag {
Name : "market" ,
Usage : "Output market balances" ,
Aliases : [ ] string { "m" } ,
} ,
2020-09-29 09:17:23 +00:00
} ,
2019-07-13 00:41:32 +00:00
Action : func ( cctx * cli . Context ) error {
2019-10-03 18:12:30 +00:00
api , closer , err := GetFullNodeAPI ( cctx )
2019-07-13 00:41:32 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer ( )
2019-07-18 23:16:23 +00:00
ctx := ReqContext ( cctx )
2019-07-13 00:41:32 +00:00
2022-03-01 01:21:22 +00:00
afmt := NewAppFmt ( cctx . App )
2019-07-13 00:41:32 +00:00
addrs , err := api . WalletList ( ctx )
if err != nil {
return err
}
2020-09-30 05:10:03 +00:00
// Assume an error means no default key is set
def , _ := api . WalletDefaultAddress ( ctx )
2020-09-29 09:17:23 +00:00
tw := tablewriter . New (
tablewriter . Col ( "Address" ) ,
2020-10-23 12:42:19 +00:00
tablewriter . Col ( "ID" ) ,
2020-09-29 09:17:23 +00:00
tablewriter . Col ( "Balance" ) ,
2020-10-23 12:42:19 +00:00
tablewriter . Col ( "Market(Avail)" ) ,
tablewriter . Col ( "Market(Locked)" ) ,
2020-09-29 09:17:23 +00:00
tablewriter . Col ( "Nonce" ) ,
tablewriter . Col ( "Default" ) ,
tablewriter . NewLineCol ( "Error" ) )
2019-07-13 00:41:32 +00:00
for _ , addr := range addrs {
2020-09-29 09:17:23 +00:00
if cctx . Bool ( "addr-only" ) {
2022-03-01 01:21:22 +00:00
afmt . Println ( addr . String ( ) )
2020-09-29 09:17:23 +00:00
} else {
a , err := api . StateGetActor ( ctx , addr , types . EmptyTSK )
if err != nil {
if ! strings . Contains ( err . Error ( ) , "actor not found" ) {
tw . Write ( map [ string ] interface { } {
"Address" : addr ,
"Error" : err ,
} )
continue
}
a = & types . Actor {
Balance : big . Zero ( ) ,
}
}
row := map [ string ] interface { } {
"Address" : addr ,
"Balance" : types . FIL ( a . Balance ) ,
"Nonce" : a . Nonce ,
}
if addr == def {
row [ "Default" ] = "X"
}
2020-10-23 12:42:19 +00:00
if cctx . Bool ( "id" ) {
id , err := api . StateLookupID ( ctx , addr , types . EmptyTSK )
if err != nil {
row [ "ID" ] = "n/a"
} else {
row [ "ID" ] = id
}
}
if cctx . Bool ( "market" ) {
mbal , err := api . StateMarketBalance ( ctx , addr , types . EmptyTSK )
if err == nil {
row [ "Market(Avail)" ] = types . FIL ( types . BigSub ( mbal . Escrow , mbal . Locked ) )
row [ "Market(Locked)" ] = types . FIL ( mbal . Locked )
}
}
2020-09-29 09:17:23 +00:00
tw . Write ( row )
}
}
if ! cctx . Bool ( "addr-only" ) {
return tw . Flush ( os . Stdout )
2019-07-13 00:41:32 +00:00
}
2020-09-29 09:17:23 +00:00
2019-07-13 00:41:32 +00:00
return nil
} ,
}
2019-07-18 20:26:04 +00:00
var walletBalance = & cli . Command {
2019-10-09 20:02:08 +00:00
Name : "balance" ,
Usage : "Get account balance" ,
2020-03-04 21:46:00 +00:00
ArgsUsage : "[address]" ,
2019-07-18 20:26:04 +00:00
Action : func ( cctx * cli . Context ) error {
2019-10-03 18:12:30 +00:00
api , closer , err := GetFullNodeAPI ( cctx )
2019-07-18 20:26:04 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer ( )
2019-07-18 23:16:23 +00:00
ctx := ReqContext ( cctx )
2019-07-18 20:26:04 +00:00
2022-02-22 14:35:46 +00:00
afmt := NewAppFmt ( cctx . App )
2019-07-25 14:18:55 +00:00
var addr address . Address
if cctx . Args ( ) . First ( ) != "" {
addr , err = address . NewFromString ( cctx . Args ( ) . First ( ) )
} else {
addr , err = api . WalletDefaultAddress ( ctx )
}
2019-07-18 20:26:04 +00:00
if err != nil {
return err
}
balance , err := api . WalletBalance ( ctx , addr )
if err != nil {
return err
}
2023-08-09 15:13:25 +00:00
inSync , err := IsSyncDone ( ctx , api )
2023-08-08 08:06:19 +00:00
if err != nil {
return err
}
2023-08-09 15:13:25 +00:00
if balance . Equals ( types . NewInt ( 0 ) ) && ! inSync {
2022-02-22 14:35:46 +00:00
afmt . Printf ( "%s (warning: may display 0 if chain sync in progress)\n" , types . FIL ( balance ) )
2020-06-08 19:20:44 +00:00
} else {
2022-02-22 14:35:46 +00:00
afmt . Printf ( "%s\n" , types . FIL ( balance ) )
2020-06-08 19:20:44 +00:00
}
2019-07-18 20:26:04 +00:00
return nil
} ,
}
2019-10-08 09:17:03 +00:00
2019-10-17 10:18:40 +00:00
var walletGetDefault = & cli . Command {
Name : "default" ,
Usage : "Get default wallet address" ,
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return err
}
defer closer ( )
ctx := ReqContext ( cctx )
2022-02-22 14:35:46 +00:00
afmt := NewAppFmt ( cctx . App )
2019-10-17 10:18:40 +00:00
addr , err := api . WalletDefaultAddress ( ctx )
if err != nil {
return err
}
2022-02-22 14:35:46 +00:00
afmt . Printf ( "%s\n" , addr . String ( ) )
2019-10-17 10:18:40 +00:00
return nil
} ,
}
var walletSetDefault = & cli . Command {
2020-03-06 19:01:28 +00:00
Name : "set-default" ,
Usage : "Set default wallet address" ,
2020-03-04 21:46:00 +00:00
ArgsUsage : "[address]" ,
2019-10-17 10:18:40 +00:00
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return err
}
defer closer ( )
ctx := ReqContext ( cctx )
2023-01-25 12:13:56 +00:00
if cctx . NArg ( ) != 1 {
return IncorrectNumArgs ( cctx )
2019-10-17 10:18:40 +00:00
}
addr , err := address . NewFromString ( cctx . Args ( ) . First ( ) )
if err != nil {
return err
}
2022-10-05 07:47:31 +00:00
fmt . Println ( "Default address set to:" , addr )
2019-10-17 10:18:40 +00:00
return api . WalletSetDefault ( ctx , addr )
} ,
}
2019-10-08 09:17:03 +00:00
var walletExport = & cli . Command {
2020-03-06 19:01:28 +00:00
Name : "export" ,
Usage : "export keys" ,
2020-03-04 21:46:00 +00:00
ArgsUsage : "[address]" ,
2019-10-08 09:17:03 +00:00
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return err
}
defer closer ( )
ctx := ReqContext ( cctx )
2022-02-22 14:35:46 +00:00
afmt := NewAppFmt ( cctx . App )
2023-01-25 12:13:56 +00:00
if cctx . NArg ( ) != 1 {
return IncorrectNumArgs ( cctx )
2019-10-08 09:17:03 +00:00
}
addr , err := address . NewFromString ( cctx . Args ( ) . First ( ) )
if err != nil {
return err
}
2019-10-08 09:46:36 +00:00
ki , err := api . WalletExport ( ctx , addr )
if err != nil {
return err
}
b , err := json . Marshal ( ki )
2019-10-08 09:17:03 +00:00
if err != nil {
return err
}
2022-02-22 14:35:46 +00:00
afmt . Println ( hex . EncodeToString ( b ) )
2019-10-08 09:17:03 +00:00
return nil
} ,
}
var walletImport = & cli . Command {
2020-03-06 19:01:28 +00:00
Name : "import" ,
Usage : "import keys" ,
2020-03-04 21:46:00 +00:00
ArgsUsage : "[<path> (optional, will read from stdin if omitted)]" ,
2020-04-01 22:01:14 +00:00
Flags : [ ] cli . Flag {
& cli . StringFlag {
Name : "format" ,
Usage : "specify input format for key" ,
Value : "hex-lotus" ,
} ,
2020-08-06 20:34:11 +00:00
& cli . BoolFlag {
Name : "as-default" ,
Usage : "import the given key as your new default key" ,
} ,
2020-04-01 22:01:14 +00:00
} ,
2019-10-08 09:17:03 +00:00
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return err
}
defer closer ( )
ctx := ReqContext ( cctx )
2020-04-01 22:01:14 +00:00
var inpdata [ ] byte
2019-10-09 00:22:18 +00:00
if ! cctx . Args ( ) . Present ( ) || cctx . Args ( ) . First ( ) == "-" {
2023-06-27 19:29:58 +00:00
if term . IsTerminal ( int ( os . Stdin . Fd ( ) ) ) {
fmt . Print ( "Enter private key(not display in the terminal): " )
2023-08-21 07:10:47 +00:00
sigCh := make ( chan os . Signal , 1 )
// Notify the channel when SIGINT is received
2023-08-21 08:37:42 +00:00
signal . Notify ( sigCh , syscall . SIGINT , syscall . SIGTERM )
2023-08-21 07:10:47 +00:00
go func ( ) {
<- sigCh
fmt . Println ( "\nInterrupt signal received. Exiting..." )
os . Exit ( 1 )
} ( )
2023-06-27 19:29:58 +00:00
inpdata , err = term . ReadPassword ( int ( os . Stdin . Fd ( ) ) )
if err != nil {
return err
}
fmt . Println ( )
} else {
reader := bufio . NewReader ( os . Stdin )
indata , err := reader . ReadBytes ( '\n' )
if err != nil {
return err
}
inpdata = indata
2019-10-09 00:22:18 +00:00
}
} else {
2023-03-29 19:24:07 +00:00
fdata , err := os . ReadFile ( cctx . Args ( ) . First ( ) )
2019-10-09 00:22:18 +00:00
if err != nil {
return err
}
2020-04-01 22:01:14 +00:00
inpdata = fdata
2019-10-08 09:17:03 +00:00
}
2019-10-08 09:46:36 +00:00
var ki types . KeyInfo
2020-04-01 22:01:14 +00:00
switch cctx . String ( "format" ) {
case "hex-lotus" :
data , err := hex . DecodeString ( strings . TrimSpace ( string ( inpdata ) ) )
if err != nil {
return err
}
if err := json . Unmarshal ( data , & ki ) ; err != nil {
return err
}
case "json-lotus" :
if err := json . Unmarshal ( inpdata , & ki ) ; err != nil {
return err
}
case "gfc-json" :
var f struct {
KeyInfo [ ] struct {
PrivateKey [ ] byte
SigType int
}
}
if err := json . Unmarshal ( inpdata , & f ) ; err != nil {
return xerrors . Errorf ( "failed to parse go-filecoin key: %s" , err )
}
gk := f . KeyInfo [ 0 ]
ki . PrivateKey = gk . PrivateKey
switch gk . SigType {
case 1 :
2020-10-11 18:12:01 +00:00
ki . Type = types . KTSecp256k1
2020-04-01 22:01:14 +00:00
case 2 :
2020-10-11 18:12:01 +00:00
ki . Type = types . KTBLS
2020-04-01 22:01:14 +00:00
default :
return fmt . Errorf ( "unrecognized key type: %d" , gk . SigType )
}
default :
return fmt . Errorf ( "unrecognized format: %s" , cctx . String ( "format" ) )
2019-10-08 09:46:36 +00:00
}
addr , err := api . WalletImport ( ctx , & ki )
2019-10-08 09:17:03 +00:00
if err != nil {
return err
}
2020-08-06 20:34:11 +00:00
if cctx . Bool ( "as-default" ) {
if err := api . WalletSetDefault ( ctx , addr ) ; err != nil {
return fmt . Errorf ( "failed to set default key: %w" , err )
}
}
2019-10-29 12:54:26 +00:00
fmt . Printf ( "imported key %s successfully!\n" , addr )
2019-10-08 09:17:03 +00:00
return nil
} ,
}
2020-02-25 21:35:41 +00:00
var walletSign = & cli . Command {
Name : "sign" ,
Usage : "sign a message" ,
ArgsUsage : "<signing address> <hexMessage>" ,
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return err
}
defer closer ( )
ctx := ReqContext ( cctx )
2022-03-01 01:34:52 +00:00
afmt := NewAppFmt ( cctx . App )
2023-01-25 12:13:56 +00:00
if cctx . NArg ( ) != 2 {
return IncorrectNumArgs ( cctx )
2020-02-25 21:35:41 +00:00
}
addr , err := address . NewFromString ( cctx . Args ( ) . First ( ) )
if err != nil {
return err
}
msg , err := hex . DecodeString ( cctx . Args ( ) . Get ( 1 ) )
if err != nil {
return err
}
sig , err := api . WalletSign ( ctx , addr , msg )
if err != nil {
2023-09-19 08:57:35 +00:00
// Check if the address is a multisig address
act , actErr := api . StateGetActor ( ctx , addr , types . EmptyTSK )
if actErr == nil && builtin . IsMultisigActor ( act . Code ) {
2023-09-20 09:17:57 +00:00
return xerrors . Errorf ( "specified signer address is a multisig actor, it doesn’ t have keys to sign transactions. To send a message with a multisig, signers of the multisig need to propose and approve transactions." )
2023-09-19 08:57:35 +00:00
}
return xerrors . Errorf ( "failed to sign message: %w" , err )
2020-02-25 21:35:41 +00:00
}
2020-03-08 00:46:12 +00:00
sigBytes := append ( [ ] byte { byte ( sig . Type ) } , sig . Data ... )
2020-02-25 21:35:41 +00:00
2022-03-01 01:34:52 +00:00
afmt . Println ( hex . EncodeToString ( sigBytes ) )
2020-02-25 21:35:41 +00:00
return nil
} ,
}
2020-02-25 23:17:15 +00:00
var walletVerify = & cli . Command {
Name : "verify" ,
Usage : "verify the signature of a message" ,
ArgsUsage : "<signing address> <hexMessage> <signature>" ,
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return err
}
defer closer ( )
ctx := ReqContext ( cctx )
2022-02-22 14:35:46 +00:00
afmt := NewAppFmt ( cctx . App )
2023-01-25 12:13:56 +00:00
if cctx . NArg ( ) != 3 {
return IncorrectNumArgs ( cctx )
2020-02-25 23:17:15 +00:00
}
addr , err := address . NewFromString ( cctx . Args ( ) . First ( ) )
if err != nil {
return err
}
msg , err := hex . DecodeString ( cctx . Args ( ) . Get ( 1 ) )
if err != nil {
return err
}
sigBytes , err := hex . DecodeString ( cctx . Args ( ) . Get ( 2 ) )
if err != nil {
return err
}
2020-03-08 00:46:12 +00:00
var sig crypto . Signature
if err := sig . UnmarshalBinary ( sigBytes ) ; err != nil {
2020-02-25 23:17:15 +00:00
return err
}
2020-09-17 10:22:56 +00:00
ok , err := api . WalletVerify ( ctx , addr , msg , & sig )
if err != nil {
return err
}
if ok {
2022-02-22 14:35:46 +00:00
afmt . Println ( "valid" )
2020-02-25 23:17:15 +00:00
return nil
}
2022-02-22 14:35:46 +00:00
afmt . Println ( "invalid" )
2020-08-20 04:49:10 +00:00
return NewCliError ( "CLI Verify called with invalid signature" )
2020-02-25 23:17:15 +00:00
} ,
}
2020-06-05 23:04:23 +00:00
var walletDelete = & cli . Command {
2022-04-12 06:31:54 +00:00
Name : "delete" ,
2022-04-06 08:23:03 +00:00
Usage : "Soft delete an address from the wallet - hard deletion needed for permanent removal" ,
2020-06-05 23:04:23 +00:00
ArgsUsage : "<address> " ,
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return err
}
defer closer ( )
ctx := ReqContext ( cctx )
2023-01-25 12:13:56 +00:00
if cctx . NArg ( ) != 1 {
return IncorrectNumArgs ( cctx )
2020-06-05 23:04:23 +00:00
}
addr , err := address . NewFromString ( cctx . Args ( ) . First ( ) )
if err != nil {
return err
}
2022-10-05 07:47:31 +00:00
fmt . Println ( "Soft deleting address:" , addr )
fmt . Println ( "Hard deletion of the address in `~/.lotus/keystore` is needed for permanent removal" )
2020-06-05 23:04:23 +00:00
return api . WalletDelete ( ctx , addr )
} ,
}
2020-10-21 00:34:46 +00:00
2020-10-23 09:16:29 +00:00
var walletMarket = & cli . Command {
Name : "market" ,
Usage : "Interact with market balances" ,
Subcommands : [ ] * cli . Command {
walletMarketWithdraw ,
2021-01-06 13:27:28 +00:00
walletMarketAdd ,
2020-10-23 09:16:29 +00:00
} ,
}
var walletMarketWithdraw = & cli . Command {
Name : "withdraw" ,
2020-10-21 00:34:46 +00:00
Usage : "Withdraw funds from the Storage Market Actor" ,
ArgsUsage : "[amount (FIL) optional, otherwise will withdraw max available]" ,
Flags : [ ] cli . Flag {
& cli . StringFlag {
2021-01-07 08:35:30 +00:00
Name : "wallet" ,
Usage : "Specify address to withdraw funds to, otherwise it will use the default wallet address" ,
Aliases : [ ] string { "w" } ,
2020-10-21 00:34:46 +00:00
} ,
2020-10-23 12:54:52 +00:00
& cli . StringFlag {
Name : "address" ,
2021-01-07 08:35:30 +00:00
Usage : "Market address to withdraw from (account or miner actor address, defaults to --wallet address)" ,
2020-10-23 12:54:52 +00:00
Aliases : [ ] string { "a" } ,
} ,
2021-09-29 16:59:51 +00:00
& cli . IntFlag {
Name : "confidence" ,
Usage : "number of block confirmations to wait for" ,
Value : int ( build . MessageConfidence ) ,
} ,
2020-10-21 00:34:46 +00:00
} ,
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return xerrors . Errorf ( "getting node API: %w" , err )
}
defer closer ( )
ctx := ReqContext ( cctx )
2022-02-22 14:35:46 +00:00
afmt := NewAppFmt ( cctx . App )
2021-01-07 08:35:30 +00:00
var wallet address . Address
if cctx . String ( "wallet" ) != "" {
wallet , err = address . NewFromString ( cctx . String ( "wallet" ) )
2020-10-21 00:34:46 +00:00
if err != nil {
2020-10-23 09:16:29 +00:00
return xerrors . Errorf ( "parsing from address: %w" , err )
}
} else {
2021-01-07 08:35:30 +00:00
wallet , err = api . WalletDefaultAddress ( ctx )
2020-10-23 09:16:29 +00:00
if err != nil {
return xerrors . Errorf ( "getting default wallet address: %w" , err )
2020-10-21 00:34:46 +00:00
}
}
2021-01-07 08:35:30 +00:00
addr := wallet
2020-10-23 12:54:52 +00:00
if cctx . String ( "address" ) != "" {
addr , err = address . NewFromString ( cctx . String ( "address" ) )
if err != nil {
return xerrors . Errorf ( "parsing market address: %w" , err )
}
}
2021-01-07 08:35:30 +00:00
// Work out if there are enough unreserved, unlocked funds to withdraw
2020-10-23 09:16:29 +00:00
bal , err := api . StateMarketBalance ( ctx , addr , types . EmptyTSK )
2020-10-21 00:34:46 +00:00
if err != nil {
return xerrors . Errorf ( "getting market balance for address %s: %w" , addr . String ( ) , err )
}
2021-01-07 08:35:30 +00:00
reserved , err := api . MarketGetReserved ( ctx , addr )
if err != nil {
return xerrors . Errorf ( "getting market reserved amount for address %s: %w" , addr . String ( ) , err )
}
avail := big . Subtract ( big . Subtract ( bal . Escrow , bal . Locked ) , reserved )
notEnoughErr := func ( msg string ) error {
return xerrors . Errorf ( "%s; " +
"available (%s) = escrow (%s) - locked (%s) - reserved (%s)" ,
msg , types . FIL ( avail ) , types . FIL ( bal . Escrow ) , types . FIL ( bal . Locked ) , types . FIL ( reserved ) )
}
if avail . IsZero ( ) || avail . LessThan ( big . Zero ( ) ) {
avail = big . Zero ( )
return notEnoughErr ( "no funds available to withdraw" )
}
// Default to withdrawing all available funds
2020-10-21 00:34:46 +00:00
amt := avail
2021-01-07 08:35:30 +00:00
// If there was an amount argument, only withdraw that amount
2020-10-21 00:34:46 +00:00
if cctx . Args ( ) . Present ( ) {
f , err := types . ParseFIL ( cctx . Args ( ) . First ( ) )
if err != nil {
return xerrors . Errorf ( "parsing 'amount' argument: %w" , err )
}
amt = abi . TokenAmount ( f )
}
2021-01-07 08:35:30 +00:00
// Check the amount is positive
if amt . IsZero ( ) || amt . LessThan ( big . Zero ( ) ) {
return xerrors . Errorf ( "amount must be > 0" )
2020-10-21 00:34:46 +00:00
}
2021-01-07 08:35:30 +00:00
// Check there are enough available funds
if amt . GreaterThan ( avail ) {
msg := fmt . Sprintf ( "can't withdraw more funds than available; requested: %s" , types . FIL ( amt ) )
return notEnoughErr ( msg )
2020-10-21 00:34:46 +00:00
}
2021-01-07 08:35:30 +00:00
fmt . Printf ( "Submitting WithdrawBalance message for amount %s for address %s\n" , types . FIL ( amt ) , wallet . String ( ) )
smsg , err := api . MarketWithdraw ( ctx , wallet , addr , amt )
2020-10-21 00:34:46 +00:00
if err != nil {
2020-12-03 20:47:26 +00:00
return xerrors . Errorf ( "fund manager withdraw error: %w" , err )
2020-10-21 00:34:46 +00:00
}
2022-02-22 14:35:46 +00:00
afmt . Printf ( "WithdrawBalance message cid: %s\n" , smsg )
2020-10-21 00:34:46 +00:00
2021-09-29 16:59:51 +00:00
// wait for it to get mined into a block
wait , err := api . StateWaitMsg ( ctx , smsg , uint64 ( cctx . Int ( "confidence" ) ) )
if err != nil {
return err
}
// check it executed successfully
2022-09-14 18:53:11 +00:00
if wait . Receipt . ExitCode . IsError ( ) {
2022-02-22 14:35:46 +00:00
afmt . Println ( cctx . App . Writer , "withdrawal failed!" )
2021-09-29 16:59:51 +00:00
return err
}
2021-10-10 00:11:49 +00:00
nv , err := api . StateNetworkVersion ( ctx , wait . TipSet )
if err != nil {
2021-09-29 16:59:51 +00:00
return err
}
2021-10-10 00:11:49 +00:00
if nv >= network . Version14 {
var withdrawn abi . TokenAmount
if err := withdrawn . UnmarshalCBOR ( bytes . NewReader ( wait . Receipt . Return ) ) ; err != nil {
return err
}
2022-02-22 14:35:46 +00:00
afmt . Printf ( "Successfully withdrew %s \n" , types . FIL ( withdrawn ) )
2021-10-11 12:06:21 +00:00
if withdrawn . LessThan ( amt ) {
2021-10-27 00:51:40 +00:00
fmt . Printf ( "Note that this is less than the requested amount of %s \n" , types . FIL ( amt ) )
2021-10-10 00:11:49 +00:00
}
2021-09-29 16:59:51 +00:00
}
2020-10-21 00:34:46 +00:00
return nil
} ,
}
2021-01-06 13:27:28 +00:00
var walletMarketAdd = & cli . Command {
Name : "add" ,
Usage : "Add funds to the Storage Market Actor" ,
ArgsUsage : "<amount>" ,
Flags : [ ] cli . Flag {
& cli . StringFlag {
Name : "from" ,
Usage : "Specify address to move funds from, otherwise it will use the default wallet address" ,
Aliases : [ ] string { "f" } ,
} ,
& cli . StringFlag {
Name : "address" ,
Usage : "Market address to move funds to (account or miner actor address, defaults to --from address)" ,
Aliases : [ ] string { "a" } ,
} ,
} ,
Action : func ( cctx * cli . Context ) error {
api , closer , err := GetFullNodeAPI ( cctx )
if err != nil {
return xerrors . Errorf ( "getting node API: %w" , err )
}
defer closer ( )
ctx := ReqContext ( cctx )
2022-02-22 14:35:46 +00:00
afmt := NewAppFmt ( cctx . App )
2021-01-06 13:27:28 +00:00
// Get amount param
2023-01-26 09:13:39 +00:00
if cctx . NArg ( ) < 1 {
2023-01-25 12:13:56 +00:00
return IncorrectNumArgs ( cctx )
2021-01-06 13:27:28 +00:00
}
f , err := types . ParseFIL ( cctx . Args ( ) . First ( ) )
if err != nil {
return xerrors . Errorf ( "parsing 'amount' argument: %w" , err )
}
amt := abi . TokenAmount ( f )
// Get from param
var from address . Address
if cctx . String ( "from" ) != "" {
from , err = address . NewFromString ( cctx . String ( "from" ) )
if err != nil {
return xerrors . Errorf ( "parsing from address: %w" , err )
}
} else {
from , err = api . WalletDefaultAddress ( ctx )
if err != nil {
return xerrors . Errorf ( "getting default wallet address: %w" , err )
}
}
// Get address param
addr := from
if cctx . String ( "address" ) != "" {
addr , err = address . NewFromString ( cctx . String ( "address" ) )
if err != nil {
return xerrors . Errorf ( "parsing market address: %w" , err )
}
}
// Add balance to market actor
fmt . Printf ( "Submitting Add Balance message for amount %s for address %s\n" , types . FIL ( amt ) , addr )
smsg , err := api . MarketAddBalance ( ctx , from , addr , amt )
if err != nil {
return xerrors . Errorf ( "add balance error: %w" , err )
}
2022-02-22 14:35:46 +00:00
afmt . Printf ( "AddBalance message cid: %s\n" , smsg )
2021-01-06 13:27:28 +00:00
return nil
} ,
}