github.com/hashgraph/hedera-sdk-go/v2@v2.48.0/contract_id.go (about) 1 package hedera 2 3 /*- 4 * 5 * Hedera Go SDK 6 * 7 * Copyright (C) 2020 - 2024 Hedera Hashgraph, LLC 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 * 21 */ 22 23 import ( 24 "encoding/hex" 25 "encoding/json" 26 "fmt" 27 "net/http" 28 "strconv" 29 "strings" 30 31 "github.com/hashgraph/hedera-protobufs-go/services" 32 "github.com/pkg/errors" 33 protobuf "google.golang.org/protobuf/proto" 34 ) 35 36 // ContractID is the ID for a Hedera smart contract 37 type ContractID struct { 38 Shard uint64 39 Realm uint64 40 Contract uint64 41 EvmAddress []byte 42 checksum *string 43 } 44 45 // ContractIDFromString constructs a ContractID from a string formatted as `Shard.Realm.Contract` (for example "0.0.3") 46 func ContractIDFromString(data string) (ContractID, error) { 47 shard, realm, num, checksum, evm, err := _ContractIDFromString(data) 48 if err != nil { 49 return ContractID{}, err 50 } 51 52 if num == -1 { 53 return ContractID{ 54 Shard: uint64(shard), 55 Realm: uint64(realm), 56 Contract: 0, 57 EvmAddress: evm, 58 checksum: checksum, 59 }, nil 60 } 61 62 return ContractID{ 63 Shard: uint64(shard), 64 Realm: uint64(realm), 65 Contract: uint64(num), 66 EvmAddress: nil, 67 checksum: checksum, 68 }, nil 69 } 70 71 // Verify that the client has a valid checksum. 72 func (id *ContractID) ValidateChecksum(client *Client) error { 73 if !id._IsZero() && client != nil { 74 var tempChecksum _ParseAddressResult 75 var err error 76 if client.network.ledgerID != nil { 77 tempChecksum, err = _ChecksumParseAddress(client.GetLedgerID(), fmt.Sprintf("%d.%d.%d", id.Shard, id.Realm, id.Contract)) 78 } 79 if err != nil { 80 return err 81 } 82 err = _ChecksumVerify(tempChecksum.status) 83 if err != nil { 84 return err 85 } 86 if id.checksum == nil { 87 return errChecksumMissing 88 } 89 if tempChecksum.correctChecksum != *id.checksum { 90 networkName := NetworkNameOther 91 if client.network.ledgerID != nil { 92 networkName, _ = client.network.ledgerID.ToNetworkName() 93 } 94 return errors.New(fmt.Sprintf("network mismatch or wrong checksum given, given checksum: %s, correct checksum %s, network: %s", 95 *id.checksum, 96 tempChecksum.correctChecksum, 97 networkName)) 98 } 99 } 100 101 return nil 102 } 103 104 // Deprecated - use ValidateChecksum instead 105 func (id *ContractID) Validate(client *Client) error { 106 return id.ValidateChecksum(client) 107 } 108 109 // ContractIDFromEvmAddress constructs a ContractID from a string representation of an EVM address 110 func ContractIDFromEvmAddress(shard uint64, realm uint64, evmAddress string) (ContractID, error) { 111 temp, err := hex.DecodeString(evmAddress) 112 if err != nil { 113 return ContractID{}, err 114 } 115 return ContractID{ 116 Shard: shard, 117 Realm: realm, 118 Contract: 0, 119 EvmAddress: temp, 120 checksum: nil, 121 }, nil 122 } 123 124 // ContractIDFromSolidityAddress constructs a ContractID from a string representation of a _Solidity address 125 // Does not populate ContractID.EvmAddress 126 // Deprecated 127 func ContractIDFromSolidityAddress(s string) (ContractID, error) { 128 shard, realm, contract, err := _IdFromSolidityAddress(s) 129 if err != nil { 130 return ContractID{}, err 131 } 132 133 return ContractID{ 134 Shard: shard, 135 Realm: realm, 136 Contract: contract, 137 }, nil 138 } 139 140 // String returns the string representation of a ContractID formatted as `Shard.Realm.Contract` (for example "0.0.3") 141 func (id ContractID) String() string { 142 if len(id.EvmAddress) > 0 { 143 temp := hex.EncodeToString(id.EvmAddress) 144 return fmt.Sprintf("%d.%d.%s", id.Shard, id.Realm, temp) 145 } 146 return fmt.Sprintf("%d.%d.%d", id.Shard, id.Realm, id.Contract) 147 } 148 149 // ToStringWithChecksum returns the string representation of a ContractID formatted as `Shard.Realm.Contract-Checksum` 150 func (id ContractID) ToStringWithChecksum(client Client) (string, error) { 151 if id.EvmAddress != nil { 152 return "", errors.New("EvmAddress doesn't support checksums") 153 } 154 if client.GetNetworkName() == nil && client.GetLedgerID() == nil { 155 return "", errNetworkNameMissing 156 } 157 var checksum _ParseAddressResult 158 var err error 159 if client.network.ledgerID != nil { 160 checksum, err = _ChecksumParseAddress(client.GetLedgerID(), fmt.Sprintf("%d.%d.%d", id.Shard, id.Realm, id.Contract)) 161 } 162 if err != nil { 163 return "", err 164 } 165 166 return fmt.Sprintf("%d.%d.%d-%s", id.Shard, id.Realm, id.Contract, checksum.correctChecksum), nil 167 } 168 169 // ToSolidityAddress returns the string representation of the ContractID as a _Solidity address. 170 func (id ContractID) ToSolidityAddress() string { 171 return _IdToSolidityAddress(id.Shard, id.Realm, id.Contract) 172 } 173 174 // PopulateContract gets the actual `Contract` field of the `ContractId` from the Mirror Node. 175 // Should be used after generating `ContractId.FromEvmAddress()` because it sets the `Contract` field to `0` 176 // automatically since there is no connection between the `Contract` and the `evmAddress` 177 func (id *ContractID) PopulateContract(client *Client) error { 178 if client.mirrorNetwork == nil || len(client.GetMirrorNetwork()) == 0 { 179 return errors.New("mirror node is not set") 180 } 181 mirrorUrl := client.GetMirrorNetwork()[0] 182 index := strings.Index(mirrorUrl, ":") 183 if index == -1 { 184 return errors.New("invalid mirrorUrl format") 185 } 186 mirrorUrl = mirrorUrl[:index] 187 url := fmt.Sprintf("https://%s/api/v1/contracts/%s", mirrorUrl, hex.EncodeToString(id.EvmAddress)) 188 if client.GetLedgerID().String() == "" { 189 url = fmt.Sprintf("http://%s:5551/api/v1/contracts/%s", mirrorUrl, hex.EncodeToString(id.EvmAddress)) 190 } 191 192 resp, err := http.Get(url) // #nosec 193 if err != nil { 194 return err 195 } 196 defer resp.Body.Close() 197 198 var result map[string]interface{} 199 if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { 200 return err 201 } 202 203 mirrorContractId, ok := result["contract_id"].(string) 204 if !ok { 205 return errors.New("unexpected response format") 206 } 207 208 numStr := mirrorContractId[strings.LastIndex(mirrorContractId, ".")+1:] 209 num, err := strconv.ParseInt(numStr, 10, 64) 210 if err != nil { 211 return err 212 } 213 id.Contract = uint64(num) 214 return nil 215 } 216 217 func (id ContractID) _ToProtobuf() *services.ContractID { 218 resultID := services.ContractID{ 219 ShardNum: int64(id.Shard), 220 RealmNum: int64(id.Realm), 221 } 222 223 if id.EvmAddress != nil { 224 resultID.Contract = &services.ContractID_EvmAddress{EvmAddress: id.EvmAddress} 225 return &resultID 226 } 227 228 resultID.Contract = &services.ContractID_ContractNum{ContractNum: int64(id.Contract)} 229 230 return &resultID 231 } 232 233 func _ContractIDFromProtobuf(contractID *services.ContractID) *ContractID { 234 if contractID == nil { 235 return nil 236 } 237 resultID := ContractID{ 238 Shard: uint64(contractID.ShardNum), 239 Realm: uint64(contractID.RealmNum), 240 } 241 242 switch id := contractID.Contract.(type) { 243 case *services.ContractID_ContractNum: 244 resultID.Contract = uint64(id.ContractNum) 245 resultID.EvmAddress = nil 246 return &resultID 247 case *services.ContractID_EvmAddress: 248 resultID.EvmAddress = id.EvmAddress 249 resultID.Contract = 0 250 return &resultID 251 default: 252 return &resultID 253 } 254 } 255 256 func (id ContractID) _IsZero() bool { 257 return id.Shard == 0 && id.Realm == 0 && id.Contract == 0 258 } 259 260 func (id ContractID) _ToProtoKey() *services.Key { 261 return &services.Key{Key: &services.Key_ContractID{ContractID: id._ToProtobuf()}} 262 } 263 264 // ToBytes returns a byte array representation of the ContractID 265 func (id ContractID) ToBytes() []byte { 266 data, err := protobuf.Marshal(id._ToProtobuf()) 267 if err != nil { 268 return make([]byte, 0) 269 } 270 271 return data 272 } 273 274 // ContractIDFromBytes returns a ContractID generated from a byte array 275 func ContractIDFromBytes(data []byte) (ContractID, error) { 276 pb := services.ContractID{} 277 err := protobuf.Unmarshal(data, &pb) 278 if err != nil { 279 return ContractID{}, err 280 } 281 282 return *_ContractIDFromProtobuf(&pb), nil 283 }