github.com/status-im/status-go@v1.1.0/services/ens/api.go (about) 1 package ens 2 3 import ( 4 "context" 5 "database/sql" 6 "encoding/binary" 7 "encoding/hex" 8 "fmt" 9 "math/big" 10 "net/url" 11 "strings" 12 "sync" 13 "time" 14 15 "github.com/ipfs/go-cid" 16 "github.com/multiformats/go-multibase" 17 "github.com/multiformats/go-multihash" 18 "github.com/pkg/errors" 19 "github.com/wealdtech/go-ens/v3" 20 "github.com/wealdtech/go-multicodec" 21 22 "github.com/ethereum/go-ethereum" 23 "github.com/ethereum/go-ethereum/accounts/abi" 24 "github.com/ethereum/go-ethereum/accounts/abi/bind" 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/common/hexutil" 27 "github.com/ethereum/go-ethereum/log" 28 "github.com/status-im/status-go/account" 29 "github.com/status-im/status-go/contracts" 30 "github.com/status-im/status-go/contracts/registrar" 31 "github.com/status-im/status-go/contracts/resolver" 32 "github.com/status-im/status-go/contracts/snt" 33 "github.com/status-im/status-go/params" 34 "github.com/status-im/status-go/rpc" 35 "github.com/status-im/status-go/services/utils" 36 wcommon "github.com/status-im/status-go/services/wallet/common" 37 "github.com/status-im/status-go/transactions" 38 ) 39 40 const StatusDomain = "stateofus.eth" 41 42 func NewAPI(rpcClient *rpc.Client, accountsManager *account.GethManager, pendingTracker *transactions.PendingTxTracker, config *params.NodeConfig, appDb *sql.DB, timeSource func() time.Time, syncUserDetailFunc *syncUsernameDetail) *API { 43 return &API{ 44 contractMaker: &contracts.ContractMaker{ 45 RPCClient: rpcClient, 46 }, 47 accountsManager: accountsManager, 48 pendingTracker: pendingTracker, 49 config: config, 50 addrPerChain: make(map[uint64]common.Address), 51 db: NewEnsDatabase(appDb), 52 53 quit: make(chan struct{}), 54 timeSource: timeSource, 55 syncUserDetailFunc: syncUserDetailFunc, 56 } 57 } 58 59 type URI struct { 60 Scheme string 61 Host string 62 Path string 63 } 64 65 // use this to avoid using messenger directly to avoid circular dependency (protocol->ens->protocol) 66 type syncUsernameDetail func(context.Context, *UsernameDetail) error 67 68 type API struct { 69 contractMaker *contracts.ContractMaker 70 accountsManager *account.GethManager 71 pendingTracker *transactions.PendingTxTracker 72 config *params.NodeConfig 73 74 addrPerChain map[uint64]common.Address 75 addrPerChainMutex sync.Mutex 76 77 quitOnce sync.Once 78 quit chan struct{} 79 80 db *Database 81 syncUserDetailFunc *syncUsernameDetail 82 83 timeSource func() time.Time 84 } 85 86 func (api *API) Stop() { 87 api.quitOnce.Do(func() { 88 close(api.quit) 89 }) 90 } 91 92 func (api *API) unixTime() uint64 { 93 return uint64(api.timeSource().Unix()) 94 } 95 96 func (api *API) GetEnsUsernames(ctx context.Context) ([]*UsernameDetail, error) { 97 removed := false 98 return api.db.GetEnsUsernames(&removed) 99 } 100 101 func (api *API) Add(ctx context.Context, chainID uint64, username string) error { 102 ud := &UsernameDetail{Username: username, ChainID: chainID, Clock: api.unixTime()} 103 err := api.db.AddEnsUsername(ud) 104 if err != nil { 105 return err 106 } 107 return (*api.syncUserDetailFunc)(ctx, ud) 108 } 109 110 func (api *API) Remove(ctx context.Context, chainID uint64, username string) error { 111 ud := &UsernameDetail{Username: username, ChainID: chainID, Clock: api.unixTime()} 112 affected, err := api.db.RemoveEnsUsername(ud) 113 if err != nil { 114 return err 115 } 116 if affected { 117 return (*api.syncUserDetailFunc)(ctx, ud) 118 } 119 return nil 120 } 121 122 func (api *API) GetRegistrarAddress(ctx context.Context, chainID uint64) (common.Address, error) { 123 return api.usernameRegistrarAddr(ctx, chainID) 124 } 125 126 func (api *API) Resolver(ctx context.Context, chainID uint64, username string) (*common.Address, error) { 127 err := ValidateENSUsername(username) 128 if err != nil { 129 return nil, err 130 } 131 132 registry, err := api.contractMaker.NewRegistry(chainID) 133 if err != nil { 134 return nil, err 135 } 136 137 callOpts := &bind.CallOpts{Context: ctx, Pending: false} 138 resolver, err := registry.Resolver(callOpts, NameHash(username)) 139 if err != nil { 140 return nil, err 141 } 142 143 return &resolver, nil 144 } 145 146 func (api *API) GetName(ctx context.Context, chainID uint64, address common.Address) (string, error) { 147 backend, err := api.contractMaker.RPCClient.EthClient(chainID) 148 if err != nil { 149 return "", err 150 } 151 return ens.ReverseResolve(backend, address) 152 } 153 154 func (api *API) OwnerOf(ctx context.Context, chainID uint64, username string) (*common.Address, error) { 155 err := ValidateENSUsername(username) 156 if err != nil { 157 return nil, err 158 } 159 160 registry, err := api.contractMaker.NewRegistry(chainID) 161 if err != nil { 162 return nil, err 163 } 164 165 callOpts := &bind.CallOpts{Context: ctx, Pending: false} 166 owner, err := registry.Owner(callOpts, NameHash(username)) 167 if err != nil { 168 return nil, err 169 } 170 171 return &owner, nil 172 } 173 174 func (api *API) ContentHash(ctx context.Context, chainID uint64, username string) ([]byte, error) { 175 err := ValidateENSUsername(username) 176 if err != nil { 177 return nil, err 178 } 179 180 resolverAddress, err := api.Resolver(ctx, chainID, username) 181 if err != nil { 182 return nil, err 183 } 184 185 resolver, err := api.contractMaker.NewPublicResolver(chainID, resolverAddress) 186 if err != nil { 187 return nil, err 188 } 189 190 callOpts := &bind.CallOpts{Context: ctx, Pending: false} 191 contentHash, err := resolver.Contenthash(callOpts, NameHash(username)) 192 if err != nil { 193 return nil, nil 194 } 195 196 return contentHash, nil 197 } 198 199 func (api *API) PublicKeyOf(ctx context.Context, chainID uint64, username string) (string, error) { 200 err := ValidateENSUsername(username) 201 if err != nil { 202 return "", err 203 } 204 205 resolverAddress, err := api.Resolver(ctx, chainID, username) 206 if err != nil { 207 return "", err 208 } 209 210 resolver, err := api.contractMaker.NewPublicResolver(chainID, resolverAddress) 211 if err != nil { 212 return "", err 213 } 214 215 callOpts := &bind.CallOpts{Context: ctx, Pending: false} 216 pubKey, err := resolver.Pubkey(callOpts, NameHash(username)) 217 if err != nil { 218 return "", err 219 } 220 return "0x04" + hex.EncodeToString(pubKey.X[:]) + hex.EncodeToString(pubKey.Y[:]), nil 221 } 222 223 func (api *API) AddressOf(ctx context.Context, chainID uint64, username string) (*common.Address, error) { 224 err := ValidateENSUsername(username) 225 if err != nil { 226 return nil, err 227 } 228 229 resolverAddress, err := api.Resolver(ctx, chainID, username) 230 if err != nil { 231 return nil, err 232 } 233 234 resolver, err := api.contractMaker.NewPublicResolver(chainID, resolverAddress) 235 if err != nil { 236 return nil, err 237 } 238 239 callOpts := &bind.CallOpts{Context: ctx, Pending: false} 240 addr, err := resolver.Addr(callOpts, NameHash(username)) 241 if err != nil { 242 return nil, err 243 } 244 245 return &addr, nil 246 } 247 248 func (api *API) usernameRegistrarAddr(ctx context.Context, chainID uint64) (common.Address, error) { 249 log.Info("obtaining username registrar address") 250 api.addrPerChainMutex.Lock() 251 defer api.addrPerChainMutex.Unlock() 252 addr, ok := api.addrPerChain[chainID] 253 if ok { 254 return addr, nil 255 } 256 257 registryAddr, err := api.OwnerOf(ctx, chainID, StatusDomain) 258 if err != nil { 259 return common.Address{}, err 260 } 261 262 api.addrPerChain[chainID] = *registryAddr 263 264 go func() { 265 registry, err := api.contractMaker.NewRegistry(chainID) 266 if err != nil { 267 return 268 } 269 270 logs := make(chan *resolver.ENSRegistryWithFallbackNewOwner) 271 272 sub, err := registry.WatchNewOwner(&bind.WatchOpts{}, logs, nil, nil) 273 if err != nil { 274 return 275 } 276 277 for { 278 select { 279 case <-api.quit: 280 log.Info("quitting ens contract subscription") 281 sub.Unsubscribe() 282 return 283 case err := <-sub.Err(): 284 if err != nil { 285 log.Error("ens contract subscription error: " + err.Error()) 286 } 287 return 288 case vLog := <-logs: 289 api.addrPerChainMutex.Lock() 290 api.addrPerChain[chainID] = vLog.Owner 291 api.addrPerChainMutex.Unlock() 292 } 293 } 294 }() 295 296 return *registryAddr, nil 297 } 298 299 func (api *API) ExpireAt(ctx context.Context, chainID uint64, username string) (string, error) { 300 registryAddr, err := api.usernameRegistrarAddr(ctx, chainID) 301 if err != nil { 302 return "", err 303 } 304 305 registrar, err := api.contractMaker.NewUsernameRegistrar(chainID, registryAddr) 306 if err != nil { 307 return "", err 308 } 309 310 callOpts := &bind.CallOpts{Context: ctx, Pending: false} 311 expTime, err := registrar.GetExpirationTime(callOpts, UsernameToLabel(username)) 312 if err != nil { 313 return "", err 314 } 315 316 return fmt.Sprintf("%x", expTime), nil 317 } 318 319 func (api *API) Price(ctx context.Context, chainID uint64) (string, error) { 320 registryAddr, err := api.usernameRegistrarAddr(ctx, chainID) 321 if err != nil { 322 return "", err 323 } 324 325 registrar, err := api.contractMaker.NewUsernameRegistrar(chainID, registryAddr) 326 if err != nil { 327 return "", err 328 } 329 330 callOpts := &bind.CallOpts{Context: ctx, Pending: false} 331 price, err := registrar.GetPrice(callOpts) 332 if err != nil { 333 return "", err 334 } 335 336 return fmt.Sprintf("%x", price), nil 337 } 338 339 func (api *API) Release(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, password string, username string) (string, error) { 340 registryAddr, err := api.usernameRegistrarAddr(ctx, chainID) 341 if err != nil { 342 return "", err 343 } 344 345 registrar, err := api.contractMaker.NewUsernameRegistrar(chainID, registryAddr) 346 if err != nil { 347 return "", err 348 } 349 350 txOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password)) 351 tx, err := registrar.Release(txOpts, UsernameToLabel(username)) 352 if err != nil { 353 return "", err 354 } 355 356 err = api.pendingTracker.TrackPendingTransaction( 357 wcommon.ChainID(chainID), 358 tx.Hash(), 359 common.Address(txArgs.From), 360 registryAddr, 361 transactions.ReleaseENS, 362 transactions.AutoDelete, 363 "", 364 ) 365 if err != nil { 366 log.Error("TrackPendingTransaction error", "error", err) 367 return "", err 368 } 369 370 err = api.Remove(ctx, chainID, fullDomainName(username)) 371 372 if err != nil { 373 log.Warn("Releasing ENS username: transaction successful, but removing failed") 374 } 375 376 return tx.Hash().String(), nil 377 } 378 379 func (api *API) ReleasePrepareTxCallMsg(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string) (ethereum.CallMsg, error) { 380 registrarABI, err := abi.JSON(strings.NewReader(registrar.UsernameRegistrarABI)) 381 if err != nil { 382 return ethereum.CallMsg{}, err 383 } 384 385 data, err := registrarABI.Pack("release", UsernameToLabel(username)) 386 if err != nil { 387 return ethereum.CallMsg{}, err 388 } 389 390 sntAddress, err := snt.ContractAddress(chainID) 391 if err != nil { 392 return ethereum.CallMsg{}, err 393 } 394 return ethereum.CallMsg{ 395 From: common.Address(txArgs.From), 396 To: &sntAddress, 397 Value: big.NewInt(0), 398 Data: data, 399 }, nil 400 } 401 402 func (api *API) ReleasePrepareTx(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string) (interface{}, error) { 403 callMsg, err := api.ReleasePrepareTxCallMsg(ctx, chainID, txArgs, username) 404 if err != nil { 405 return nil, err 406 } 407 408 return toCallArg(callMsg), nil 409 } 410 411 func (api *API) ReleaseEstimate(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string) (uint64, error) { 412 registrarABI, err := abi.JSON(strings.NewReader(registrar.UsernameRegistrarABI)) 413 if err != nil { 414 return 0, err 415 } 416 417 data, err := registrarABI.Pack("release", UsernameToLabel(username)) 418 if err != nil { 419 return 0, err 420 } 421 422 ethClient, err := api.contractMaker.RPCClient.EthClient(chainID) 423 if err != nil { 424 return 0, err 425 } 426 427 registryAddr, err := api.usernameRegistrarAddr(ctx, chainID) 428 if err != nil { 429 return 0, err 430 } 431 432 estimate, err := ethClient.EstimateGas(ctx, ethereum.CallMsg{ 433 From: common.Address(txArgs.From), 434 To: ®istryAddr, 435 Value: big.NewInt(0), 436 Data: data, 437 }) 438 if err != nil { 439 return 0, err 440 } 441 return estimate + 1000, nil 442 } 443 444 func (api *API) Register(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, password string, username string, pubkey string) (string, error) { 445 snt, err := api.contractMaker.NewSNT(chainID) 446 if err != nil { 447 return "", err 448 } 449 450 priceHex, err := api.Price(ctx, chainID) 451 if err != nil { 452 return "", err 453 } 454 price := new(big.Int) 455 price.SetString(priceHex, 16) 456 457 registrarABI, err := abi.JSON(strings.NewReader(registrar.UsernameRegistrarABI)) 458 if err != nil { 459 return "", err 460 } 461 462 x, y := ExtractCoordinates(pubkey) 463 extraData, err := registrarABI.Pack("register", UsernameToLabel(username), common.Address(txArgs.From), x, y) 464 if err != nil { 465 return "", err 466 } 467 468 registryAddr, err := api.usernameRegistrarAddr(ctx, chainID) 469 if err != nil { 470 return "", err 471 } 472 473 txOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password)) 474 tx, err := snt.ApproveAndCall( 475 txOpts, 476 registryAddr, 477 price, 478 extraData, 479 ) 480 481 if err != nil { 482 return "", err 483 } 484 485 err = api.pendingTracker.TrackPendingTransaction( 486 wcommon.ChainID(chainID), 487 tx.Hash(), 488 common.Address(txArgs.From), 489 registryAddr, 490 transactions.RegisterENS, 491 transactions.AutoDelete, 492 "", 493 ) 494 if err != nil { 495 log.Error("TrackPendingTransaction error", "error", err) 496 return "", err 497 } 498 499 err = api.Add(ctx, chainID, fullDomainName(username)) 500 if err != nil { 501 log.Warn("Registering ENS username: transaction successful, but adding failed") 502 } 503 504 return tx.Hash().String(), nil 505 } 506 507 func (api *API) RegisterPrepareTxCallMsg(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string, pubkey string) (ethereum.CallMsg, error) { 508 priceHex, err := api.Price(ctx, chainID) 509 if err != nil { 510 return ethereum.CallMsg{}, err 511 } 512 price := new(big.Int) 513 price.SetString(priceHex, 16) 514 515 registrarABI, err := abi.JSON(strings.NewReader(registrar.UsernameRegistrarABI)) 516 if err != nil { 517 return ethereum.CallMsg{}, err 518 } 519 520 x, y := ExtractCoordinates(pubkey) 521 extraData, err := registrarABI.Pack("register", UsernameToLabel(username), common.Address(txArgs.From), x, y) 522 if err != nil { 523 return ethereum.CallMsg{}, err 524 } 525 526 sntABI, err := abi.JSON(strings.NewReader(snt.SNTABI)) 527 if err != nil { 528 return ethereum.CallMsg{}, err 529 } 530 531 registryAddr, err := api.usernameRegistrarAddr(ctx, chainID) 532 if err != nil { 533 return ethereum.CallMsg{}, err 534 } 535 536 data, err := sntABI.Pack("approveAndCall", registryAddr, price, extraData) 537 if err != nil { 538 return ethereum.CallMsg{}, err 539 } 540 541 sntAddress, err := snt.ContractAddress(chainID) 542 if err != nil { 543 return ethereum.CallMsg{}, err 544 } 545 return ethereum.CallMsg{ 546 From: common.Address(txArgs.From), 547 To: &sntAddress, 548 Value: big.NewInt(0), 549 Data: data, 550 }, nil 551 } 552 553 func (api *API) RegisterPrepareTx(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string, pubkey string) (interface{}, error) { 554 callMsg, err := api.RegisterPrepareTxCallMsg(ctx, chainID, txArgs, username, pubkey) 555 if err != nil { 556 return nil, err 557 } 558 559 return toCallArg(callMsg), nil 560 } 561 562 func (api *API) RegisterEstimate(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string, pubkey string) (uint64, error) { 563 ethClient, err := api.contractMaker.RPCClient.EthClient(chainID) 564 if err != nil { 565 return 0, err 566 } 567 568 callMsg, err := api.RegisterPrepareTxCallMsg(ctx, chainID, txArgs, username, pubkey) 569 if err != nil { 570 return 0, err 571 } 572 573 estimate, err := ethClient.EstimateGas(ctx, callMsg) 574 if err != nil { 575 return 0, err 576 } 577 return estimate + 1000, nil 578 } 579 580 func (api *API) SetPubKey(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, password string, username string, pubkey string) (string, error) { 581 err := ValidateENSUsername(username) 582 if err != nil { 583 return "", err 584 } 585 586 resolverAddress, err := api.Resolver(ctx, chainID, username) 587 if err != nil { 588 return "", err 589 } 590 591 resolver, err := api.contractMaker.NewPublicResolver(chainID, resolverAddress) 592 if err != nil { 593 return "", err 594 } 595 596 x, y := ExtractCoordinates(pubkey) 597 txOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password)) 598 tx, err := resolver.SetPubkey(txOpts, NameHash(username), x, y) 599 if err != nil { 600 return "", err 601 } 602 603 err = api.pendingTracker.TrackPendingTransaction( 604 wcommon.ChainID(chainID), 605 tx.Hash(), 606 common.Address(txArgs.From), 607 *resolverAddress, 608 transactions.SetPubKey, 609 transactions.AutoDelete, 610 "", 611 ) 612 if err != nil { 613 log.Error("TrackPendingTransaction error", "error", err) 614 return "", err 615 } 616 617 err = api.Add(ctx, chainID, fullDomainName(username)) 618 619 if err != nil { 620 log.Warn("Registering ENS username: transaction successful, but adding failed") 621 } 622 623 return tx.Hash().String(), nil 624 } 625 626 func (api *API) SetPubKeyPrepareTxCallMsg(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string, pubkey string) (ethereum.CallMsg, error) { 627 err := ValidateENSUsername(username) 628 if err != nil { 629 return ethereum.CallMsg{}, err 630 } 631 x, y := ExtractCoordinates(pubkey) 632 633 resolverABI, err := abi.JSON(strings.NewReader(resolver.PublicResolverABI)) 634 if err != nil { 635 return ethereum.CallMsg{}, err 636 } 637 638 data, err := resolverABI.Pack("setPubkey", NameHash(username), x, y) 639 if err != nil { 640 return ethereum.CallMsg{}, err 641 } 642 643 resolverAddress, err := api.Resolver(ctx, chainID, username) 644 if err != nil { 645 return ethereum.CallMsg{}, err 646 } 647 648 return ethereum.CallMsg{ 649 From: common.Address(txArgs.From), 650 To: resolverAddress, 651 Value: big.NewInt(0), 652 Data: data, 653 }, nil 654 } 655 656 func (api *API) SetPubKeyPrepareTx(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string, pubkey string) (interface{}, error) { 657 callMsg, err := api.SetPubKeyPrepareTxCallMsg(ctx, chainID, txArgs, username, pubkey) 658 if err != nil { 659 return nil, err 660 } 661 662 return toCallArg(callMsg), nil 663 } 664 665 func (api *API) SetPubKeyEstimate(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, username string, pubkey string) (uint64, error) { 666 ethClient, err := api.contractMaker.RPCClient.EthClient(chainID) 667 if err != nil { 668 return 0, err 669 } 670 671 callMsg, err := api.SetPubKeyPrepareTxCallMsg(ctx, chainID, txArgs, username, pubkey) 672 if err != nil { 673 return 0, err 674 } 675 676 estimate, err := ethClient.EstimateGas(ctx, callMsg) 677 if err != nil { 678 return 0, err 679 } 680 return estimate + 1000, nil 681 } 682 683 func (api *API) ResourceURL(ctx context.Context, chainID uint64, username string) (*URI, error) { 684 scheme := "https" 685 contentHash, err := api.ContentHash(ctx, chainID, username) 686 if err != nil { 687 return nil, err 688 } 689 690 if len(contentHash) == 0 { 691 return &URI{}, nil 692 } 693 694 data, codec, err := multicodec.RemoveCodec(contentHash) 695 if err != nil { 696 return nil, err 697 } 698 codecName, err := multicodec.Name(codec) 699 if err != nil { 700 return nil, err 701 } 702 703 switch codecName { 704 case "ipfs-ns": 705 thisCID, err := cid.Parse(data) 706 if err != nil { 707 return nil, errors.Wrap(err, "failed to parse CID") 708 } 709 str, err := thisCID.StringOfBase(multibase.Base32) 710 if err != nil { 711 return nil, errors.Wrap(err, "failed to obtain base36 representation") 712 } 713 714 parsedURL, _ := url.Parse(params.IpfsGatewayURL) 715 // Remove scheme from the url 716 host := parsedURL.Hostname() + parsedURL.Path + str 717 return &URI{scheme, host, ""}, nil 718 case "ipns-ns": 719 id, offset := binary.Uvarint(data) 720 if id == 0 { 721 return nil, fmt.Errorf("unknown CID") 722 } 723 724 data, _, err := multicodec.RemoveCodec(data[offset:]) 725 if err != nil { 726 return nil, err 727 } 728 decodedMHash, err := multihash.Decode(data) 729 if err != nil { 730 return nil, err 731 } 732 733 return &URI{scheme, string(decodedMHash.Digest), ""}, nil 734 case "swarm-ns": 735 id, offset := binary.Uvarint(data) 736 if id == 0 { 737 return nil, fmt.Errorf("unknown CID") 738 } 739 data, _, err := multicodec.RemoveCodec(data[offset:]) 740 if err != nil { 741 return nil, err 742 } 743 decodedMHash, err := multihash.Decode(data) 744 if err != nil { 745 return nil, err 746 } 747 path := "/bzz:/" + hex.EncodeToString(decodedMHash.Digest) + "/" 748 return &URI{scheme, "swarm-gateways.net", path}, nil 749 default: 750 return nil, fmt.Errorf("unknown codec name %s", codecName) 751 } 752 } 753 754 func toCallArg(msg ethereum.CallMsg) interface{} { 755 arg := map[string]interface{}{ 756 "from": msg.From, 757 "to": msg.To, 758 } 759 if len(msg.Data) > 0 { 760 arg["data"] = hexutil.Bytes(msg.Data) 761 } 762 if msg.Value != nil { 763 arg["value"] = (*hexutil.Big)(msg.Value) 764 } 765 if msg.Gas != 0 { 766 arg["gas"] = hexutil.Uint64(msg.Gas) 767 } 768 if msg.GasPrice != nil { 769 arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) 770 } 771 return arg 772 } 773 774 func fullDomainName(username string) string { 775 return username + "." + StatusDomain 776 }