Repository: onflow/flow-cli
Pull request #439 (2022-01-18 16:05:30)

Update staking-info command
Closes https://github.com/onflow/flow-cli/issues/435

## Description

The "Total Stake" field was removed from NodeInfo during a core contracts [change](https://github.com/onflow/flow-core-contracts/commit/33b80c5ab9b9168f3975b268c164f3a952ef4e1e?diff=unified#diff-65336be374bb3fc9ad7b822243e065a389d73e758c7c16223e52fc3181cea59bL288)

This PR adds it back by fetching it individually for each nodeID.

The two alternative solutions I have considered are:

- Add a script to core contracts that is a combination of three scripts that fetches all NodeInfos and DelegationInfos from the address and the locked address and for each nodeInfo also outputs its total stake.
- Have node total stake as a separate command.

~An update is also needed to the scripts that are being used. This is addressed in this core-contracts [PR](https://github.com/onflow/flow-core-contracts/pull/270). The core-contracts update can be included in this PR or can be added with a separate PR later.~ After talking to Josh, I realised we can just use different scripts to get staking/delegation info.

The output formatting was changed a bit to account for the possibility for multiple stakes and delegations:
![image](https://user-images.githubusercontent.com/67895329/149976207-3bd94113-0cf4-49c3-bcd2-7f9610932240.png)

______

For contributor use:

- [x] Targeted PR against `master` branch
- [x] Linked to Github issue with discussion and accepted design OR link to spec that describes this work
- [x] Code follows the [standards mentioned here](https://github.com/onflow/flow-cli/blob/master/CONTRIBUTING.md#styleguides)
- [x] Updated relevant documentation
- [x] Re-reviewed `Files changed` in the Github PR explorer
- [ ] Added appropriate labels



Patch:
diff --git a/docs/account-staking-info.md b/docs/account-staking-info.md
index 952d2f4b..3e847028 100644
--- a/docs/account-staking-info.md
+++ b/docs/account-staking-info.md
@@ -16,29 +16,29 @@ flow accounts staking-info <address>
 > accounts staking-info 535b975637fb6bee --host access.testnet.nodes.onflow.org:9000
 
 Account Staking Info:
-ID: 			 "ca00101101010100001011010101010101010101010101011010101010101010"
-Initial Weight: 	 100
-Networking Address: 	 "ca00101101010100001011010101010101010101010101011010101010101010"
-Networking Key: 	 "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010"
-Role: 			 1
-Staking Key: 		 "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010"
-Tokens Committed: 	 0.00000000
-Tokens To Unstake: 	 0.00000000
-Tokens Rewarded: 	 82627.77000000
-Tokens Staked: 		 250000.00000000
-Tokens Unstaked: 	 0.00000000
-Tokens Unstaking: 	 0.00000000
-Total Tokens Staked: 	 250000.00000000
+    ID: 			 "ca00101101010100001011010101010101010101010101011010101010101010"
+    Initial Weight: 	 100
+    Networking Address: 	 "ca00101101010100001011010101010101010101010101011010101010101010"
+    Networking Key: 	 "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010"
+    Role: 			 1
+    Staking Key: 		 "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010"
+    Tokens Committed: 	 0.00000000
+    Tokens To Unstake: 	 0.00000000
+    Tokens Rewarded: 	 82627.77000000
+    Tokens Staked: 		 250000.00000000
+    Tokens Unstaked: 	 0.00000000
+    Tokens Unstaking: 	 0.00000000
+    Node Total Stake (including delegators):    250000.00000000
 
 
 Account Delegation Info:
-ID: 			 7
-Tokens Committed: 	 0.00000000
-Tokens To Unstake: 	 0.00000000
-Tokens Rewarded: 	 30397.81936000
-Tokens Staked: 		 100000.00000000
-Tokens Unstaked: 	 0.00000000
-Tokens Unstaking: 	 0.00000000
+    ID: 			 7
+    Tokens Committed: 	 0.00000000
+    Tokens To Unstake: 	 0.00000000
+    Tokens Rewarded: 	 30397.81936000
+    Tokens Staked: 		 100000.00000000
+    Tokens Unstaked: 	 0.00000000
+    Tokens Unstaking: 	 0.00000000
 
 ```
 
diff --git a/docs/developer-updates/release-notes-v17.md b/docs/developer-updates/release-notes-v17.md
index 1ab984c7..aedddeed 100644
--- a/docs/developer-updates/release-notes-v17.md
+++ b/docs/developer-updates/release-notes-v17.md
@@ -152,29 +152,29 @@ New command to fetch staking info from the account was added. Read more about it
 > accounts staking-info 535b975637fb6bee --host access.testnet.nodes.onflow.org:9000
 
 Account Staking Info:
-ID: 			 "ca00101101010100001011010101010101010101010101011010101010101010"
-Initial Weight: 	 100
-Networking Address: 	 "ca00101101010100001011010101010101010101010101011010101010101010"
-Networking Key: 	 "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010"
-Role: 			 1
-Staking Key: 		 "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010"
-Tokens Committed: 	 0.00000000
-Tokens To Unstake: 	 0.00000000
-Tokens Rewarded: 	 82627.77000000
-Tokens Staked: 		 250000.00000000
-Tokens Unstaked: 	 0.00000000
-Tokens Unstaking: 	 0.00000000
-Total Tokens Staked: 	 250000.00000000
+        ID: 			 "ca00101101010100001011010101010101010101010101011010101010101010"
+        Initial Weight: 	 100
+        Networking Address: 	 "ca00101101010100001011010101010101010101010101011010101010101010"
+        Networking Key: 	 "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010"
+        Role: 			 1
+        Staking Key: 		 "ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010ca00101101010100001011010101010101010101010101011010101010101010"
+        Tokens Committed: 	 0.00000000
+        Tokens To Unstake: 	 0.00000000
+        Tokens Rewarded: 	 82627.77000000
+        Tokens Staked: 		 250000.00000000
+        Tokens Unstaked: 	 0.00000000
+        Tokens Unstaking: 	 0.00000000
+        Node Total Stake (including delegators):    250000.00000000
 
 
 Account Delegation Info:
-ID: 			 7
-Tokens Committed: 	 0.00000000
-Tokens To Unstake: 	 0.00000000
-Tokens Rewarded: 	 30397.81936000
-Tokens Staked: 		 100000.00000000
-Tokens Unstaked: 	 0.00000000
-Tokens Unstaking: 	 0.00000000
+        ID: 			 7
+        Tokens Committed: 	 0.00000000
+        Tokens To Unstake: 	 0.00000000
+        Tokens Rewarded: 	 30397.81936000
+        Tokens Staked: 		 100000.00000000
+        Tokens Unstaked: 	 0.00000000
+        Tokens Unstaking: 	 0.00000000
 
 ```
 
diff --git a/internal/accounts/staking-info.go b/internal/accounts/staking-info.go
index 45b1f554..93446548 100644
--- a/internal/accounts/staking-info.go
+++ b/internal/accounts/staking-info.go
@@ -24,7 +24,6 @@ import (
 
 	"github.com/onflow/flow-go-sdk"
 
-	"github.com/onflow/cadence"
 	"github.com/spf13/cobra"
 
 	"github.com/onflow/flow-cli/internal/command"
@@ -61,18 +60,18 @@ func stakingInfo(
 		return nil, err
 	}
 
-	return &StakingResult{*staking, *delegation}, nil
+	return &StakingResult{staking, delegation}, nil
 }
 
 type StakingResult struct {
-	staking    cadence.Value
-	delegation cadence.Value
+	staking    []map[string]interface{} // stake as FlowIDTableStaking.NodeInfo
+	delegation []map[string]interface{} // delegation as FlowIDTableStaking.DelegatorInfo
 }
 
 func (r *StakingResult) JSON() interface{} {
 	result := make(map[string]interface{})
-	result["staking"] = flowkit.NewStakingInfoFromValue(r.staking)
-	result["delegation"] = flowkit.NewStakingInfoFromValue(r.delegation)
+	result["staking"] = r.staking
+	result["delegation"] = r.delegation
 
 	return result
 }
@@ -81,34 +80,46 @@ func (r *StakingResult) String() string {
 	var b bytes.Buffer
 	writer := util.CreateTabWriter(&b)
 
-	_, _ = fmt.Fprintf(writer, "Account Staking Info:\n")
-
-	stakingInfo := flowkit.NewStakingInfoFromValue(r.staking)
-
-	_, _ = fmt.Fprintf(writer, "ID: \t %v\n", stakingInfo["id"])
-	_, _ = fmt.Fprintf(writer, "Initial Weight: \t %v\n", stakingInfo["initialWeight"])
-	_, _ = fmt.Fprintf(writer, "Networking Address: \t %v\n", stakingInfo["networkingAddress"])
-	_, _ = fmt.Fprintf(writer, "Networking Key: \t %v\n", stakingInfo["networkingKey"])
-	_, _ = fmt.Fprintf(writer, "Role: \t %v\n", stakingInfo["role"])
-	_, _ = fmt.Fprintf(writer, "Staking Key: \t %v\n", stakingInfo["stakingKey"])
-	_, _ = fmt.Fprintf(writer, "Tokens Committed: \t %v\n", stakingInfo["tokensCommitted"])
-	_, _ = fmt.Fprintf(writer, "Tokens To Unstake: \t %v\n", stakingInfo["tokensRequestedToUnstake"])
-	_, _ = fmt.Fprintf(writer, "Tokens Rewarded: \t %v\n", stakingInfo["tokensRewarded"])
-	_, _ = fmt.Fprintf(writer, "Tokens Staked: \t %v\n", stakingInfo["tokensStaked"])
-	_, _ = fmt.Fprintf(writer, "Tokens Unstaked: \t %v\n", stakingInfo["tokensUnstaked"])
-	_, _ = fmt.Fprintf(writer, "Tokens Unstaking: \t %v\n", stakingInfo["tokensUnstaking"])
-	_, _ = fmt.Fprintf(writer, "Total Tokens Staked: \t %v\n", stakingInfo["totalTokensStaked"])
-
-	delegationStakingInfo := flowkit.NewStakingInfoFromValue(r.delegation)
-
-	_, _ = fmt.Fprintf(writer, "\n\nAccount Delegation Info:\n")
-	_, _ = fmt.Fprintf(writer, "ID: \t %v\n", delegationStakingInfo["id"])
-	_, _ = fmt.Fprintf(writer, "Tokens Committed: \t %v\n", delegationStakingInfo["tokensCommitted"])
-	_, _ = fmt.Fprintf(writer, "Tokens To Unstake: \t %v\n", delegationStakingInfo["tokensRequestedToUnstake"])
-	_, _ = fmt.Fprintf(writer, "Tokens Rewarded: \t %v\n", delegationStakingInfo["tokensRewarded"])
-	_, _ = fmt.Fprintf(writer, "Tokens Staked: \t %v\n", delegationStakingInfo["tokensStaked"])
-	_, _ = fmt.Fprintf(writer, "Tokens Unstaked: \t %v\n", delegationStakingInfo["tokensUnstaked"])
-	_, _ = fmt.Fprintf(writer, "Tokens Unstaking: \t %v\n", delegationStakingInfo["tokensUnstaking"])
+	if len(r.staking) != 0 {
+		_, _ = fmt.Fprintf(writer, "Account staking info:\n")
+
+		for _, stakingInfo := range r.staking {
+			_, _ = fmt.Fprintf(writer, "\tID: \t %v\n", stakingInfo["id"])
+			_, _ = fmt.Fprintf(writer, "\tInitial Weight: \t %v\n", stakingInfo["initialWeight"])
+			_, _ = fmt.Fprintf(writer, "\tNetworking Address: \t %v\n", stakingInfo["networkingAddress"])
+			_, _ = fmt.Fprintf(writer, "\tNetworking Key: \t %v\n", stakingInfo["networkingKey"])
+			_, _ = fmt.Fprintf(writer, "\tRole: \t %v\n", stakingInfo["role"])
+			_, _ = fmt.Fprintf(writer, "\tStaking Key: \t %v\n", stakingInfo["stakingKey"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Committed: \t %v\n", stakingInfo["tokensCommitted"])
+			_, _ = fmt.Fprintf(writer, "\tTokens To Unstake: \t %v\n", stakingInfo["tokensRequestedToUnstake"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Rewarded: \t %v\n", stakingInfo["tokensRewarded"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Staked: \t %v\n", stakingInfo["tokensStaked"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Unstaked: \t %v\n", stakingInfo["tokensUnstaked"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Unstaking: \t %v\n", stakingInfo["tokensUnstaking"])
+			_, _ = fmt.Fprintf(writer, "\tNode Total Stake (including delegators): \t %v\n", stakingInfo["nodeTotalStake"])
+			_, _ = fmt.Fprintf(writer, "\n")
+		}
+	} else {
+		_, _ = fmt.Fprintf(writer, "Account has no stakes.\n")
+	}
+
+	if len(r.delegation) != 0 {
+		_, _ = fmt.Fprintf(writer, "\nAccount delegation info:\n")
+
+		for _, delegationStakingInfo := range r.delegation {
+			_, _ = fmt.Fprintf(writer, "\tID: \t %v\n", delegationStakingInfo["id"])
+			_, _ = fmt.Fprintf(writer, "\tNode ID: \t %v\n", delegationStakingInfo["nodeID"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Committed: \t %v\n", delegationStakingInfo["tokensCommitted"])
+			_, _ = fmt.Fprintf(writer, "\tTokens To Unstake: \t %v\n", delegationStakingInfo["tokensRequestedToUnstake"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Rewarded: \t %v\n", delegationStakingInfo["tokensRewarded"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Staked: \t %v\n", delegationStakingInfo["tokensStaked"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Unstaked: \t %v\n", delegationStakingInfo["tokensUnstaked"])
+			_, _ = fmt.Fprintf(writer, "\tTokens Unstaking: \t %v\n", delegationStakingInfo["tokensUnstaking"])
+			_, _ = fmt.Fprintf(writer, "\n")
+		}
+	} else {
+		_, _ = fmt.Fprintf(writer, "Account has no delegations.\n")
+	}
 
 	writer.Flush()
 	return b.String()
diff --git a/internal/transactions/build.go b/internal/transactions/build.go
index 64b771c4..fc3e1a2c 100644
--- a/internal/transactions/build.go
+++ b/internal/transactions/build.go
@@ -112,7 +112,6 @@ func build(
 		buildFlags.GasLimit,
 		transactionArgs,
 		globalFlags.Network,
-		globalFlags.Yes,
 	)
 	if err != nil {
 		return nil, err
diff --git a/internal/transactions/send-signed.go b/internal/transactions/send-signed.go
index 7970b924..29d91c4f 100644
--- a/internal/transactions/send-signed.go
+++ b/internal/transactions/send-signed.go
@@ -50,7 +50,7 @@ var SendSignedCommand = &command.Command{
 func sendSigned(
 	args []string,
 	readerWriter flowkit.ReaderWriter,
-	globalFlags command.GlobalFlags,
+	_ command.GlobalFlags,
 	services *services.Services,
 	_ *flowkit.State,
 ) (command.Result, error) {
@@ -61,7 +61,7 @@ func sendSigned(
 		return nil, fmt.Errorf("error loading transaction payload: %w", err)
 	}
 
-	tx, result, err := services.Transactions.SendSigned(code, globalFlags.Yes)
+	tx, result, err := services.Transactions.SendSigned(code)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/flowkit/gateway/grpc.go b/pkg/flowkit/gateway/grpc.go
index 57ecb49b..ce7a7221 100644
--- a/pkg/flowkit/gateway/grpc.go
+++ b/pkg/flowkit/gateway/grpc.go
@@ -29,6 +29,7 @@ import (
 	"github.com/onflow/flow-go-sdk"
 	"github.com/onflow/flow-go-sdk/client"
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
 )
 
 // maxGRPCMessageSize 16mb, matching the value set in onflow/flow-go
@@ -46,7 +47,7 @@ func NewGrpcGateway(host string) (*GrpcGateway, error) {
 
 	gClient, err := client.New(
 		host,
-		grpc.WithInsecure(),
+		grpc.WithTransportCredentials(insecure.NewCredentials()),
 		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxGRPCMessageSize)),
 	)
 	ctx := context.Background()
diff --git a/pkg/flowkit/output/prompt.go b/pkg/flowkit/output/prompt.go
index 21a43924..bb96355c 100644
--- a/pkg/flowkit/output/prompt.go
+++ b/pkg/flowkit/output/prompt.go
@@ -32,19 +32,7 @@ import (
 	"github.com/manifoldco/promptui"
 )
 
-func ApproveTransactionForSigningPrompt(transaction *flowkit.Transaction) bool {
-	return ApproveTransactionPrompt(transaction, "⚠️  Do you want to SIGN this transaction?")
-}
-
-func ApproveTransactionForBuildingPrompt(transaction *flowkit.Transaction) bool {
-	return ApproveTransactionPrompt(transaction, "⚠️  Do you want to BUILD this transaction?")
-}
-
-func ApproveTransactionForSendingPrompt(transaction *flowkit.Transaction) bool {
-	return ApproveTransactionPrompt(transaction, "⚠️  Do you want to SEND this transaction?")
-}
-
-func ApproveTransactionPrompt(transaction *flowkit.Transaction, promptMsg string) bool {
+func ApproveTransactionPrompt(transaction *flowkit.Transaction) bool {
 	writer := uilive.New()
 	tx := transaction.FlowTransaction()
 
@@ -97,7 +85,7 @@ func ApproveTransactionPrompt(transaction *flowkit.Transaction, promptMsg string
 	_ = writer.Flush()
 
 	prompt := promptui.Select{
-		Label: promptMsg,
+		Label: "⚠️  Do you want to sign this transaction?",
 		Items: []string{"No", "Yes"},
 	}
 
diff --git a/pkg/flowkit/services/accounts.go b/pkg/flowkit/services/accounts.go
index e1cee952..86b0f8dc 100644
--- a/pkg/flowkit/services/accounts.go
+++ b/pkg/flowkit/services/accounts.go
@@ -67,8 +67,8 @@ func (a *Accounts) Get(address flow.Address) (*flow.Account, error) {
 	return account, err
 }
 
-// StakingInfo returns the staking information for an account.
-func (a *Accounts) StakingInfo(address flow.Address) (*cadence.Value, *cadence.Value, error) {
+// StakingInfo returns the staking and delegation information for an account.
+func (a *Accounts) StakingInfo(address flow.Address) ([]map[string]interface{}, []map[string]interface{}, error) {
 	a.logger.StartProgress(fmt.Sprintf("Fetching info for %s...", address.String()))
 	defer a.logger.StopProgress()
 
@@ -87,8 +87,8 @@ func (a *Accounts) StakingInfo(address flow.Address) (*cadence.Value, *cadence.V
 
 	env := util.EnvFromNetwork(chain)
 
-	stakingInfoScript := tmpl.GenerateGetLockedStakerInfoScript(env)
-	delegationInfoScript := tmpl.GenerateGetLockedDelegatorInfoScript(env)
+	stakingInfoScript := tmpl.GenerateCollectionGetAllNodeInfoScript(env)
+	delegationInfoScript := tmpl.GenerateCollectionGetAllDelegatorInfoScript(env)
 
 	stakingValue, err := a.gateway.ExecuteScript(stakingInfoScript, cadenceAddress)
 	if err != nil {
@@ -100,9 +100,73 @@ func (a *Accounts) StakingInfo(address flow.Address) (*cadence.Value, *cadence.V
 		return nil, nil, fmt.Errorf("error getting delegation info: %s", err.Error())
 	}
 
+	// get staking infos and delegation infos
+	stakingInfos, err := flowkit.NewStakingInfoFromValue(stakingValue)
+	if err != nil {
+		return nil, nil, fmt.Errorf("error parsing staking info: %s", err.Error())
+	}
+	delegationInfos, err := flowkit.NewStakingInfoFromValue(delegationValue)
+	if err != nil {
+		return nil, nil, fmt.Errorf("error parsing delegation info: %s", err.Error())
+	}
+
+	// get a set of node ids from all staking infos
+	nodeStakes := make(map[string]cadence.Value)
+	for _, stakingInfo := range stakingInfos {
+		nodeID, ok := stakingInfo["id"]
+		if ok {
+			nodeStakes[nodeIDToString(nodeID)] = nil
+		}
+	}
+	totalCommitmentScript := tmpl.GenerateGetTotalCommitmentBalanceScript(env)
+
+	// foreach node id, get the node total stake
+	for nodeID := range nodeStakes {
+		stake, err := a.gateway.ExecuteScript(totalCommitmentScript, []cadence.Value{cadence.String(nodeID)})
+		if err != nil {
+			return nil, nil, fmt.Errorf("error getting total stake for node: %s", err.Error())
+		}
+
+		nodeStakes[nodeID] = stake
+	}
+
+	// foreach staking info, add the node total stake
+	for _, stakingInfo := range stakingInfos {
+		nodeID, ok := stakingInfo["id"]
+		if ok {
+			stakingInfo["nodeTotalStake"] = nodeStakes[nodeIDToString(nodeID)].(cadence.UFix64)
+		}
+	}
+
+	a.logger.StopProgress()
+
+	return stakingInfos, delegationInfos, nil
+}
+
+func nodeIDToString(value interface{}) string {
+	return value.(cadence.String).ToGoValue().(string)
+}
+
+// NodeTotalStake returns the total stake including delegations of a node.
+func (a *Accounts) NodeTotalStake(nodeId string, chain flow.ChainID) (*cadence.Value, error) {
+	a.logger.StartProgress(fmt.Sprintf("Fetching total stake for node id %s...", nodeId))
+	defer a.logger.StopProgress()
+
+	if chain == flow.Emulator {
+		return nil, fmt.Errorf("emulator chain not supported")
+	}
+
+	env := util.EnvFromNetwork(chain)
+
+	stakingInfoScript := tmpl.GenerateGetTotalCommitmentBalanceScript(env)
+	stakingValue, err := a.gateway.ExecuteScript(stakingInfoScript, []cadence.Value{cadence.String(nodeId)})
+	if err != nil {
+		return nil, fmt.Errorf("error getting total stake for node: %s", err.Error())
+	}
+
 	a.logger.StopProgress()
 
-	return &stakingValue, &delegationValue, nil
+	return &stakingValue, nil
 }
 
 // Create creates and returns a new account.
diff --git a/pkg/flowkit/services/transactions.go b/pkg/flowkit/services/transactions.go
index b7d49052..a932d09a 100644
--- a/pkg/flowkit/services/transactions.go
+++ b/pkg/flowkit/services/transactions.go
@@ -87,7 +87,6 @@ func (t *Transactions) Build(
 	gasLimit uint64,
 	args []cadence.Value,
 	network string,
-	approveBuild bool,
 ) (*flowkit.Transaction, error) {
 
 	latestBlock, err := t.gateway.GetLatestBlock()
@@ -144,14 +143,6 @@ func (t *Transactions) Build(
 		return nil, err
 	}
 
-	if approveBuild {
-		return tx, nil
-	}
-
-	if !output.ApproveTransactionForBuildingPrompt(tx) {
-		return nil, fmt.Errorf("transaction was not approved")
-	}
-
 	return tx, nil
 }
 
@@ -179,7 +170,7 @@ func (t *Transactions) Sign(
 		return tx.Sign()
 	}
 
-	if !output.ApproveTransactionForSigningPrompt(tx) {
+	if !output.ApproveTransactionPrompt(tx) {
 		return nil, fmt.Errorf("transaction was not approved for signing")
 	}
 
@@ -189,15 +180,11 @@ func (t *Transactions) Sign(
 // SendSigned sends the transaction that is already signed.
 func (t *Transactions) SendSigned(
 	payload []byte,
-	approveSend bool,
 ) (*flow.Transaction, *flow.TransactionResult, error) {
 	tx, err := flowkit.NewTransactionFromPayload(payload)
 	if err != nil {
 		return nil, nil, err
 	}
-	if !approveSend && !output.ApproveTransactionForSendingPrompt(tx) {
-		return nil, nil, fmt.Errorf("transaction was not approved for sending")
-	}
 
 	t.logger.StartProgress(fmt.Sprintf("Sending transaction with ID: %s", tx.FlowTransaction().ID()))
 	defer t.logger.StopProgress()
@@ -240,7 +227,6 @@ func (t *Transactions) Send(
 		gasLimit,
 		args,
 		network,
-		true,
 	)
 	if err != nil {
 		return nil, nil, err
diff --git a/pkg/flowkit/values.go b/pkg/flowkit/values.go
index cb211d0e..e1b1d153 100644
--- a/pkg/flowkit/values.go
+++ b/pkg/flowkit/values.go
@@ -19,25 +19,38 @@
 package flowkit
 
 import (
+	"fmt"
+
 	"github.com/onflow/cadence"
 )
 
-func NewStakingInfoFromValue(value cadence.Value) map[string]interface{} {
-	stakingInfo := make(map[string]interface{})
-	arrayValue := value.(cadence.Array)
+func NewStakingInfoFromValue(value cadence.Value) ([]map[string]interface{}, error) {
+	stakingInfo := make([]map[string]interface{}, 0)
+	arrayValue, ok := value.(cadence.Array)
+	if !ok {
+		return stakingInfo, fmt.Errorf("staking info must be a cadence array")
+	}
 
 	if len(arrayValue.Values) == 0 {
-		return stakingInfo
+		return stakingInfo, nil
 	}
 
-	keys := make([]string, 0)
-	for _, field := range arrayValue.Values[0].(cadence.Struct).StructType.Fields {
-		keys = append(keys, field.Identifier)
-	}
+	for _, v := range arrayValue.Values {
+		vs, ok := v.(cadence.Struct)
+		if !ok {
+			return stakingInfo, fmt.Errorf("staking info must be a cadence array of structs")
+		}
 
-	for i, value := range arrayValue.Values[0].(cadence.Struct).Fields {
-		stakingInfo[keys[i]] = value
+		keys := make([]string, 0)
+		values := make(map[string]interface{})
+		for _, field := range vs.StructType.Fields {
+			keys = append(keys, field.Identifier)
+		}
+		for j, value := range vs.Fields {
+			values[keys[j]] = value
+		}
+		stakingInfo = append(stakingInfo, values)
 	}
 
-	return stakingInfo
+	return stakingInfo, nil
 }


Test patch:
diff --git a/pkg/flowkit/services/accounts_test.go b/pkg/flowkit/services/accounts_test.go
index a2306c83..098eed69 100644
--- a/pkg/flowkit/services/accounts_test.go
+++ b/pkg/flowkit/services/accounts_test.go
@@ -225,17 +225,54 @@ func TestAccounts(t *testing.T) {
 	t.Run("Staking Info for Account", func(t *testing.T) {
 		_, s, gw := setup()
 
+		count := 0
 		gw.ExecuteScript.Run(func(args mock.Arguments) {
+			count++
 			assert.True(t, strings.Contains(string(args.Get(0).([]byte)), "import FlowIDTableStaking from 0x9eca2b38b18b5dfe"))
-			gw.ExecuteScript.Return(cadence.NewValue(nil))
+			gw.ExecuteScript.Return(cadence.NewArray([]cadence.Value{}), nil)
 		})
 
 		val1, val2, err := s.Accounts.StakingInfo(flow.HexToAddress("df9c30eb2252f1fa"))
 		assert.NoError(t, err)
 		assert.NotNil(t, val1)
 		assert.NotNil(t, val2)
+		assert.Equal(t, 2, count)
 	})
+	t.Run("Staking Info for Account fetches node total", func(t *testing.T) {
+		_, s, gw := setup()
+
+		count := 0
+		gw.ExecuteScript.Run(func(args mock.Arguments) {
+			assert.True(t, strings.Contains(string(args.Get(0).([]byte)), "import FlowIDTableStaking from 0x9eca2b38b18b5dfe"))
+			if count < 2 {
+				gw.ExecuteScript.Return(cadence.NewArray(
+					[]cadence.Value{
+						cadence.Struct{
+							StructType: &cadence.StructType{
+								Fields: []cadence.Field{
+									{
+										Identifier: "id",
+									},
+								},
+							},
+							Fields: []cadence.Value{
+								cadence.String("8f4d09dae7918afbf62c48fa968a9e8b0891cee8442065fa47cc05f4bc9a8a91"),
+							},
+						},
+					}), nil)
+			} else {
+				assert.True(t, strings.Contains(args.Get(1).([]cadence.Value)[0].String(), "8f4d09dae7918afbf62c48fa968a9e8b0891cee8442065fa47cc05f4bc9a8a91"))
+				gw.ExecuteScript.Return(cadence.NewUFix64("1.0"))
+			}
+			count++
+		})
 
+		val1, val2, err := s.Accounts.StakingInfo(flow.HexToAddress("df9c30eb2252f1fa"))
+		assert.NoError(t, err)
+		assert.NotNil(t, val1)
+		assert.NotNil(t, val2)
+		assert.Equal(t, 3, count)
+	})
 }
 
 func setupIntegration() (*flowkit.State, *Services) {
diff --git a/pkg/flowkit/services/transactions_test.go b/pkg/flowkit/services/transactions_test.go
index 5792ca64..776a9f60 100644
--- a/pkg/flowkit/services/transactions_test.go
+++ b/pkg/flowkit/services/transactions_test.go
@@ -142,7 +142,6 @@ func TestTransactions_Integration(t *testing.T) {
 			gas     uint64
 			args    []cadence.Value
 			network string
-			yes     bool
 		}
 
 		a, _ := state.Accounts().ByName("Alice")
@@ -159,7 +158,6 @@ func TestTransactions_Integration(t *testing.T) {
 			1000,
 			nil,
 			"",
-			true,
 		}, {
 			c.Address(),
 			[]flow.Address{a.Address(), b.Address()},
@@ -170,11 +168,10 @@ func TestTransactions_Integration(t *testing.T) {
 			1000,
 			nil,
 			"",
-			true,
 		}}
 
 		for _, i := range txIns {
-			tx, err := s.Transactions.Build(i.prop, i.auth, i.payer, i.index, i.code, i.file, i.gas, i.args, i.network, i.yes)
+			tx, err := s.Transactions.Build(i.prop, i.auth, i.payer, i.index, i.code, i.file, i.gas, i.args, i.network)
 
 			assert.NoError(t, err)
 			ftx := tx.FlowTransaction()
@@ -230,7 +227,6 @@ func TestTransactions_Integration(t *testing.T) {
 			1000,
 			nil,
 			n.Name,
-			true,
 		)
 
 		assert.NoError(t, err)
@@ -262,7 +258,6 @@ func TestTransactions_Integration(t *testing.T) {
 			1000,
 			nil,
 			"",
-			true,
 		)
 
 		assert.Nil(t, err)
@@ -299,7 +294,6 @@ func TestTransactions_Integration(t *testing.T) {
 			1000,
 			nil,
 			"",
-			true,
 		)
 
 		assert.Nil(t, err)
@@ -315,7 +309,6 @@ func TestTransactions_Integration(t *testing.T) {
 
 		txSent, txResult, err := s.Transactions.SendSigned(
 			[]byte(fmt.Sprintf("%x", txSigned.FlowTransaction().Encode())),
-			true,
 		)
 		assert.Nil(t, err)
 		assert.Equal(t, txResult.Status, flow.TransactionStatusSealed)
@@ -340,7 +333,6 @@ func TestTransactions_Integration(t *testing.T) {
 			1000,
 			nil,
 			"",
-			true,
 		)
 
 		assert.Nil(t, err)
@@ -375,7 +367,6 @@ func TestTransactions_Integration(t *testing.T) {
 			1000,
 			nil,
 			"",
-			true,
 		)
 
 		assert.EqualError(t, err, "provided authorizers length mismatch, required authorizers 2, but provided 1")