github.com/algorand/go-algorand-sdk@v1.24.0/abi/interactions.go (about) 1 package abi 2 3 import ( 4 "crypto/sha512" 5 "fmt" 6 "strings" 7 8 avm_abi "github.com/algorand/avm-abi/abi" 9 ) 10 11 // Arg represents an ABI Method argument 12 type Arg struct { 13 // Optional, user-friendly name for the argument 14 Name string `json:"name,omitempty"` 15 // The type of the argument as a string. See the method GetTypeObject to 16 // obtain the ABI type object 17 Type string `json:"type"` 18 // A hidden type object cache that holds the parsed type object 19 typeObject *Type `json:"-"` 20 // Optional, user-friendly description for the argument 21 Desc string `json:"desc,omitempty"` 22 } 23 24 // IsTransactionArg checks if this argument's type is a transaction type 25 func (a Arg) IsTransactionArg() bool { 26 return IsTransactionType(a.Type) 27 } 28 29 // IsReferenceArg checks if this argument's type is a reference type 30 func (a Arg) IsReferenceArg() bool { 31 return IsReferenceType(a.Type) 32 } 33 34 // GetTypeObject parses and returns the ABI type object for this argument's 35 // type. An error will be returned if this argument's type is a transaction or 36 // reference type 37 func (a *Arg) GetTypeObject() (Type, error) { 38 if a.IsTransactionArg() { 39 return Type{}, fmt.Errorf("Invalid operation on transaction type %s", a.Type) 40 } 41 if a.IsReferenceArg() { 42 return Type{}, fmt.Errorf("Invalid operation on reference type %s", a.Type) 43 } 44 if a.typeObject != nil { 45 return *a.typeObject, nil 46 } 47 typeObject, err := TypeOf(a.Type) 48 if err == nil { 49 a.typeObject = &typeObject 50 } 51 return typeObject, err 52 } 53 54 // Return represents an ABI method return value 55 type Return struct { 56 // The type of the return value as a string. See the method GetTypeObject to 57 // obtain the ABI type object 58 Type string `json:"type"` 59 // A hidden type object cache that holds the parsed type object 60 typeObject *Type `json:"-"` 61 // Optional, user-friendly description for the return value 62 Desc string `json:"desc,omitempty"` 63 } 64 65 // IsVoid checks if this return type is void, meaning the method does not have 66 // any return value 67 func (r Return) IsVoid() bool { 68 return r.Type == VoidReturnType 69 } 70 71 // GetTypeObject parses and returns the ABI type object for this return type. 72 // An error will be returned if this is a void return type. 73 func (r *Return) GetTypeObject() (Type, error) { 74 if r.IsVoid() { 75 return Type{}, fmt.Errorf("Invalid operation on void return type") 76 } 77 if r.typeObject != nil { 78 return *r.typeObject, nil 79 } 80 typeObject, err := TypeOf(r.Type) 81 if err == nil { 82 r.typeObject = &typeObject 83 } 84 return typeObject, err 85 } 86 87 // Method represents an individual ABI method definition 88 type Method struct { 89 // The name of the method 90 Name string `json:"name"` 91 // Optional, user-friendly description for the method 92 Desc string `json:"desc,omitempty"` 93 // The arguments of the method, in order 94 Args []Arg `json:"args"` 95 // Information about the method's return value 96 Returns Return `json:"returns"` 97 } 98 99 // MethodFromSignature decoded a method signature string into a Method object. 100 func MethodFromSignature(methodStr string) (Method, error) { 101 name, argTypesStr, retTypeStr, err := avm_abi.ParseMethodSignature(methodStr) 102 if err != nil { 103 return Method{}, err 104 } 105 106 returnType := Return{Type: retTypeStr} 107 if !returnType.IsVoid() { 108 // fill type object cache and catch any errors 109 _, err := returnType.GetTypeObject() 110 if err != nil { 111 return Method{}, fmt.Errorf("Could not parse method return type: %w", err) 112 } 113 } 114 115 args := make([]Arg, len(argTypesStr)) 116 for i, argTypeStr := range argTypesStr { 117 args[i].Type = argTypeStr 118 119 if IsTransactionType(argTypeStr) || IsReferenceType(argTypeStr) { 120 continue 121 } 122 123 // fill type object cache and catch any errors 124 _, err := args[i].GetTypeObject() 125 if err != nil { 126 return Method{}, fmt.Errorf("Could not parse argument type at index %d: %w", i, err) 127 } 128 } 129 130 return Method{ 131 Name: name, 132 Args: args, 133 Returns: returnType, 134 }, nil 135 } 136 137 // GetSignature calculates and returns the signature of the method 138 func (method *Method) GetSignature() string { 139 var methodSignature string 140 methodSignature += method.Name + "(" 141 142 var strTypes []string 143 for _, arg := range method.Args { 144 strTypes = append(strTypes, arg.Type) 145 } 146 147 methodSignature += strings.Join(strTypes, ",") 148 methodSignature += ")" 149 methodSignature += method.Returns.Type 150 151 return methodSignature 152 } 153 154 // GetSelector calculates and returns the 4-byte selector of the method 155 func (method *Method) GetSelector() []byte { 156 sig := method.GetSignature() 157 sigHash := sha512.Sum512_256([]byte(sig)) 158 return sigHash[:4] 159 } 160 161 // GetTxCount returns the number of transactions required to invoke this method 162 func (method *Method) GetTxCount() int { 163 cnt := 1 164 for _, arg := range method.Args { 165 if arg.IsTransactionArg() { 166 cnt++ 167 } 168 } 169 return cnt 170 } 171 172 func GetMethodByName(methods []Method, name string) (Method, error) { 173 var filteredMethods []Method 174 for _, method := range methods { 175 if method.Name == name { 176 filteredMethods = append(filteredMethods, method) 177 } 178 } 179 180 if len(filteredMethods) > 1 { 181 var sigs []string 182 for _, method := range filteredMethods { 183 sigs = append(sigs, method.GetSignature()) 184 } 185 186 return Method{}, fmt.Errorf("found %d methods with the same name %s", len(filteredMethods), strings.Join(sigs, ",")) 187 } 188 189 if len(filteredMethods) == 0 { 190 return Method{}, fmt.Errorf("found 0 methods with the name %s", name) 191 } 192 193 return filteredMethods[0], nil 194 } 195 196 // Interface represents an ABI interface, which is a logically grouped 197 // collection of methods 198 type Interface struct { 199 // A user-friendly name for the interface 200 Name string `json:"name"` 201 // Optional, user-friendly description for the interface 202 Desc string `json:"desc,omitempty"` 203 // The methods that the interface contains 204 Methods []Method `json:"methods"` 205 } 206 207 func (i *Interface) GetMethodByName(name string) (Method, error) { 208 return GetMethodByName(i.Methods, name) 209 } 210 211 // ContractNetworkInfo contains network-specific information about the contract 212 type ContractNetworkInfo struct { 213 // The application ID of the contract for this network 214 AppID uint64 `json:"appID"` 215 } 216 217 // Contract represents an ABI contract, which is a concrete set of methods 218 // implemented by a single app 219 type Contract struct { 220 // A user-friendly name for the contract 221 Name string `json:"name"` 222 // Optional, user-friendly description for the contract 223 Desc string `json:"desc,omitempty"` 224 // Optional information about the contract's instances across different 225 // networks 226 Networks map[string]ContractNetworkInfo `json:"networks,omitempty"` 227 // The methods that the contract implements 228 Methods []Method `json:"methods"` 229 } 230 231 func (c *Contract) GetMethodByName(name string) (Method, error) { 232 return GetMethodByName(c.Methods, name) 233 }