Skip to content

Commit

Permalink
Approve SD for repay
Browse files Browse the repository at this point in the history
  • Loading branch information
batphonghan committed Dec 5, 2023
1 parent 98dacbe commit 0638c7a
Show file tree
Hide file tree
Showing 7 changed files with 358 additions and 0 deletions.
48 changes: 48 additions & 0 deletions shared/services/stader/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -727,3 +727,51 @@ func (c *Client) GetSDStatus(numValidators *big.Int) (api.GetSdStatusResponse, e

return response, nil
}

// Get the node's SD allowance
func (c *Client) GetSDPoolUtilitySdAllowance() (api.SDPoolUtilitySdAllowanceResponse, error) {
responseBytes, err := c.callAPI(fmt.Sprintf("node utility-sd-allowance"))
if err != nil {
return api.SDPoolUtilitySdAllowanceResponse{}, fmt.Errorf("could not get node deposit SD allowance: %w", err)
}
var response api.SDPoolUtilitySdAllowanceResponse

Check failure on line 737 in shared/services/stader/node.go

View workflow job for this annotation

GitHub Actions / build

declarations should never be cuddled (wsl)
if err := json.Unmarshal(responseBytes, &response); err != nil {

Check failure on line 738 in shared/services/stader/node.go

View workflow job for this annotation

GitHub Actions / build

only one cuddle assignment allowed before if statement (wsl)
return api.SDPoolUtilitySdAllowanceResponse{}, fmt.Errorf("could not decode node deposit SD allowance response: %w", err)
}
if response.Error != "" {

Check failure on line 741 in shared/services/stader/node.go

View workflow job for this annotation

GitHub Actions / build

if statements should only be cuddled with assignments (wsl)
return api.SDPoolUtilitySdAllowanceResponse{}, fmt.Errorf("could not get node deposit SD allowance: %s", response.Error)
}
return response, nil

Check failure on line 744 in shared/services/stader/node.go

View workflow job for this annotation

GitHub Actions / build

return statements should not be cuddled if block has more than two lines (wsl)
}

// Approve SD for utility contract
func (c *Client) SDPoolUtilitySdApprove(amountWei *big.Int) (api.NodeDepositSdApproveResponse, error) {
responseBytes, err := c.callAPI(fmt.Sprintf("node utility-approve-sd %s", amountWei.String()))
if err != nil {
return api.NodeDepositSdApproveResponse{}, fmt.Errorf("could not approve SD for staking: %w", err)
}
var response api.NodeDepositSdApproveResponse
if err := json.Unmarshal(responseBytes, &response); err != nil {
return api.NodeDepositSdApproveResponse{}, fmt.Errorf("could not decode deposit node SD approve response: %w", err)
}
if response.Error != "" {
return api.NodeDepositSdApproveResponse{}, fmt.Errorf("could not approve SD for staking: %s", response.Error)
}
return response, nil
}

// Get the gas estimate for approving new SD interaction
func (c *Client) SDPoolUtilitySdApproveGas(amountWei *big.Int) (api.NodeDepositSdApproveGasResponse, error) {
responseBytes, err := c.callAPI(fmt.Sprintf("node utility-approve-sd-gas %s", amountWei.String()))
if err != nil {
return api.NodeDepositSdApproveGasResponse{}, fmt.Errorf("could not get new SD approval gas: %w", err)
}
var response api.NodeDepositSdApproveGasResponse
if err := json.Unmarshal(responseBytes, &response); err != nil {
return api.NodeDepositSdApproveGasResponse{}, fmt.Errorf("could not decode utility SD approve gas response: %w", err)
}
if response.Error != "" {
return api.NodeDepositSdApproveGasResponse{}, fmt.Errorf("could not get utility SD approval gas: %s", response.Error)
}
return response, nil
}
6 changes: 6 additions & 0 deletions shared/types/api/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ type NodeDepositSdAllowanceResponse struct {
Allowance *big.Int `json:"allowance"`
}

type SDPoolUtilitySdAllowanceResponse struct {
Status string `json:"status"`
Error string `json:"error"`
Allowance *big.Int `json:"allowance"`
}

