github.com/klaytn/klaytn@v1.12.1/client/klay_client.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2016 The go-ethereum Authors 3 // This file is part of go-ethereum. 4 // 5 // go-ethereum is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // go-ethereum is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from ethclient/ethclient.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package client 22 23 import ( 24 "context" 25 "encoding/json" 26 "errors" 27 "fmt" 28 "math/big" 29 30 "github.com/klaytn/klaytn" 31 "github.com/klaytn/klaytn/api" 32 "github.com/klaytn/klaytn/blockchain/types" 33 "github.com/klaytn/klaytn/common" 34 "github.com/klaytn/klaytn/common/hexutil" 35 "github.com/klaytn/klaytn/networks/rpc" 36 "github.com/klaytn/klaytn/rlp" 37 ) 38 39 // TODO-Klaytn Needs to separate APIs along with each namespaces. 40 41 // Client defines typed wrappers for the Klaytn RPC API. 42 type Client struct { 43 c *rpc.Client 44 chainID *big.Int 45 } 46 47 // Dial connects a client to the given URL. 48 func Dial(rawurl string) (*Client, error) { 49 return DialContext(context.Background(), rawurl) 50 } 51 52 func DialContext(ctx context.Context, rawurl string) (*Client, error) { 53 c, err := rpc.DialContext(ctx, rawurl) 54 if err != nil { 55 return nil, err 56 } 57 return NewClient(c), nil 58 } 59 60 // NewClient creates a client that uses the given RPC client. 61 func NewClient(c *rpc.Client) *Client { 62 return &Client{c, nil} 63 } 64 65 func (ec *Client) Close() { 66 ec.c.Close() 67 } 68 69 func (ec *Client) SetHeader(key, value string) { 70 ec.c.SetHeader(key, value) 71 } 72 73 // Blockchain Access 74 75 // BlockByHash returns the given full block. 76 // 77 // Note that loading full blocks requires two requests. Use HeaderByHash 78 // if you don't need all transactions. 79 func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 80 return ec.getBlock(ctx, "klay_getBlockByHash", hash, true) 81 } 82 83 // BlockByNumber returns a block from the current canonical chain. If number is nil, the 84 // latest known block is returned. 85 // 86 // Note that loading full blocks requires two requests. Use HeaderByNumber 87 // if you don't need all transactions. 88 func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { 89 return ec.getBlock(ctx, "klay_getBlockByNumber", toBlockNumArg(number), true) 90 } 91 92 type rpcBlock struct { 93 Hash common.Hash `json:"hash"` 94 Transactions []rpcTransaction `json:"transactions"` 95 } 96 97 func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { 98 var raw json.RawMessage 99 err := ec.c.CallContext(ctx, &raw, method, args...) 100 if err != nil { 101 return nil, err 102 } else if len(raw) == 0 { 103 return nil, klaytn.NotFound 104 } 105 // Decode header and transactions. 106 var head *types.Header 107 var body rpcBlock 108 if err := json.Unmarshal(raw, &head); err != nil { 109 return nil, err 110 } 111 if err := json.Unmarshal(raw, &body); err != nil { 112 return nil, err 113 } 114 // TODO-Klaytn Enable the below error checks after having a way to get the correct EmptyRootHash 115 // Quick-verify transaction lists. This mostly helps with debugging the server. 116 //if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 { 117 // return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions") 118 //} 119 //if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 { 120 // return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions") 121 //} 122 // Fill the sender cache of transactions in the block. 123 txs := make([]*types.Transaction, len(body.Transactions)) 124 for i, tx := range body.Transactions { 125 if tx.From != nil { 126 setSenderFromServer(tx.tx, *tx.From, body.Hash) 127 } 128 txs[i] = tx.tx 129 } 130 return types.NewBlockWithHeader(head).WithBody(txs), nil 131 } 132 133 // HeaderByHash returns the block header with the given hash. 134 func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { 135 var head *types.Header 136 err := ec.c.CallContext(ctx, &head, "klay_getBlockByHash", hash, false) 137 if err == nil && head == nil { 138 err = klaytn.NotFound 139 } 140 return head, err 141 } 142 143 // HeaderByNumber returns a block header from the current canonical chain. If number is 144 // nil, the latest known header is returned. 145 func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { 146 var head *types.Header 147 err := ec.c.CallContext(ctx, &head, "klay_getBlockByNumber", toBlockNumArg(number), false) 148 if err == nil && head == nil { 149 err = klaytn.NotFound 150 } 151 return head, err 152 } 153 154 type rpcTransaction struct { 155 tx *types.Transaction 156 txExtraInfo 157 } 158 159 type txExtraInfo struct { 160 BlockNumber *string `json:"blockNumber,omitempty"` 161 BlockHash *common.Hash `json:"blockHash,omitempty"` 162 From *common.Address `json:"from,omitempty"` 163 } 164 165 func (tx *rpcTransaction) UnmarshalJSON(msg []byte) error { 166 if err := json.Unmarshal(msg, &tx.tx); err != nil { 167 return err 168 } 169 return json.Unmarshal(msg, &tx.txExtraInfo) 170 } 171 172 // TransactionByHash returns the transaction with the given hash. 173 func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { 174 var json *rpcTransaction 175 err = ec.c.CallContext(ctx, &json, "klay_getTransactionByHash", hash) 176 if err != nil { 177 return nil, false, err 178 } else if json == nil { 179 return nil, false, klaytn.NotFound 180 } else if sigs := json.tx.RawSignatureValues(); sigs[0].V == nil { 181 return nil, false, fmt.Errorf("server returned transaction without signature") 182 } 183 if json.From != nil && json.BlockHash != nil { 184 setSenderFromServer(json.tx, *json.From, *json.BlockHash) 185 } 186 return json.tx, json.BlockNumber == nil, nil 187 } 188 189 // TransactionSender returns the sender address of the given transaction. The transaction 190 // must be known to the remote node and included in the blockchain at the given block and 191 // index. The sender is the one derived by the protocol at the time of inclusion. 192 // 193 // There is a fast-path for transactions retrieved by TransactionByHash and 194 // TransactionInBlock. Getting their sender address can be done without an RPC interaction. 195 func (ec *Client) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) { 196 if tx == nil { 197 return common.Address{}, errors.New("Transaction must not be nil") 198 } 199 // Try to load the address from the cache. 200 sender, err := types.Sender(&senderFromServer{blockhash: block}, tx) 201 if err == nil { 202 return sender, nil 203 } 204 var meta struct { 205 Hash common.Hash 206 From common.Address 207 } 208 if err = ec.c.CallContext(ctx, &meta, "klay_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index)); err != nil { 209 return common.Address{}, err 210 } 211 if meta.Hash == (common.Hash{}) || meta.Hash != tx.Hash() { 212 return common.Address{}, errors.New("wrong inclusion block/index") 213 } 214 return meta.From, nil 215 } 216 217 // TransactionCount returns the total number of transactions in the given block. 218 func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { 219 var num hexutil.Uint 220 err := ec.c.CallContext(ctx, &num, "klay_getBlockTransactionCountByHash", blockHash) 221 return uint(num), err 222 } 223 224 // TransactionInBlock returns a single transaction at index in the given block. 225 func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { 226 var json *rpcTransaction 227 err := ec.c.CallContext(ctx, &json, "klay_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index)) 228 if err != nil { 229 return nil, err 230 } 231 if json == nil { 232 return nil, klaytn.NotFound 233 } else if sigs := json.tx.RawSignatureValues(); sigs[0].V == nil { 234 return nil, fmt.Errorf("server returned transaction without signature") 235 } 236 if json.From != nil && json.BlockHash != nil { 237 setSenderFromServer(json.tx, *json.From, *json.BlockHash) 238 } 239 return json.tx, err 240 } 241 242 // TransactionReceipt returns the receipt of a transaction by transaction hash. 243 // Note that the receipt is not available for pending transactions. 244 func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { 245 var r *types.Receipt 246 err := ec.c.CallContext(ctx, &r, "klay_getTransactionReceipt", txHash) 247 if err == nil { 248 if r == nil { 249 return nil, klaytn.NotFound 250 } 251 } 252 return r, err 253 } 254 255 // TransactionReceiptRpcOutput returns the receipt of a transaction by transaction hash as a rpc output. 256 func (ec *Client) TransactionReceiptRpcOutput(ctx context.Context, txHash common.Hash) (r map[string]interface{}, err error) { 257 err = ec.c.CallContext(ctx, &r, "klay_getTransactionReceipt", txHash) 258 if err == nil && r == nil { 259 return nil, klaytn.NotFound 260 } 261 return 262 } 263 264 func toBlockNumArg(number *big.Int) string { 265 if number == nil { 266 return "latest" 267 } 268 return hexutil.EncodeBig(number) 269 } 270 271 type rpcProgress struct { 272 StartingBlock hexutil.Uint64 273 CurrentBlock hexutil.Uint64 274 HighestBlock hexutil.Uint64 275 PulledStates hexutil.Uint64 276 KnownStates hexutil.Uint64 277 } 278 279 // SyncProgress retrieves the current progress of the sync algorithm. If there's 280 // no sync currently running, it returns nil. 281 func (ec *Client) SyncProgress(ctx context.Context) (*klaytn.SyncProgress, error) { 282 var raw json.RawMessage 283 if err := ec.c.CallContext(ctx, &raw, "klay_syncing"); err != nil { 284 return nil, err 285 } 286 // Handle the possible response types 287 var syncing bool 288 if err := json.Unmarshal(raw, &syncing); err == nil { 289 return nil, nil // Not syncing (always false) 290 } 291 var progress *rpcProgress 292 if err := json.Unmarshal(raw, &progress); err != nil { 293 return nil, err 294 } 295 return &klaytn.SyncProgress{ 296 StartingBlock: uint64(progress.StartingBlock), 297 CurrentBlock: uint64(progress.CurrentBlock), 298 HighestBlock: uint64(progress.HighestBlock), 299 PulledStates: uint64(progress.PulledStates), 300 KnownStates: uint64(progress.KnownStates), 301 }, nil 302 } 303 304 // SubscribeNewHead subscribes to notifications about the current blockchain head 305 // on the given channel. 306 func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (klaytn.Subscription, error) { 307 return ec.c.KlaySubscribe(ctx, ch, "newHeads") 308 } 309 310 // State Access 311 312 // NetworkID returns the network ID (also known as the chain ID) for this chain. 313 func (ec *Client) NetworkID(ctx context.Context) (*big.Int, error) { 314 version := new(big.Int) 315 var ver string 316 if err := ec.c.CallContext(ctx, &ver, "net_version"); err != nil { 317 return nil, err 318 } 319 if _, ok := version.SetString(ver, 10); !ok { 320 return nil, fmt.Errorf("invalid net_version result %q", ver) 321 } 322 return version, nil 323 } 324 325 // BalanceAt returns the peb balance of the given account. 326 // The block number can be nil, in which case the balance is taken from the latest known block. 327 func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { 328 var result hexutil.Big 329 err := ec.c.CallContext(ctx, &result, "klay_getBalance", account, toBlockNumArg(blockNumber)) 330 return (*big.Int)(&result), err 331 } 332 333 // StorageAt returns the value of key in the contract storage of the given account. 334 // The block number can be nil, in which case the value is taken from the latest known block. 335 func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { 336 var result hexutil.Bytes 337 err := ec.c.CallContext(ctx, &result, "klay_getStorageAt", account, key, toBlockNumArg(blockNumber)) 338 return result, err 339 } 340 341 // CodeAt returns the contract code of the given account. 342 // The block number can be nil, in which case the code is taken from the latest known block. 343 func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { 344 var result hexutil.Bytes 345 err := ec.c.CallContext(ctx, &result, "klay_getCode", account, toBlockNumArg(blockNumber)) 346 return result, err 347 } 348 349 // NonceAt returns the account nonce of the given account. 350 // The block number can be nil, in which case the nonce is taken from the latest known block. 351 func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { 352 var result hexutil.Uint64 353 err := ec.c.CallContext(ctx, &result, "klay_getTransactionCount", account, toBlockNumArg(blockNumber)) 354 return uint64(result), err 355 } 356 357 // Filters 358 359 // FilterLogs executes a filter query. 360 func (ec *Client) FilterLogs(ctx context.Context, q klaytn.FilterQuery) ([]types.Log, error) { 361 var result []types.Log 362 err := ec.c.CallContext(ctx, &result, "klay_getLogs", toFilterArg(q)) 363 return result, err 364 } 365 366 // SubscribeFilterLogs subscribes to the results of a streaming filter query. 367 func (ec *Client) SubscribeFilterLogs(ctx context.Context, q klaytn.FilterQuery, ch chan<- types.Log) (klaytn.Subscription, error) { 368 return ec.c.KlaySubscribe(ctx, ch, "logs", toFilterArg(q)) 369 } 370 371 func toFilterArg(q klaytn.FilterQuery) interface{} { 372 arg := map[string]interface{}{ 373 "fromBlock": toBlockNumArg(q.FromBlock), 374 "toBlock": toBlockNumArg(q.ToBlock), 375 "address": q.Addresses, 376 "topics": q.Topics, 377 } 378 if q.FromBlock == nil { 379 arg["fromBlock"] = "0x0" 380 } 381 return arg 382 } 383 384 // Pending State 385 386 // PendingBalanceAt returns the peb balance of the given account in the pending state. 387 func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { 388 var result hexutil.Big 389 err := ec.c.CallContext(ctx, &result, "klay_getBalance", account, "pending") 390 return (*big.Int)(&result), err 391 } 392 393 // PendingStorageAt returns the value of key in the contract storage of the given account in the pending state. 394 func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { 395 var result hexutil.Bytes 396 err := ec.c.CallContext(ctx, &result, "klay_getStorageAt", account, key, "pending") 397 return result, err 398 } 399 400 // PendingCodeAt returns the contract code of the given account in the pending state. 401 func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { 402 var result hexutil.Bytes 403 err := ec.c.CallContext(ctx, &result, "klay_getCode", account, "pending") 404 return result, err 405 } 406 407 // PendingNonceAt returns the account nonce of the given account in the pending state. 408 // This is the nonce that should be used for the next transaction. 409 func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 410 var result hexutil.Uint64 411 err := ec.c.CallContext(ctx, &result, "klay_getTransactionCount", account, "pending") 412 return uint64(result), err 413 } 414 415 // PendingTransactionCount returns the total number of transactions in the pending state. 416 func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { 417 var num hexutil.Uint 418 err := ec.c.CallContext(ctx, &num, "klay_getBlockTransactionCountByNumber", "pending") 419 return uint(num), err 420 } 421 422 // TODO: SubscribePendingTransactions (needs server side) 423 424 // Contract Calling 425 426 // CallContract executes a message call transaction, which is directly executed in the VM 427 // of the node, but never mined into the blockchain. 428 // 429 // blockNumber selects the block height at which the call runs. It can be nil, in which 430 // case the code is taken from the latest known block. Note that state from very old 431 // blocks might not be available. 432 func (ec *Client) CallContract(ctx context.Context, msg klaytn.CallMsg, blockNumber *big.Int) ([]byte, error) { 433 var hex hexutil.Bytes 434 err := ec.c.CallContext(ctx, &hex, "klay_call", toCallArg(msg), toBlockNumArg(blockNumber)) 435 if err != nil { 436 return nil, err 437 } 438 return hex, nil 439 } 440 441 // PendingCallContract executes a message call transaction using the EVM. 442 // The state seen by the contract call is the pending state. 443 func (ec *Client) PendingCallContract(ctx context.Context, msg klaytn.CallMsg) ([]byte, error) { 444 var hex hexutil.Bytes 445 err := ec.c.CallContext(ctx, &hex, "klay_call", toCallArg(msg), "pending") 446 if err != nil { 447 return nil, err 448 } 449 return hex, nil 450 } 451 452 // SuggestGasPrice retrieves the currently suggested gas price to allow a timely 453 // execution of a transaction. 454 func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 455 var hex hexutil.Big 456 if err := ec.c.CallContext(ctx, &hex, "klay_gasPrice"); err != nil { 457 return nil, err 458 } 459 return (*big.Int)(&hex), nil 460 } 461 462 // EstimateGas tries to estimate the gas needed to execute a specific transaction based on 463 // the latest state of the backend blockchain. There is no guarantee that this is 464 // the true gas limit requirement as other transactions may be added or removed by miners, 465 // but it should provide a basis for setting a reasonable default. 466 func (ec *Client) EstimateGas(ctx context.Context, msg klaytn.CallMsg) (uint64, error) { 467 var hex hexutil.Uint64 468 err := ec.c.CallContext(ctx, &hex, "klay_estimateGas", toCallArg(msg)) 469 if err != nil { 470 return 0, err 471 } 472 return uint64(hex), nil 473 } 474 475 // SendTransaction injects a signed transaction into the pending pool for execution. 476 // 477 // If the transaction was a contract creation use the TransactionReceipt method to get the 478 // contract address after the transaction has been mined. 479 func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error { 480 _, err := ec.SendRawTransaction(ctx, tx) 481 return err 482 //data, err := rlp.EncodeToBytes(tx) 483 //if err != nil { 484 // return err 485 //} 486 //return ec.c.CallContext(ctx, nil, "klay_sendRawTransaction", common.ToHex(data)) 487 } 488 489 // SendRawTransaction injects a signed transaction into the pending pool for execution. 490 // 491 // This function can return the transaction hash and error. 492 func (ec *Client) SendRawTransaction(ctx context.Context, tx *types.Transaction) (common.Hash, error) { 493 var hex hexutil.Bytes 494 data, err := rlp.EncodeToBytes(tx) 495 if err != nil { 496 return common.Hash{}, err 497 } 498 if err := ec.c.CallContext(ctx, &hex, "klay_sendRawTransaction", hexutil.Encode(data)); err != nil { 499 return common.Hash{}, err 500 } 501 hash := common.BytesToHash(hex) 502 return hash, nil 503 } 504 505 // SendUnsignedTransaction injects a unsigned transaction into the pending pool for execution. 506 // 507 // This function can return the transaction hash and error. 508 func (ec *Client) SendUnsignedTransaction(ctx context.Context, from common.Address, to common.Address, gas uint64, gasPrice uint64, value *big.Int, data []byte, input []byte) (common.Hash, error) { 509 var hex hexutil.Bytes 510 511 tGas := hexutil.Uint64(gas) 512 bigGasPrice := new(big.Int).SetUint64(gasPrice) 513 tGasPrice := (*hexutil.Big)(bigGasPrice) 514 hValue := (*hexutil.Big)(value) 515 tData := hexutil.Bytes(data) 516 tInput := hexutil.Bytes(input) 517 518 unsignedTx := api.SendTxArgs{ 519 From: from, 520 Recipient: &to, 521 GasLimit: &tGas, 522 Price: tGasPrice, 523 Amount: hValue, 524 // Nonce : nonce, Nonce will be determined by Klay node. 525 Data: &tData, 526 Payload: &tInput, 527 } 528 529 if err := ec.c.CallContext(ctx, &hex, "klay_sendTransaction", toSendTxArgs(unsignedTx)); err != nil { 530 return common.Hash{}, err 531 } 532 hash := common.BytesToHash(hex) 533 return hash, nil 534 } 535 536 // ImportRawKey can create key store from private key string on Klaytn node. 537 func (ec *Client) ImportRawKey(ctx context.Context, key string, password string) (common.Address, error) { 538 var result hexutil.Bytes 539 err := ec.c.CallContext(ctx, &result, "personal_importRawKey", key, password) 540 address := common.BytesToAddress(result) 541 return address, err 542 } 543 544 // UnlockAccount can unlock the account on Klaytn node. 545 func (ec *Client) UnlockAccount(ctx context.Context, address common.Address, password string, time uint) (bool, error) { 546 var result bool 547 err := ec.c.CallContext(ctx, &result, "personal_unlockAccount", address, password, time) 548 return result, err 549 } 550 551 func toCallArg(msg klaytn.CallMsg) interface{} { 552 arg := map[string]interface{}{ 553 "from": msg.From, 554 "to": msg.To, 555 } 556 if len(msg.Data) > 0 { 557 arg["data"] = hexutil.Bytes(msg.Data) 558 } 559 if msg.Value != nil { 560 arg["value"] = (*hexutil.Big)(msg.Value) 561 } 562 if msg.Gas != 0 { 563 arg["gas"] = hexutil.Uint64(msg.Gas) 564 } 565 if msg.GasPrice != nil { 566 arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) 567 } 568 return arg 569 } 570 571 func toSendTxArgs(msg api.SendTxArgs) interface{} { 572 arg := map[string]interface{}{ 573 "from": msg.From, 574 "to": msg.Recipient, 575 } 576 if *msg.GasLimit != 0 { 577 arg["gas"] = (*hexutil.Uint64)(msg.GasLimit) 578 } 579 if msg.Price != nil { 580 arg["gasPrice"] = (*hexutil.Big)(msg.Price) 581 } 582 if msg.Amount != nil { 583 arg["value"] = (*hexutil.Big)(msg.Amount) 584 } 585 if len(*msg.Data) > 0 { 586 arg["data"] = (*hexutil.Bytes)(msg.Data) 587 } 588 if len(*msg.Payload) > 0 { 589 arg["input"] = (*hexutil.Bytes)(msg.Payload) 590 } 591 592 return arg 593 } 594 595 // BlockNumber can get the latest block number. 596 func (ec *Client) BlockNumber(ctx context.Context) (*big.Int, error) { 597 var result hexutil.Big 598 err := ec.c.CallContext(ctx, &result, "klay_blockNumber") 599 return (*big.Int)(&result), err 600 } 601 602 // ChainID can return the chain ID of the chain. 603 func (ec *Client) ChainID(ctx context.Context) (*big.Int, error) { 604 if ec.chainID != nil { 605 return ec.chainID, nil 606 } 607 608 var result hexutil.Big 609 err := ec.c.CallContext(ctx, &result, "klay_chainID") 610 if err == nil { 611 ec.chainID = (*big.Int)(&result) 612 } 613 return ec.chainID, err 614 } 615 616 // AddPeer can add a static peer on Klaytn node. 617 func (ec *Client) AddPeer(ctx context.Context, url string) (bool, error) { 618 var result bool 619 err := ec.c.CallContext(ctx, &result, "admin_addPeer", url) 620 return result, err 621 } 622 623 // RemovePeer can remove a static peer on Klaytn node. 624 func (ec *Client) RemovePeer(ctx context.Context, url string) (bool, error) { 625 var result bool 626 err := ec.c.CallContext(ctx, &result, "admin_removePeer", url) 627 return result, err 628 } 629 630 // CreateAccessList tries to create an access list for a specific transaction based on the 631 // current pending state of the blockchain. 632 func (ec *Client) CreateAccessList(ctx context.Context, msg klaytn.CallMsg) (*types.AccessList, uint64, string, error) { 633 type AccessListResult struct { 634 Accesslist *types.AccessList `json:"accessList"` 635 Error string `json:"error,omitempty"` 636 GasUsed hexutil.Uint64 `json:"gasUsed"` 637 } 638 var result AccessListResult 639 if err := ec.c.CallContext(ctx, &result, "klay_createAccessList", toCallArg(msg)); err != nil { 640 return nil, 0, "", err 641 } 642 return result.Accesslist, uint64(result.GasUsed), result.Error, nil 643 }