Skip to content

Commit

Permalink
feat: add --csv option to the lotus send cmd (#12892)
Browse files Browse the repository at this point in the history
  • Loading branch information
rjan90 authored Feb 14, 2025
1 parent 36b1aa3 commit 97dc186
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 155 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
- Exposed `StateGetNetworkParams` in the Lotus Gateway API ([filecoin-project/lotus#12881](https://github.com/filecoin-project/lotus/pull/12881))
- **BREAKING**: Removed `SupportedProofTypes` from `StateGetNetworkParams` response as it was unreliable and didn't match FVM's actual supported proofs ([filecoin-project/lotus#12881](https://github.com/filecoin-project/lotus/pull/12881))
- refactor(eth): attach ToFilecoinMessage converter to EthCall method for improved package/module import structure. This change also exports the converter as a public method, enhancing usability for developers utilizing Lotus as a library. ([filecoin-project/lotus#12844](https://github.com/filecoin-project/lotus/pull/12844))

- chore: switch to pure-go zstd decoder for snapshot imports. ([filecoin-project/lotus#12857](https://github.com/filecoin-project/lotus/pull/12857))

- feat: automatically detect if the genesis is zstd compressed. ([filecoin-project/lotus#12885](https://github.com/filecoin-project/lotus/pull/12885)
- `lotus send` now supports `--csv` option for sending multiple transactions. ([filecoin-project/lotus#12892](https://github.com/filecoin-project/lotus/pull/12892))

- chore: upgrade to the latest go-f3 and allow F3 chain exchange topics ([filecoin-project/lotus#12893](https://github.com/filecoin-project/lotus/pull/12893)

Expand Down
138 changes: 138 additions & 0 deletions cli/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package cli

import (
"bytes"
"encoding/csv"
"encoding/hex"
"fmt"
"os"
"strconv"
"strings"

"github.com/urfave/cli/v2"
Expand Down Expand Up @@ -69,8 +72,16 @@ var SendCmd = &cli.Command{
Name: "force",
Usage: "Deprecated: use global 'force-send'",
},
&cli.StringFlag{
Name: "csv",
Usage: "send multiple transactions from a CSV file (format: Recipient,FIL,Method,Params)",
},
},
Action: func(cctx *cli.Context) error {
if csvFile := cctx.String("csv"); csvFile != "" {
return handleCSVSend(cctx, csvFile)
}

if cctx.IsSet("force") {
fmt.Println("'force' flag is deprecated, use global flag 'force-send'")
}
Expand Down Expand Up @@ -244,3 +255,130 @@ var SendCmd = &cli.Command{
return nil
},
}

func handleCSVSend(cctx *cli.Context, csvFile string) error {
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck

ctx := ReqContext(cctx)

var fromAddr address.Address
if from := cctx.String("from"); from != "" {
addr, err := address.NewFromString(from)
if err != nil {
return err
}
fromAddr = addr
} else {
defaddr, err := srv.FullNodeAPI().WalletDefaultAddress(ctx)
if err != nil {
return fmt.Errorf("failed to get default address: %w", err)
}
fromAddr = defaddr
}

// Print sending address
_, _ = fmt.Fprintf(cctx.App.Writer, "Sending messages from: %s\n", fromAddr.String())

fileReader, err := os.Open(csvFile)
if err != nil {
return xerrors.Errorf("read csv: %w", err)
}
defer func() {
if err := fileReader.Close(); err != nil {
log.Errorf("failed to close csv file: %v", err)
}
}()

r := csv.NewReader(fileReader)
records, err := r.ReadAll()
if err != nil {
return xerrors.Errorf("read csv: %w", err)
}

// Validate header
if len(records) == 0 ||
len(records[0]) != 4 ||
strings.TrimSpace(records[0][0]) != "Recipient" ||
strings.TrimSpace(records[0][1]) != "FIL" ||
strings.TrimSpace(records[0][2]) != "Method" ||
strings.TrimSpace(records[0][3]) != "Params" {
return xerrors.Errorf("expected header row to be \"Recipient,FIL,Method,Params\"")
}

// First pass: validate and build params
var sendParams []SendParams
totalAmount := abi.NewTokenAmount(0)

for i, e := range records[1:] {
if len(e) != 4 {
return xerrors.Errorf("row %d has %d fields, expected 4", i, len(e))
}

var params SendParams
params.From = fromAddr

// Parse recipient
var err error
params.To, err = address.NewFromString(e[0])
if err != nil {
return xerrors.Errorf("failed to parse address in row %d: %w", i, err)
}

// Parse value
val, err := types.ParseFIL(e[1])
if err != nil {
return xerrors.Errorf("failed to parse amount in row %d: %w", i, err)
}
params.Val = abi.TokenAmount(val)
totalAmount = types.BigAdd(totalAmount, params.Val)

// Parse method
method, err := strconv.Atoi(strings.TrimSpace(e[2]))
if err != nil {
return xerrors.Errorf("failed to parse method number in row %d: %w", i, err)
}
params.Method = abi.MethodNum(method)

// Parse params
if strings.TrimSpace(e[3]) != "nil" {
params.Params, err = hex.DecodeString(strings.TrimSpace(e[3]))
if err != nil {
return xerrors.Errorf("failed to parse hex params in row %d: %w", i, err)
}
}

sendParams = append(sendParams, params)
}

// Check sender balance
senderBalance, err := srv.FullNodeAPI().WalletBalance(ctx, fromAddr)
if err != nil {
return xerrors.Errorf("failed to get sender balance: %w", err)
}

if senderBalance.LessThan(totalAmount) {
return xerrors.Errorf("insufficient funds: need %s FIL, have %s FIL",
types.FIL(totalAmount), types.FIL(senderBalance))
}

// Second pass: perform sends
for i, params := range sendParams {
proto, err := srv.MessageForSend(ctx, params)
if err != nil {
return xerrors.Errorf("creating message prototype for row %d: %w", i, err)
}

sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return xerrors.Errorf("sending message for row %d: %w", i, err)
}

fmt.Printf("Sent message %d: %s\n", i, sm.Cid())
}

return nil
}
1 change: 0 additions & 1 deletion cmd/lotus-shed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ func main() {
fr32Cmd,
chainCmd,
balancerCmd,
sendCsvCmd,
terminationsCmd,
migrationsCmd,
diffCmd,
Expand Down
152 changes: 0 additions & 152 deletions cmd/lotus-shed/send-csv.go

This file was deleted.

1 change: 1 addition & 0 deletions documentation/en/cli-lotus.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ OPTIONS:
--params-json value specify invocation parameters in json
--params-hex value specify invocation parameters in hex
--force Deprecated: use global 'force-send' (default: false)
--csv value send multiple transactions from a CSV file (format: Recipient,FIL,Method,Params)
--help, -h show help
```

Expand Down

0 comments on commit 97dc186

Please sign in to comment.