Skip to content

Commit b3518f5

Browse files
committed
Add list
1 parent aa19e64 commit b3518f5

File tree

5 files changed

+242
-67
lines changed

5 files changed

+242
-67
lines changed

internal/schedule/cancel.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func cancelRun(
120120
signerStr := branding.GrayStyle.Render(cancelFlags.Signer)
121121
txIDStr := branding.PurpleStyle.Render(transactionIDStr)
122122

123-
logger.Info("Canceling scheduled transaction")
123+
logger.Info("Canceling scheduled transaction...")
124124
logger.Info("")
125125
logger.Info(fmt.Sprintf("🌐 Network: %s", networkStr))
126126
logger.Info(fmt.Sprintf("📝 Signer: %s (%s)", signerStr, addressStr))

internal/schedule/get.go

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func getRun(
9797
networkStr := branding.GrayStyle.Render(globalFlags.Network)
9898
txIDStr := branding.PurpleStyle.Render(transactionIDStr)
9999

100-
logger.Info("Getting scheduled transaction details")
100+
logger.Info("Getting scheduled transaction details...")
101101
logger.Info("")
102102
logger.Info(fmt.Sprintf("🌐 Network: %s", networkStr))
103103
logger.Info(fmt.Sprintf("🔍 Transaction ID: %s", txIDStr))
@@ -131,7 +131,6 @@ access(all) fun main(transactionID: UInt64): FlowTransactionScheduler.Transactio
131131
return nil, fmt.Errorf("scheduled transaction not found")
132132
}
133133

134-
// Log success
135134
logger.Info("")
136135
successIcon := branding.GreenStyle.Render("✅")
137136
successMsg := branding.GreenStyle.Render("Transaction data retrieved successfully")
@@ -158,49 +157,7 @@ func (r *getResult) JSON() any {
158157
}
159158

160159
func (r *getResult) String() string {
161-
var output string
162-
163-
// ID
164-
idLabel := branding.GrayStyle.Render(" ID:")
165-
idValue := branding.PurpleStyle.Render(fmt.Sprintf("%d", r.data.ID))
166-
output += fmt.Sprintf("%s %s\n", idLabel, idValue)
167-
168-
// Status
169-
statusLabel := branding.GrayStyle.Render(" Status:")
170-
statusValue := branding.GreenStyle.Render(GetStatusString(r.data.Status))
171-
output += fmt.Sprintf("%s %s\n", statusLabel, statusValue)
172-
173-
// Priority
174-
priorityLabel := branding.GrayStyle.Render(" Priority:")
175-
priorityValue := branding.PurpleStyle.Render(GetPriorityString(r.data.Priority))
176-
output += fmt.Sprintf("%s %s\n", priorityLabel, priorityValue)
177-
178-
// Execution Effort
179-
effortLabel := branding.GrayStyle.Render(" Execution Effort:")
180-
effortValue := branding.PurpleStyle.Render(fmt.Sprintf("%d", r.data.ExecutionEffort))
181-
output += fmt.Sprintf("%s %s\n", effortLabel, effortValue)
182-
183-
// Fees
184-
feesLabel := branding.GrayStyle.Render(" Fees:")
185-
feesValue := branding.PurpleStyle.Render(fmt.Sprintf("%s FLOW", r.data.Fees))
186-
output += fmt.Sprintf("%s %s\n", feesLabel, feesValue)
187-
188-
// Scheduled Timestamp
189-
timestampLabel := branding.GrayStyle.Render(" Scheduled Timestamp:")
190-
timestampValue := branding.PurpleStyle.Render(r.data.ScheduledTimestamp)
191-
output += fmt.Sprintf("%s %s\n", timestampLabel, timestampValue)
192-
193-
// Handler Type
194-
handlerTypeLabel := branding.GrayStyle.Render(" Handler Type:")
195-
handlerTypeValue := branding.PurpleStyle.Render(r.data.HandlerTypeIdentifier)
196-
output += fmt.Sprintf("%s %s\n", handlerTypeLabel, handlerTypeValue)
197-
198-
// Handler Address
199-
handlerAddrLabel := branding.GrayStyle.Render(" Handler Address:")
200-
handlerAddrValue := branding.PurpleStyle.Render(r.data.HandlerAddress)
201-
output += fmt.Sprintf("%s %s\n", handlerAddrLabel, handlerAddrValue)
202-
203-
return output
160+
return FormatTransactionDetails(r.data)
204161
}
205162

206163
func (r *getResult) Oneliner() string {

internal/schedule/list.go

Lines changed: 137 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@
1919
package schedule
2020

2121
import (
22+
"context"
2223
"fmt"
24+
"strings"
2325

26+
"github.com/onflow/cadence"
27+
flowsdk "github.com/onflow/flow-go-sdk"
2428
"github.com/spf13/cobra"
2529

2630
"github.com/onflow/flowkit/v2"
2731
"github.com/onflow/flowkit/v2/output"
2832

33+
"github.com/onflow/flow-cli/common/branding"
2934
"github.com/onflow/flow-cli/internal/command"
3035
"github.com/onflow/flow-cli/internal/util"
3136
)
@@ -71,54 +76,166 @@ func listRun(
7176

7277
accountInput := args[0]
7378

74-
// Resolve account address from input (could be address or account name)
7579
address, err := util.ResolveAddressOrAccountNameForNetworks(accountInput, state, []string{"mainnet", "testnet", "emulator"})
7680
if err != nil {
7781
return nil, fmt.Errorf("failed to resolve account: %w", err)
7882
}
7983

80-
logger.Info(fmt.Sprintf("Network: %s", globalFlags.Network))
81-
logger.Info(fmt.Sprintf("Account: %s (%s)", accountInput, address.String()))
82-
logger.Info("Retrieving scheduled transactions...")
84+
chainID, err := util.NetworkToChainID(globalFlags.Network)
85+
if err != nil {
86+
return nil, err
87+
}
88+
89+
if chainID == flowsdk.Mainnet {
90+
return nil, fmt.Errorf("transaction scheduling is not yet supported on mainnet")
91+
}
8392

84-
// TODO: Implement list logic for scheduled transactions
93+
schedulerAddress, err := getContractAddress(FlowTransactionScheduler, chainID)
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
schedulerUtilsAddress, err := getContractAddress(FlowTransactionSchedulerUtils, chainID)
99+
if err != nil {
100+
return nil, err
101+
}
102+
103+
networkStr := branding.GrayStyle.Render(globalFlags.Network)
104+
accountStr := branding.PurpleStyle.Render(accountInput)
105+
addressStr := branding.GrayStyle.Render(address.String())
106+
107+
logger.Info("Listing scheduled transactions...")
108+
logger.Info("")
109+
logger.Info(fmt.Sprintf("🌐 Network: %s", networkStr))
110+
logger.Info(fmt.Sprintf("📝 Account: %s (%s)", accountStr, addressStr))
111+
112+
script := fmt.Sprintf(`import FlowTransactionScheduler from %s
113+
import FlowTransactionSchedulerUtils from %s
114+
115+
access(all) fun main(managerAddress: Address): [FlowTransactionScheduler.TransactionData] {
116+
// Use the helper function to borrow the Manager
117+
let manager = FlowTransactionSchedulerUtils.borrowManager(at: managerAddress)
118+
?? panic("Could not borrow Manager from account")
119+
120+
let transactionIds = manager.getTransactionIDs()
121+
var transactions: [FlowTransactionScheduler.TransactionData] = []
122+
123+
// Get transaction data through the Manager instead of directly from FlowTransactionScheduler
124+
for id in transactionIds {
125+
if let txData = manager.getTransactionData(id) {
126+
transactions.append(txData)
127+
}
128+
}
129+
130+
return transactions
131+
}`, schedulerAddress, schedulerUtilsAddress)
132+
133+
value, err := flow.ExecuteScript(
134+
context.Background(),
135+
flowkit.Script{
136+
Code: []byte(script),
137+
Args: []cadence.Value{cadence.NewAddress(address)},
138+
},
139+
flowkit.LatestScriptQuery,
140+
)
141+
if err != nil {
142+
return nil, fmt.Errorf("failed to execute script: %w", err)
143+
}
144+
145+
transactions, err := parseTransactionList(value)
146+
if err != nil {
147+
return nil, fmt.Errorf("failed to parse transaction list: %w", err)
148+
}
149+
150+
logger.Info("")
151+
if len(transactions) == 0 {
152+
warningIcon := branding.GrayStyle.Render("ℹ")
153+
warningMsg := branding.GrayStyle.Render("No scheduled transactions found")
154+
logger.Info(fmt.Sprintf("%s %s", warningIcon, warningMsg))
155+
} else {
156+
successIcon := branding.GreenStyle.Render("✅")
157+
successMsg := branding.GreenStyle.Render(fmt.Sprintf("Found %d scheduled transaction(s)", len(transactions)))
158+
logger.Info(fmt.Sprintf("%s %s", successIcon, successMsg))
159+
}
85160

86161
return &listResult{
87-
success: true,
88-
message: "Scheduled transactions retrieved successfully",
89-
transactions: []string{"0x1234567890abcdef", "0xfedcba0987654321"}, // Mock data for now
162+
transactions: transactions,
90163
}, nil
91164
}
92165

166+
func parseTransactionList(value cadence.Value) ([]*TransactionData, error) {
167+
array, ok := value.(cadence.Array)
168+
if !ok {
169+
return nil, fmt.Errorf("expected array value, got %T", value)
170+
}
171+
172+
var transactions []*TransactionData
173+
for _, item := range array.Values {
174+
optional := cadence.NewOptional(item)
175+
txData, err := ParseTransactionData(optional)
176+
if err != nil {
177+
return nil, fmt.Errorf("failed to parse transaction: %w", err)
178+
}
179+
if txData != nil {
180+
transactions = append(transactions, txData)
181+
}
182+
}
183+
184+
return transactions, nil
185+
}
186+
93187
type listResult struct {
94-
success bool
95-
message string
96-
transactions []string
188+
transactions []*TransactionData
97189
}
98190

99191
func (r *listResult) JSON() any {
192+
var txList []map[string]any
193+
for _, tx := range r.transactions {
194+
txList = append(txList, map[string]any{
195+
"id": tx.ID,
196+
"priority": tx.Priority,
197+
"execution_effort": tx.ExecutionEffort,
198+
"status": tx.Status,
199+
"fees": tx.Fees,
200+
"scheduled_timestamp": tx.ScheduledTimestamp,
201+
"handler_type_identifier": tx.HandlerTypeIdentifier,
202+
"handler_address": tx.HandlerAddress,
203+
})
204+
}
100205
return map[string]any{
101-
"success": r.success,
102-
"message": r.message,
103-
"transactions": r.transactions,
206+
"transactions": txList,
207+
"count": len(r.transactions),
104208
}
105209
}
106210

107211
func (r *listResult) String() string {
108212
if len(r.transactions) == 0 {
109-
return "No scheduled transactions found"
213+
return ""
110214
}
111215

112-
result := fmt.Sprintf("Found %d scheduled transaction(s):\n", len(r.transactions))
113-
for i, txID := range r.transactions {
114-
result += fmt.Sprintf(" %d. %s\n", i+1, txID)
216+
var output strings.Builder
217+
218+
// Display each transaction with details
219+
for i, tx := range r.transactions {
220+
if i > 0 {
221+
output.WriteString("\n")
222+
}
223+
224+
// Transaction header line
225+
txLabel := branding.GrayStyle.Render("Transaction")
226+
txID := branding.PurpleStyle.Render(fmt.Sprintf("%d", tx.ID))
227+
output.WriteString(fmt.Sprintf("%s %s\n", txLabel, txID))
228+
229+
// Transaction details using shared formatting
230+
output.WriteString(FormatTransactionDetails(tx))
115231
}
116-
return result
232+
233+
return output.String()
117234
}
118235

119236
func (r *listResult) Oneliner() string {
120237
if len(r.transactions) == 0 {
121238
return "No scheduled transactions found"
122239
}
123-
return fmt.Sprintf("Found %d scheduled transactions", len(r.transactions))
240+
return fmt.Sprintf("Found %d scheduled transaction(s)", len(r.transactions))
124241
}

internal/schedule/setup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func setupRun(
102102
logger.Info(fmt.Sprintf("🌐 Network: %s", networkStr))
103103
logger.Info(fmt.Sprintf("📝 Signer: %s (%s)", signerStr, addressStr))
104104
logger.Info("")
105-
logger.Info(" Setting up Transaction Scheduler Manager...")
105+
logger.Info("🔧 Setting up Transaction Scheduler Manager...")
106106

107107
// Transaction checks if manager exists and only creates it if needed
108108
setupTx := fmt.Sprintf(`import FlowTransactionSchedulerUtils from %s

internal/schedule/utils.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Flow CLI
3+
*
4+
* Copyright Flow Foundation
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package schedule
20+
21+
import (
22+
"fmt"
23+
24+
"github.com/onflow/flow-cli/common/branding"
25+
)
26+
27+
// GetColoredStatus returns a colored status string based on the status code
28+
func GetColoredStatus(status uint8) string {
29+
statusStr := GetStatusString(status)
30+
switch status {
31+
case 0, 1: // Pending, Scheduled
32+
return branding.GrayStyle.Render(statusStr)
33+
case 2: // Executing
34+
return branding.PurpleStyle.Render(statusStr)
35+
case 3: // Executed
36+
return branding.GreenStyle.Render(statusStr)
37+
case 4: // Failed
38+
return branding.ErrorStyle.Render(statusStr)
39+
case 5: // Cancelled
40+
return branding.GrayStyle.Render(statusStr)
41+
default:
42+
return statusStr
43+
}
44+
}
45+
46+
// GetColoredPriority returns a colored priority string based on the priority code
47+
func GetColoredPriority(priority uint8) string {
48+
priorityStr := GetPriorityString(priority)
49+
switch priority {
50+
case 0: // Low
51+
return branding.GrayStyle.Render(priorityStr)
52+
case 1: // Medium
53+
return branding.PurpleStyle.Render(priorityStr)
54+
case 2: // High
55+
return branding.GreenStyle.Render(priorityStr)
56+
default:
57+
return priorityStr
58+
}
59+
}
60+
61+
// FormatTransactionDetails returns a formatted string with transaction details
62+
func FormatTransactionDetails(tx *TransactionData) string {
63+
var output string
64+
65+
// Status
66+
statusLabel := branding.GrayStyle.Render(" Status:")
67+
statusValue := GetColoredStatus(tx.Status)
68+
output += fmt.Sprintf("%s %s\n", statusLabel, statusValue)
69+
70+
// Priority
71+
priorityLabel := branding.GrayStyle.Render(" Priority:")
72+
priorityValue := GetColoredPriority(tx.Priority)
73+
output += fmt.Sprintf("%s %s\n", priorityLabel, priorityValue)
74+
75+
// Execution Effort
76+
effortLabel := branding.GrayStyle.Render(" Execution Effort:")
77+
effortValue := branding.PurpleStyle.Render(fmt.Sprintf("%d", tx.ExecutionEffort))
78+
output += fmt.Sprintf("%s %s\n", effortLabel, effortValue)
79+
80+
// Fees
81+
feesLabel := branding.GrayStyle.Render(" Fees:")
82+
feesValue := branding.PurpleStyle.Render(fmt.Sprintf("%s FLOW", tx.Fees))
83+
output += fmt.Sprintf("%s %s\n", feesLabel, feesValue)
84+
85+
// Scheduled Timestamp
86+
timestampLabel := branding.GrayStyle.Render(" Scheduled Timestamp:")
87+
timestampValue := branding.PurpleStyle.Render(tx.ScheduledTimestamp)
88+
output += fmt.Sprintf("%s %s\n", timestampLabel, timestampValue)
89+
90+
// Handler Type
91+
handlerTypeLabel := branding.GrayStyle.Render(" Handler Type:")
92+
handlerTypeValue := branding.PurpleStyle.Render(tx.HandlerTypeIdentifier)
93+
output += fmt.Sprintf("%s %s\n", handlerTypeLabel, handlerTypeValue)
94+
95+
// Handler Address
96+
handlerAddrLabel := branding.GrayStyle.Render(" Handler Address:")
97+
handlerAddrValue := branding.PurpleStyle.Render(tx.HandlerAddress)
98+
output += fmt.Sprintf("%s %s\n", handlerAddrLabel, handlerAddrValue)
99+
100+
return output
101+
}

0 commit comments

Comments
 (0)