github.com/klaytn/klaytn@v1.10.2/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 // Try to load the address from the cache. 197 sender, err := types.Sender(&senderFromServer{blockhash: block}, tx) 198 if err == nil { 199 return sender, nil 200 } 201 var meta struct { 202 Hash common.Hash 203 From common.Address 204 } 205 if err = ec.c.CallContext(ctx, &meta, "klay_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index)); err != nil { 206 return common.Address{}, err 207 } 208 if meta.Hash == (common.Hash{}) || meta.Hash != tx.Hash() { 209 return common.Address{}, errors.New("wrong inclusion block/index") 210 } 211 return meta.From, nil 212 } 213 214 // TransactionCount returns the total number of transactions in the given block. 215 func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { 216 var num hexutil.Uint 217 err := ec.c.CallContext(ctx, &num, "klay_getBlockTransactionCountByHash", blockHash) 218 return uint(num), err 219 } 220 221 // TransactionInBlock returns a single transaction at index in the given block. 222 func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { 223 var json *rpcTransaction 224 err := ec.c.CallContext(ctx, &json, "klay_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index)) 225 if err == nil { 226 if json == nil { 227 return nil, klaytn.NotFound 228 } else if sigs := json.tx.RawSignatureValues(); sigs[0].V == nil { 229 return nil, fmt.Errorf("server returned transaction without signature") 230 } 231 } 232 if json.From != nil && json.BlockHash != nil { 233 setSenderFromServer(json.tx, *json.From, *json.BlockHash) 234 } 235 return json.tx, err 236 } 237 238 // TransactionReceipt returns the receipt of a transaction by transaction hash. 239 // Note that the receipt is not available for pending transactions. 240 func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { 241 var r *types.Receipt 242 err := ec.c.CallContext(ctx, &r, "klay_getTransactionReceipt", txHash) 243 if err == nil { 244 if r == nil { 245 return nil, klaytn.NotFound 246 } 247 } 248 return r, err 249 } 250 251 // TransactionReceiptRpcOutput returns the receipt of a transaction by transaction hash as a rpc output. 252 func (ec *Client) TransactionReceiptRpcOutput(ctx context.Context, txHash common.Hash) (r map[string]interface{}, err error) { 253 err = ec.c.CallContext(ctx, &r, "klay_getTransactionReceipt", txHash) 254 if err == nil && r == nil { 255 return nil, klaytn.NotFound 256 } 257 return 258 } 259 260 func toBlockNumArg(number *big.Int) string { 261 if number == nil { 262 return "latest" 263 } 264 return hexutil.EncodeBig(number) 265 } 266 267 type rpcProgress struct { 268 StartingBlock hexutil.Uint64 269 CurrentBlock hexutil.Uint64 270 HighestBlock hexutil.Uint64 271 PulledStates hexutil.Uint64 272 KnownStates hexutil.Uint64 273 } 274 275 // SyncProgress retrieves the current progress of the sync algorithm. If there's 276 // no sync currently running, it returns nil. 277 func (ec *Client) SyncProgress(ctx context.Context) (*klaytn.SyncProgress, error) { 278 var raw json.RawMessage 279 if err := ec.c.CallContext(ctx, &raw, "klay_syncing"); err != nil { 280 return nil, err 281 } 282 // Handle the possible response types 283 var syncing bool 284 if err := json.Unmarshal(raw, &syncing); err == nil { 285 return nil, nil // Not syncing (always false) 286 } 287 var progress *rpcProgress 288 if err := json.Unmarshal(raw, &progress); err != nil { 289 return nil, err 290 } 291 return &klaytn.SyncProgress{ 292 StartingBlock: uint64(progress.StartingBlock), 293 CurrentBlock: uint64(progress.CurrentBlock), 294 HighestBlock: uint64(progress.HighestBlock), 295 PulledStates: uint64(progress.PulledStates), 296 KnownStates: uint64(progress.KnownStates), 297 }, nil 298 } 299 300 // SubscribeNewHead subscribes to notifications about the current blockchain head 301 // on the given channel. 302 func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (klaytn.Subscription, error) { 303 return ec.c.KlaySubscribe(ctx, ch, "newHeads") 304 } 305 306 // State Access 307 308 // NetworkID returns the network ID (also known as the chain ID) for this chain. 309 func (ec *Client) NetworkID(ctx context.Context) (*big.Int, error) { 310 version := new(big.Int) 311 var ver string 312 if err := ec.c.CallContext(ctx, &ver, "net_version"); err != nil { 313 return nil, err 314 } 315 if _, ok := version.SetString(ver, 10); !ok { 316 return nil, fmt.Errorf("invalid net_version result %q", ver) 317 } 318 return version, nil 319 } 320 321 // BalanceAt returns the peb balance of the given account. 322 // The block number can be nil, in which case the balance is taken from the latest known block. 323 func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { 324 var result hexutil.Big 325 err := ec.c.CallContext(ctx, &result, "klay_getBalance", account, toBlockNumArg(blockNumber)) 326 return (*big.Int)(&result), err 327 } 328 329 // StorageAt returns the value of key in the contract storage of the given account. 330 // The block number can be nil, in which case the value is taken from the latest known block. 331 func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { 332 var result hexutil.Bytes 333 err := ec.c.CallContext(ctx, &result, "klay_getStorageAt", account, key, toBlockNumArg(blockNumber)) 334 return result, err 335 } 336 337 // CodeAt returns the contract code of the given account. 338 // The block number can be nil, in which case the code is taken from the latest known block. 339 func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { 340 var result hexutil.Bytes 341 err := ec.c.CallContext(ctx, &result, "klay_getCode", account, toBlockNumArg(blockNumber)) 342 return result, err 343 } 344 345 // NonceAt returns the account nonce of the given account. 346 // The block number can be nil, in which case the nonce is taken from the latest known block. 347 func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { 348 var result hexutil.Uint64 349 err := ec.c.CallContext(ctx, &result, "klay_getTransactionCount", account, toBlockNumArg(blockNumber)) 350 return uint64(result), err 351 } 352 353 // Filters 354 355 // FilterLogs executes a filter query. 356 func (ec *Client) FilterLogs(ctx context.Context, q klaytn.FilterQuery) ([]types.Log, error) { 357 var result []types.Log 358 err := ec.c.CallContext(ctx, &result, "klay_getLogs", toFilterArg(q)) 359 return result, err 360 } 361 362 // SubscribeFilterLogs subscribes to the results of a streaming filter query. 363 func (ec *Client) SubscribeFilterLogs(ctx context.Context, q klaytn.FilterQuery, ch chan<- types.Log) (klaytn.Subscription, error) { 364 return ec.c.KlaySubscribe(ctx, ch, "logs", toFilterArg(q)) 365 } 366 367 func toFilterArg(q klaytn.FilterQuery) interface{} { 368 arg := map[string]interface{}{ 369 "fromBlock": toBlockNumArg(q.FromBlock), 370 "toBlock": toBlockNumArg(q.ToBlock), 371 "address": q.Addresses, 372 "topics": q.Topics, 373 } 374 if q.FromBlock == nil { 375 arg["fromBlock"] = "0x0" 376 } 377 return arg 378 } 379 380 // Pending State 381 382 // PendingBalanceAt returns the peb balance of the given account in the pending state. 383 func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { 384 var result hexutil.Big 385 err := ec.c.CallContext(ctx, &result, "klay_getBalance", account, "pending") 386 return (*big.Int)(&result), err 387 } 388 389 // PendingStorageAt returns the value of key in the contract storage of the given account in the pending state. 390 func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { 391 var result hexutil.Bytes 392 err := ec.c.CallContext(ctx, &result, "klay_getStorageAt", account, key, "pending") 393 return result, err 394 } 395 396 // PendingCodeAt returns the contract code of the given account in the pending state. 397 func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { 398 var result hexutil.Bytes 399 err := ec.c.CallContext(ctx, &result, "klay_getCode", account, "pending") 400 return result, err 401 } 402 403 // PendingNonceAt returns the account nonce of the given account in the pending state. 404 // This is the nonce that should be used for the next transaction. 405 func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 406 var result hexutil.Uint64 407 err := ec.c.CallContext(ctx, &result, "klay_getTransactionCount", account, "pending") 408 return uint64(result), err 409 } 410 411 // PendingTransactionCount returns the total number of transactions in the pending state. 412 func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { 413 var num hexutil.Uint 414 err := ec.c.CallContext(ctx, &num, "klay_getBlockTransactionCountByNumber", "pending") 415 return uint(num), err 416 } 417 418 // TODO: SubscribePendingTransactions (needs server side) 419 420 // Contract Calling 421 422 // CallContract executes a message call transaction, which is directly executed in the VM 423 // of the node, but never mined into the blockchain. 424 // 425 // blockNumber selects the block height at which the call runs. It can be nil, in which 426 // case the code is taken from the latest known block. Note that state from very old 427 // blocks might not be available. 428 func (ec *Client) CallContract(ctx context.Context, msg klaytn.CallMsg, blockNumber *big.Int) ([]byte, error) { 429 var hex hexutil.Bytes 430 err := ec.c.CallContext(ctx, &hex, "klay_call", toCallArg(msg), toBlockNumArg(blockNumber)) 431 if err != nil { 432 return nil, err 433 } 434 return hex, nil 435 } 436 437 // PendingCallContract executes a message call transaction using the EVM. 438 // The state seen by the contract call is the pending state. 439 func (ec *Client) PendingCallContract(ctx context.Context, msg klaytn.CallMsg) ([]byte, error) { 440 var hex hexutil.Bytes 441 err := ec.c.CallContext(ctx, &hex, "klay_call", toCallArg(msg), "pending") 442 if err != nil { 443 return nil, err 444 } 445 return hex, nil 446 } 447 448 // SuggestGasPrice retrieves the currently suggested gas price to allow a timely 449 // execution of a transaction. 450 func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 451 var hex hexutil.Big 452 if err := ec.c.CallContext(ctx, &hex, "klay_gasPrice"); err != nil { 453 return nil, err 454 } 455 return (*big.Int)(&hex), nil 456 } 457 458 // EstimateGas tries to estimate the gas needed to execute a specific transaction based on 459 // the latest state of the backend blockchain. There is no guarantee that this is 460 // the true gas limit requirement as other transactions may be added or removed by miners, 461 // but it should provide a basis for setting a reasonable default. 462 func (ec *Client) EstimateGas(ctx context.Context, msg klaytn.CallMsg) (uint64, error) { 463 var hex hexutil.Uint64 464 err := ec.c.CallContext(ctx, &hex, "klay_estimateGas", toCallArg(msg)) 465 if err != nil { 466 return 0, err 467 } 468 return uint64(hex), nil 469 } 470 471 // SendTransaction injects a signed transaction into the pending pool for execution. 472 // 473 // If the transaction was a contract creation use the TransactionReceipt method to get the 474 // contract address after the transaction has been mined. 475 func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error { 476 _, err := ec.SendRawTransaction(ctx, tx) 477 return err 478 //data, err := rlp.EncodeToBytes(tx) 479 //if err != nil { 480 // return err 481 //} 482 //return ec.c.CallContext(ctx, nil, "klay_sendRawTransaction", common.ToHex(data)) 483 } 484 485 // SendRawTransaction injects a signed transaction into the pending pool for execution. 486 // 487 // This function can return the transaction hash and error. 488 func (ec *Client) SendRawTransaction(ctx context.Context, tx *types.Transaction) (common.Hash, error) { 489 var hex hexutil.Bytes 490 data, err := rlp.EncodeToBytes(tx) 491 if err != nil { 492 return common.Hash{}, err 493 } 494 if err := ec.c.CallContext(ctx, &hex, "klay_sendRawTransaction", hexutil.Encode(data)); err != nil { 495 return common.Hash{}, err 496 } 497 hash := common.BytesToHash(hex) 498 return hash, nil 499 } 500 501 // SendUnsignedTransaction injects a unsigned transaction into the pending pool for execution. 502 // 503 // This function can return the transaction hash and error. 504 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) { 505 var hex hexutil.Bytes 506 507 tGas := hexutil.Uint64(gas) 508 bigGasPrice := new(big.Int).SetUint64(gasPrice) 509 tGasPrice := (*hexutil.Big)(bigGasPrice) 510 hValue := (*hexutil.Big)(value) 511 tData := hexutil.Bytes(data) 512 tInput := hexutil.Bytes(input) 513 514 unsignedTx := api.SendTxArgs{ 515 From: from, 516 Recipient: &to, 517 GasLimit: &tGas, 518 Price: tGasPrice, 519 Amount: hValue, 520 // Nonce : nonce, Nonce will be determined by Klay node. 521 Data: &tData, 522 Payload: &tInput, 523 } 524 525 if err := ec.c.CallContext(ctx, &hex, "klay_sendTransaction", toSendTxArgs(unsignedTx)); err != nil { 526 return common.Hash{}, err 527 } 528 hash := common.BytesToHash(hex) 529 return hash, nil 530 } 531 532 // ImportRawKey can create key store from private key string on Klaytn node. 533 func (ec *Client) ImportRawKey(ctx context.Context, key string, password string) (common.Address, error) { 534 var result hexutil.Bytes 535 err := ec.c.CallContext(ctx, &result, "personal_importRawKey", key, password) 536 address := common.BytesToAddress(result) 537 return address, err 538 } 539 540 // UnlockAccount can unlock the account on Klaytn node. 541 func (ec *Client) UnlockAccount(ctx context.Context, address common.Address, password string, time uint) (bool, error) { 542 var result bool 543 err := ec.c.CallContext(ctx, &result, "personal_unlockAccount", address, password, time) 544 return result, err 545 } 546 547 func toCallArg(msg klaytn.CallMsg) interface{} { 548 arg := map[string]interface{}{ 549 "from": msg.From, 550 "to": msg.To, 551 } 552 if len(msg.Data) > 0 { 553 arg["data"] = hexutil.Bytes(msg.Data) 554 } 555 if msg.Value != nil { 556 arg["value"] = (*hexutil.Big)(msg.Value) 557 } 558 if msg.Gas != 0 { 559 arg["gas"] = hexutil.Uint64(msg.Gas) 560 } 561 if msg.GasPrice != nil { 562 arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) 563 } 564 return arg 565 } 566 567 func toSendTxArgs(msg api.SendTxArgs) interface{} { 568 arg := map[string]interface{}{ 569 "from": msg.From, 570 "to": msg.Recipient, 571 } 572 if *msg.GasLimit != 0 { 573 arg["gas"] = (*hexutil.Uint64)(msg.GasLimit) 574 } 575 if msg.Price != nil { 576 arg["gasPrice"] = (*hexutil.Big)(msg.Price) 577 } 578 if msg.Amount != nil { 579 arg["value"] = (*hexutil.Big)(msg.Amount) 580 } 581 if len(*msg.Data) > 0 { 582 arg["data"] = (*hexutil.Bytes)(msg.Data) 583 } 584 if len(*msg.Payload) > 0 { 585 arg["input"] = (*hexutil.Bytes)(msg.Payload) 586 } 587 588 return arg 589 } 590 591 // BlockNumber can get the latest block number. 592 func (ec *Client) BlockNumber(ctx context.Context) (*big.Int, error) { 593 var result hexutil.Big 594 err := ec.c.CallContext(ctx, &result, "klay_blockNumber") 595 return (*big.Int)(&result), err 596 } 597 598 // ChainID can return the chain ID of the chain. 599 func (ec *Client) ChainID(ctx context.Context) (*big.Int, error) { 600 if ec.chainID != nil { 601 return ec.chainID, nil 602 } 603 604 var result hexutil.Big 605 err := ec.c.CallContext(ctx, &result, "klay_chainID") 606 if err == nil { 607 ec.chainID = (*big.Int)(&result) 608 } 609 return ec.chainID, err 610 } 611 612 // AddPeer can add a static peer on Klaytn node. 613 func (ec *Client) AddPeer(ctx context.Context, url string) (bool, error) { 614 var result bool 615 err := ec.c.CallContext(ctx, &result, "admin_addPeer", url) 616 return result, err 617 } 618 619 // RemovePeer can remove a static peer on Klaytn node. 620 func (ec *Client) RemovePeer(ctx context.Context, url string) (bool, error) { 621 var result bool 622 err := ec.c.CallContext(ctx, &result, "admin_removePeer", url) 623 return result, err 624 } 625 626 // CreateAccessList tries to create an access list for a specific transaction based on the 627 // current pending state of the blockchain. 628 func (ec *Client) CreateAccessList(ctx context.Context, msg klaytn.CallMsg) (*types.AccessList, uint64, string, error) { 629 type AccessListResult struct { 630 Accesslist *types.AccessList `json:"accessList"` 631 Error string `json:"error,omitempty"` 632 GasUsed hexutil.Uint64 `json:"gasUsed"` 633 } 634 var result AccessListResult 635 if err := ec.c.CallContext(ctx, &result, "klay_createAccessList", toCallArg(msg)); err != nil { 636 return nil, 0, "", err 637 } 638 return result.Accesslist, uint64(result.GasUsed), result.Error, nil 639 }