Skip to content

Commit c8280cb

Browse files
authored
Merge pull request #2132 from onflow/cf/st-get
Add script and logic for Scheduled Tx `get` command
2 parents 02a84b3 + 8c69c27 commit c8280cb

File tree

2 files changed

+268
-30
lines changed

2 files changed

+268
-30
lines changed

internal/schedule/get.go

Lines changed: 124 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,20 @@
1919
package schedule
2020

2121
import (
22+
"context"
2223
"fmt"
24+
"strconv"
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"
35+
"github.com/onflow/flow-cli/internal/util"
3036
)
3137

3238
type flagsGet struct{}
@@ -65,51 +71,139 @@ func getRun(
6571
return nil, fmt.Errorf("transaction ID is required as an argument")
6672
}
6773

68-
transactionID := args[0]
74+
transactionIDStr := args[0]
6975

70-
logger.Info(fmt.Sprintf("Network: %s", globalFlags.Network))
71-
logger.Info(fmt.Sprintf("Transaction ID: %s", transactionID))
72-
logger.Info("Retrieving scheduled transaction details...")
76+
// Parse transaction ID as UInt64
77+
transactionID, err := strconv.ParseUint(transactionIDStr, 10, 64)
78+
if err != nil {
79+
return nil, fmt.Errorf("invalid transaction ID: %w", err)
80+
}
81+
82+
chainID, err := util.NetworkToChainID(globalFlags.Network)
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
// Check if network is supported
88+
if chainID == flowsdk.Mainnet {
89+
return nil, fmt.Errorf("transaction scheduling is not yet supported on mainnet")
90+
}
91+
92+
contractAddress, err := getContractAddress(FlowTransactionScheduler, chainID)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
networkStr := branding.GrayStyle.Render(globalFlags.Network)
98+
txIDStr := branding.PurpleStyle.Render(transactionIDStr)
99+
100+
logger.Info("Getting scheduled transaction details")
101+
logger.Info("")
102+
logger.Info(fmt.Sprintf("🌐 Network: %s", networkStr))
103+
logger.Info(fmt.Sprintf("🔍 Transaction ID: %s", txIDStr))
104+
105+
script := fmt.Sprintf(`import FlowTransactionScheduler from %s
106+
107+
access(all) fun main(transactionID: UInt64): FlowTransactionScheduler.TransactionData? {
108+
// Get the transaction data directly from the FlowTransactionScheduler contract
109+
return FlowTransactionScheduler.getTransactionData(id: transactionID)
110+
}`, contractAddress)
111+
112+
value, err := flow.ExecuteScript(
113+
context.Background(),
114+
flowkit.Script{
115+
Code: []byte(script),
116+
Args: []cadence.Value{cadence.NewUInt64(transactionID)},
117+
},
118+
flowkit.LatestScriptQuery,
119+
)
120+
if err != nil {
121+
return nil, fmt.Errorf("failed to execute script: %w", err)
122+
}
123+
124+
txData, err := ParseTransactionData(value)
125+
if err != nil {
126+
return nil, fmt.Errorf("failed to parse transaction data: %w", err)
127+
}
128+
129+
if txData == nil {
130+
logger.Info("")
131+
return nil, fmt.Errorf("scheduled transaction not found")
132+
}
73133

74-
// TODO: Implement get logic for scheduled transaction
134+
// Log success
135+
logger.Info("")
136+
successIcon := branding.GreenStyle.Render("✅")
137+
successMsg := branding.GreenStyle.Render("Transaction data retrieved successfully")
138+
logger.Info(fmt.Sprintf("%s %s", successIcon, successMsg))
75139

76-
return &getResult{
77-
success: true,
78-
message: "Scheduled transaction details retrieved successfully",
79-
transactionID: transactionID,
80-
status: "Scheduled",
81-
scheduledAt: "2024-01-01T00:00:00Z",
82-
executeAt: "2024-01-01T12:00:00Z",
83-
}, nil
140+
return &getResult{data: txData}, nil
84141
}
85142

86143
type getResult struct {
87-
success bool
88-
message string
89-
transactionID string
90-
status string
91-
scheduledAt string
92-
executeAt string
144+
data *TransactionData
93145
}
94146

95147
func (r *getResult) JSON() any {
96148
return map[string]any{
97-
"success": r.success,
98-
"message": r.message,
99-
"transaction_id": r.transactionID,
100-
"status": r.status,
101-
"scheduled_at": r.scheduledAt,
102-
"execute_at": r.executeAt,
149+
"id": r.data.ID,
150+
"priority": r.data.Priority,
151+
"execution_effort": r.data.ExecutionEffort,
152+
"status": r.data.Status,
153+
"fees": r.data.Fees,
154+
"scheduled_timestamp": r.data.ScheduledTimestamp,
155+
"handler_type_identifier": r.data.HandlerTypeIdentifier,
156+
"handler_address": r.data.HandlerAddress,
103157
}
104158
}
105159