type CanNodeDepositResponse struct {
Status string `json:"status"`
Error string `json:"error"`
Expand Down
76 changes: 76 additions & 0 deletions stader-cli/node/approve-sd.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,79 @@ func nodeApproveSdWithAmount(staderClient *stader.Client, amountWei *big.Int, au

return nil
}

func nodeApproveUtilitySd(c *cli.Context) error {
staderClient, err := stader.NewClientFromCtx(c)
if err != nil {
return err
}
defer staderClient.Close()

// Check and assign the EC status
err = cliutils.CheckClientStatus(staderClient)
if err != nil {
return err
}

// If a custom nonce is set, print the multi-transaction warning
if c.GlobalUint64("nonce") != 0 {
cliutils.PrintMultiTransactionNonceWarning()
}

amountInString := c.String("amount")

amount, err := strconv.ParseFloat(amountInString, 64)
if err != nil {
return err
}

amountWei := eth.EthToWei(amount)

autoConfirm := c.Bool("yes")

nonce := c.GlobalUint64("nonce")

if nonce != 0 {
cliutils.PrintMultiTransactionNonceWarning()
}

// Get approval gas
approvalGas, err := staderClient.SDPoolUtilitySdApproveGas(amountWei)
if err != nil {
return err
}

// Assign max fees
err = gas.AssignMaxFeeAndLimit(approvalGas.GasInfo, staderClient, autoConfirm)
if err != nil {
return err
}

// Prompt for confirmation
if !(autoConfirm || cliutils.Confirm("Do you want to approve SD to be spent by the Utility Contract?")) {
fmt.Println("Cancelled.")
return nil
}

response, err := staderClient.SDPoolUtilitySdApprove(amountWei)
if err != nil {
return err
}

hash := response.ApproveTxHash

fmt.Println("Approving SD..")
cliutils.PrintTransactionHash(staderClient, hash)

if _, err = staderClient.WaitForTransaction(hash); err != nil {
return err
}

fmt.Println("Successfully approved SD.")

// If a custom nonce is set, increment it for the next transaction
if nonce != 0 {
staderClient.IncrementCustomNonce()
}
return nil
}
24 changes: 24 additions & 0 deletions stader-cli/node/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,30 @@ func RegisterCommands(app *cli.App, name string, aliases []string) {
return repayExcessSD(c)
},
},
{
Name: "approve-utility-sd",
Aliases: []string{"aus"},
Usage: "Approve SD for utility pool",
UsageText: "stader-cli node approve-utility-sd [options]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "amount, a",
Usage: "The amount of SD to approve",
},
cli.BoolFlag{
Name: "yes, y",
Usage: "Automatically confirm SD approve",
},
},
Action: func(c *cli.Context) error {
if _, err := cliutils.ValidatePositiveEthAmount("sd amount", c.String("amount")); err != nil {
return err
}

// Run
return nodeApproveUtilitySd(c)
},
},
},
})
}
14 changes: 14 additions & 0 deletions stader-cli/node/repay-sd.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ func repaySD(c *cli.Context) error {

amountWei := eth.EthToWei(amount)

// Check allowance
allowance, err := staderClient.GetSDPoolUtilitySdAllowance()
if err != nil {
return err
}

if allowance.Allowance.Cmp(amountWei) < 0 {
fmt.Println("Before repay SD, you must first give the utility contract approval to interact with your SD. Amount to approve: ", eth.WeiToEth(amountWei))
err = nodeApproveUtilitySd(c)
if err != nil {
return err
}
}

canClaimElRewardsResponse, err := staderClient.CanRepaySd(amountWei)
if err != nil {
return err
Expand Down
54 changes: 54 additions & 0 deletions stader/api/node/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,60 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) {
return nil
},
},
{
Name: "utility-sd-allowance",
Usage: "Get the node's SD allowance for the utility contract",
UsageText: "stader-cli api node utility-sd-allowance",
Action: func(c *cli.Context) error {
// Validate args
if err := cliutils.ValidateArgCount(c, 0); err != nil {
return err
}

// Run
api.PrintResponse(utilityAllowanceSd(c))
return nil

Check failure on line 833 in stader/api/node/commands.go

View workflow job for this annotation

GitHub Actions / build

unnecessary trailing newline (whitespace)
},
},
{
Name: "utility-approve-sd",
Usage: "Get the node's SD allowance for the utility contract",
UsageText: "stader-cli api node utility-approve-sd",
Action: func(c *cli.Context) error {
// Validate args
if err := cliutils.ValidateArgCount(c, 1); err != nil {
return err
}
amountWei, err := cliutils.ValidatePositiveWeiAmount("amount", c.Args().Get(0))
if err != nil {
return err
}

// Run
api.PrintResponse(utilityApproveSd(c, amountWei))
return nil
},
},
{
Name: "utility-approve-sd-gas",
Usage: "Get the gas info of node approve SD for the utility contract",
UsageText: "stader-cli api node utility-approve-sd-gas",
Action: func(c *cli.Context) error {
// Validate args
if err := cliutils.ValidateArgCount(c, 1); err != nil {
return err
}
amountWei, err := cliutils.ValidatePositiveWeiAmount("amount", c.Args().Get(0))
if err != nil {
return err
}

// Run
api.PrintResponse(getUtilitySdApprovalGas(c, amountWei))
return nil
},
},
},
})
}
136 changes: 136 additions & 0 deletions stader/api/node/utility-sd-allowance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package node

