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  }