106160
func (r *getResult) String() string {
107-
return fmt.Sprintf(`Transaction ID: %s
108-
Status: %s
109-
Scheduled At: %s
110-
Execute At: %s`, r.transactionID, r.status, r.scheduledAt, r.executeAt)
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
111204
}
112205

113206
func (r *getResult) Oneliner() string {
114-
return fmt.Sprintf("Transaction %s - Status: %s", r.transactionID, r.status)
207+
statusStr := GetStatusString(r.data.Status)
208+
return fmt.Sprintf("Transaction %d - Status: %s", r.data.ID, statusStr)
115209
}

internal/schedule/parse.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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/cadence"
25+
)
26+
27+
// TransactionData holds the parsed transaction data from the scheduler
28+
type TransactionData struct {
29+
ID uint64
30+
Priority uint8
31+
ExecutionEffort uint64
32+
Status uint8
33+
Fees string
34+
ScheduledTimestamp string
35+
HandlerTypeIdentifier string
36+
HandlerAddress string
37+
}
38+
39+
// ParseTransactionData parses the cadence.Value returned from a script into TransactionData
40+
func ParseTransactionData(value cadence.Value) (*TransactionData, error) {
41+
// Check if result is nil (optional return)
42+
optional, ok := value.(cadence.Optional)
43+
if !ok {
44+
return nil, fmt.Errorf("expected optional value, got %T", value)
45+
}
46+
47+
if optional.Value == nil {
48+
return nil, nil // Transaction not found
49+
}
50+
51+
// Cast to struct
52+
structValue, ok := optional.Value.(cadence.Struct)
53+
if !ok {
54+
return nil, fmt.Errorf("expected struct value, got %T", optional.Value)
55+
}
56+
57+
// Get fields mapped by name
58+
fields := cadence.FieldsMappedByName(structValue)
59+
60+
// Parse individual fields
61+
result := &TransactionData{}
62+
63+
// ID (UInt64)
64+
if id, ok := fields["id"].(cadence.UInt64); ok {
65+
result.ID = uint64(id)
66+
}
67+
68+
// Priority (Enum with rawValue)
69+
if priority, ok := fields["priority"].(cadence.Enum); ok {
70+
priorityFields := cadence.FieldsMappedByName(priority)
71+
if rawValue, ok := priorityFields["rawValue"].(cadence.UInt8); ok {
72+
result.Priority = uint8(rawValue)
73+
}
74+
}
75+
76+
// Execution Effort (UInt64)
77+
if effort, ok := fields["executionEffort"].(cadence.UInt64); ok {
78+
result.ExecutionEffort = uint64(effort)
79+
}
80+
81+
// Status (Enum with rawValue)
82+
if status, ok := fields["status"].(cadence.Enum); ok {
83+
statusFields := cadence.FieldsMappedByName(status)
84+
if rawValue, ok := statusFields["rawValue"].(cadence.UInt8); ok {
85+
result.Status = uint8(rawValue)
86+
}
87+
}
88+
89+
// Fees (UFix64)
90+
if fees, ok := fields["fees"].(cadence.UFix64); ok {
91+
result.Fees = fees.String()
92+
}
93+
94+
// Scheduled Timestamp (UFix64)
95+
if timestamp, ok := fields["scheduledTimestamp"].(cadence.UFix64); ok {
96+
result.ScheduledTimestamp = timestamp.String()
97+
}
98+
99+
// Handler Type Identifier (String)
100+
if handlerType, ok := fields["handlerTypeIdentifier"].(cadence.String); ok {
101+
result.HandlerTypeIdentifier = string(handlerType)
102+
}
103+
104+
// Handler Address (Address)
105+
if handlerAddr, ok := fields["handlerAddress"].(cadence.Address); ok {
106+
result.HandlerAddress = handlerAddr.String()
107+
}
108+
109+
return result, nil
110+
}
111+
112+
// GetStatusString converts status code to readable string
113+
func GetStatusString(status uint8) string {
114+
switch status {
115+
case 0:
116+
return "Pending"
117+
case 1:
118+
return "Scheduled"
119+
case 2:
120+
return "Executing"
121+
case 3:
122+
return "Executed"
123+
case 4:
124+
return "Failed"
125+
case 5:
126+
return "Cancelled"
127+
default:
128+
return fmt.Sprintf("Unknown(%d)", status)
129+
}
130+
}
131+
132+
// GetPriorityString converts priority code to readable string
133+
func GetPriorityString(priority uint8) string {
134+
switch priority {
135+
case 0:
136+
return "Low"
137+
case 1:
138+
return "Medium"
139+
case 2:
140+
return "High"
141+
default:
142+
return fmt.Sprintf("Unknown(%d)", priority)
143+
}
144+
}

0 commit comments

Comments
 (0)