import (
"fmt"
"math/big"

"github.com/stader-labs/stader-node/stader-lib/tokens"
"github.com/urfave/cli"

"github.com/stader-labs/stader-node/shared/services"
"github.com/stader-labs/stader-node/shared/types/api"
"github.com/stader-labs/stader-node/shared/utils/eth1"
)

func utilityAllowanceSd(c *cli.Context) (*api.SDPoolUtilitySdAllowanceResponse, error) {
// Get services
if err := services.RequireNodeRegistered(c); err != nil {
return nil, err
}

w, err := services.GetWallet(c)
if err != nil {
return nil, err
}

sdt, err := services.GetSdTokenContract(c)
if err != nil {
return nil, err
}

sduAddress, err := services.GetSdUtilityAddress(c)
if err != nil {
return nil, err
}

// Response
response := api.SDPoolUtilitySdAllowanceResponse{}

// Get node account
account, err := w.GetNodeAccount()
if err != nil {
return nil, err
}

allowance, err := tokens.Allowance(sdt, account.Address, sduAddress, nil)
if err != nil {
return nil, err
}

response.Allowance = allowance

return &response, nil
}

func utilityApproveSd(c *cli.Context, amountWei *big.Int) (*api.NodeDepositSdApproveResponse, error) {
// Get services
if err := services.RequireNodeRegistered(c); err != nil {
return nil, err
}

w, err := services.GetWallet(c)
if err != nil {
return nil, err
}

sdt, err := services.GetSdTokenContract(c)
if err != nil {
return nil, err
}

sduAddress, err := services.GetSdUtilityAddress(c)
if err != nil {
return nil, err
}
// Response
response := api.NodeDepositSdApproveResponse{}

opts, err := w.GetNodeAccountTransactor()
if err != nil {
return nil, err
}

err = eth1.CheckForNonceOverride(c, opts)
if err != nil {
return nil, fmt.Errorf("Error checking for nonce override: %w", err)
}

hash, err := tokens.Approve(sdt, sduAddress, amountWei, opts)
if err != nil {
return nil, err
}

response.ApproveTxHash = hash

// Return response
return &response, nil
}

func getUtilitySdApprovalGas(c *cli.Context, amountWei *big.Int) (*api.NodeDepositSdApproveGasResponse, error) {
// Get services
if err := services.RequireNodeWallet(c); err != nil {
return nil, err
}

w, err := services.GetWallet(c)
if err != nil {
return nil, err
}

sdt, err := services.GetSdTokenContract(c)
if err != nil {
return nil, err
}

utilityAddress, err := services.GetPoolUtilsAddress(c)
if err != nil {
return nil, err
}

// Response
response := api.NodeDepositSdApproveGasResponse{}

// Get gas estimates
opts, err := w.GetNodeAccountTransactor()
if err != nil {
return nil, err
}

gasInfo, err := tokens.EstimateApproveGas(sdt, utilityAddress, amountWei, opts)
if err != nil {
return nil, err
}

response.GasInfo = gasInfo
return &response, nil
}

0 comments on commit 0638c7a

Please sign in to comment.