txlink.gno
4.55 Kb ยท 170 lines
1// Package txlink provides utilities for creating transaction-related links
2// compatible with Gnoweb, Gnobro, and other clients within the Gno ecosystem.
3//
4// This package is optimized for generating lightweight transaction links with
5// flexible arguments, allowing users to build dynamic links that integrate
6// seamlessly with various Gno clients.
7//
8// The package offers a way to generate clickable transaction MD links
9// for the current "relative realm":
10//
11// Using a builder pattern for more structured URLs:
12// txlink.NewLink("MyFunc").
13// AddArgs("k1", "v1", "k2", "v2"). // or multiple at once
14// SetSend("1000000ugnot").
15// URL()
16//
17// The builder pattern (TxBuilder) provides a fluent interface for constructing
18// transaction URLs in the current "relative realm". Like Call, it supports both
19// local realm paths and fully qualified paths through the underlying Call
20// implementation.
21//
22// The Call function remains the core implementation, used both directly and
23// internally by the builder pattern to generate the final URLs.
24//
25// This package is a streamlined alternative to helplink, providing similar
26// functionality for transaction links without the full feature set of helplink.
27
28package txlink
29
30import (
31 "net/url"
32 "std"
33 "strings"
34)
35
36var chainDomain = std.ChainDomain()
37
38// Realm represents a specific realm for generating tx links.
39type Realm string
40
41// TxBuilder provides a fluent interface for building transaction URLs
42type TxBuilder struct {
43 fn string // function name
44 args []string // key-value pairs
45 send string // optional send amount
46 realm_XXX Realm // realm for the URL
47}
48
49// NewLink creates a transaction link builder for the specified function in the current realm.
50func NewLink(fn string) *TxBuilder {
51 return Realm("").NewLink(fn)
52}
53
54// NewLink creates a transaction link builder for the specified function in this realm.
55func (r Realm) NewLink(fn string) *TxBuilder {
56 if fn == "" {
57 return nil
58 }
59 return &TxBuilder{fn: fn, realm_XXX: r}
60}
61
62// addArg adds a key-value argument pair. Returns the builder for chaining.
63func (b *TxBuilder) addArg(key, value string) *TxBuilder {
64 if b == nil {
65 return nil
66 }
67 if key == "" {
68 return b
69 }
70
71 // Special case: "." prefix is for reserved keywords.
72 if strings.HasPrefix(key, ".") {
73 panic("invalid key")
74 }
75
76 b.args = append(b.args, key, value)
77 return b
78}
79
80// AddArgs adds multiple key-value pairs at once. Arguments should be provided
81// as pairs: AddArgs("key1", "value1", "key2", "value2").
82func (b *TxBuilder) AddArgs(args ...string) *TxBuilder {
83 if b == nil {
84 return nil
85 }
86 if len(args)%2 != 0 {
87 panic("odd number of arguments")
88 }
89 // Add key-value pairs
90 for i := 0; i < len(args); i += 2 {
91 key := args[i]
92 value := args[i+1]
93 b.addArg(key, value)
94 }
95 return b
96}
97
98// SetSend adds a send amount. (Only one send amount can be specified.)
99func (b *TxBuilder) SetSend(amount string) *TxBuilder {
100 if b == nil {
101 return nil
102 }
103 if amount == "" {
104 return b
105 }
106 b.send = amount
107 return b
108}
109
110// URL generates the final URL using the standard $help&func=name format.
111func (b *TxBuilder) URL() string {
112 if b == nil || b.fn == "" {
113 return ""
114 }
115 args := b.args
116 if b.send != "" {
117 args = append(args, ".send", b.send)
118 }
119 return b.realm_XXX.Call(b.fn, args...)
120}
121
122// Call returns a URL for the specified function with optional key-value
123// arguments, for the current realm.
124func Call(fn string, args ...string) string {
125 return Realm("").Call(fn, args...)
126}
127
128// prefix returns the URL prefix for the realm.
129func (r Realm) prefix() string {
130 // relative
131 if r == "" {
132 curPath := std.CurrentRealm().PkgPath()
133 return strings.TrimPrefix(curPath, chainDomain)
134 }
135
136 // local realm -> /realm
137 rlm := string(r)
138 if strings.HasPrefix(rlm, chainDomain) {
139 return strings.TrimPrefix(rlm, chainDomain)
140 }
141
142 // remote realm -> https://remote.land/realm
143 return "https://" + string(r)
144}
145
146// Call returns a URL for the specified function with optional key-value
147// arguments.
148func (r Realm) Call(fn string, args ...string) string {
149 if len(args) == 0 {
150 return r.prefix() + "$help&func=" + fn
151 }
152
153 // Create url.Values to properly encode parameters.
154 // But manage &func=fn as a special case to keep it as the first argument.
155 values := url.Values{}
156
157 // Check if args length is even
158 if len(args)%2 != 0 {
159 panic("odd number of arguments")
160 }
161 // Add key-value pairs to values
162 for i := 0; i < len(args); i += 2 {
163 key := args[i]
164 value := args[i+1]
165 values.Add(key, value)
166 }
167
168 // Build the base URL and append encoded query parameters
169 return r.prefix() + "$help&func=" + fn + "&" + values.Encode()
170}