Search Apps Documentation Source Content File Folder Download Copy Actions Download

txlink.gno

4.59 Kb · 171 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	"chain/runtime"
 32	"chain/runtime/unsafe"
 33	"net/url"
 34	"strings"
 35)
 36
 37var chainDomain = runtime.ChainDomain()
 38
 39// Realm represents a specific realm for generating tx links.
 40type Realm string
 41
 42// TxBuilder provides a fluent interface for building transaction URLs
 43type TxBuilder struct {
 44	fn        string   // function name
 45	args      []string // key-value pairs
 46	send      string   // optional send amount
 47	realm_XXX Realm    // realm for the URL
 48}
 49
 50// NewLink creates a transaction link builder for the specified function in the current realm.
 51func NewLink(fn string) *TxBuilder {
 52	return Realm("").NewLink(fn)
 53}
 54
 55// NewLink creates a transaction link builder for the specified function in this realm.
 56func (r Realm) NewLink(fn string) *TxBuilder {
 57	if fn == "" {
 58		return nil
 59	}
 60	return &TxBuilder{fn: fn, realm_XXX: r}
 61}
 62
 63// addArg adds a key-value argument pair. Returns the builder for chaining.
 64func (b *TxBuilder) addArg(key, value string) *TxBuilder {
 65	if b == nil {
 66		return nil
 67	}
 68	if key == "" {
 69		return b
 70	}
 71
 72	// Special case: "." prefix is for reserved keywords.
 73	if strings.HasPrefix(key, ".") {
 74		panic("invalid key")
 75	}
 76
 77	b.args = append(b.args, key, value)
 78	return b
 79}
 80
 81// AddArgs adds multiple key-value pairs at once. Arguments should be provided
 82// as pairs: AddArgs("key1", "value1", "key2", "value2").
 83func (b *TxBuilder) AddArgs(args ...string) *TxBuilder {
 84	if b == nil {
 85		return nil
 86	}
 87	if len(args)%2 != 0 {
 88		panic("odd number of arguments")
 89	}
 90	// Add key-value pairs
 91	for i := 0; i < len(args); i += 2 {
 92		key := args[i]
 93		value := args[i+1]
 94		b.addArg(key, value)
 95	}
 96	return b
 97}
 98
 99// SetSend adds a send amount. (Only one send amount can be specified.)
100func (b *TxBuilder) SetSend(amount string) *TxBuilder {
101	if b == nil {
102		return nil
103	}
104	if amount == "" {
105		return b
106	}
107	b.send = amount
108	return b
109}
110
111// URL generates the final URL using the standard $help&func=name format.
112func (b *TxBuilder) URL() string {
113	if b == nil || b.fn == "" {
114		return ""
115	}
116	args := b.args
117	if b.send != "" {
118		args = append(args, ".send", b.send)
119	}
120	return b.realm_XXX.Call(b.fn, args...)
121}
122
123// Call returns a URL for the specified function with optional key-value
124// arguments, for the current realm.
125func Call(fn string, args ...string) string {
126	return Realm("").Call(fn, args...)
127}
128
129// prefix returns the URL prefix for the realm.
130func (r Realm) prefix() string {
131	// relative
132	if r == "" {
133		curPath := unsafe.CurrentRealm().PkgPath()
134		return strings.TrimPrefix(curPath, chainDomain)
135	}
136
137	// local realm -> /realm
138	rlm := string(r)
139	if strings.HasPrefix(rlm, chainDomain) {
140		return strings.TrimPrefix(rlm, chainDomain)
141	}
142
143	// remote realm -> https://remote.land/realm
144	return "https://" + string(r)
145}
146
147// Call returns a URL for the specified function with optional key-value
148// arguments.
149func (r Realm) Call(fn string, args ...string) string {
150	if len(args) == 0 {
151		return r.prefix() + "$help&func=" + fn
152	}
153
154	// Create url.Values to properly encode parameters.
155	// But manage &func=fn as a special case to keep it as the first argument.
156	values := url.Values{}
157
158	// Check if args length is even
159	if len(args)%2 != 0 {
160		panic("odd number of arguments")
161	}
162	// Add key-value pairs to values
163	for i := 0; i < len(args); i += 2 {
164		key := args[i]
165		value := args[i+1]
166		values.Add(key, value)
167	}
168
169	// Build the base URL and append encoded query parameters
170	return r.prefix() + "$help&func=" + fn + "&" + values.Encode()
171}