├── .gitignore ├── LICENSE ├── README.md ├── btc ├── msgtx.go ├── rpcclient.go ├── transact.go └── util.go ├── eth ├── contract.go ├── erc20.go ├── erc20_meta.go ├── erc721.go ├── ethlog.go ├── rpcclient.go ├── transact.go └── util.go ├── example └── exam_wallet.go ├── go.mod ├── go.sum ├── qa ├── btc │ ├── run_bitcoind.go │ └── transact_test.go ├── check_running.go ├── eth │ ├── run_ganache.go │ └── transact_test.go ├── matic │ └── transact_test.go └── trx │ ├── transact_test.go │ └── trc20.sol ├── trx ├── account.go ├── contract.go ├── rpcclient.go ├── transact.go ├── transfer.go ├── trc20.go └── util.go └── wallet ├── common.go ├── ethchain.go ├── hdwallet.go ├── wallet.go ├── wallet_btc.go ├── wallet_eth.go ├── wallet_test.go └── wallet_trx.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/.idea 3 | **/.vscode 4 | __pycache__/ 5 | /pkg 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hdwallet 2 | 3 | hdwallet is a crypto hierarchical-deterministic wallet, implemented using golang. Implemented private key management, transaction signature, and tools related to eth and btc. 4 | 5 | ## Getting started 6 | 7 | Please refer to the test case in the qa directory 8 | 9 | ## Keywords 10 | 11 | crypto wallet 12 | transaction sign 13 | btc segwit address 14 | rpc client 15 | P2WPKH 16 | P2WPKH in P2SH 17 | eth erc20 18 | eth erc721 19 | 20 | ## JetBrains OS licenses 21 | hdwallet has been being developed with GoLand under the free JetBrains Open Source licenses granted by JetBrains. I would like to express my thanks here. 22 | 23 | 24 | -------------------------------------------------------------------------------- /btc/msgtx.go: -------------------------------------------------------------------------------- 1 | package btc 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/btcsuite/btcd/blockchain" 6 | "github.com/btcsuite/btcd/btcjson" 7 | "github.com/btcsuite/btcd/btcutil" 8 | "github.com/btcsuite/btcd/chaincfg" 9 | "github.com/btcsuite/btcd/txscript" 10 | "github.com/btcsuite/btcd/wire" 11 | ) 12 | 13 | func DecodeMsgTx(mtx *wire.MsgTx, chainParams *chaincfg.Params) *btcjson.TxRawDecodeResult { 14 | return &btcjson.TxRawDecodeResult{ 15 | Txid: mtx.TxHash().String(), 16 | Version: mtx.Version, 17 | Locktime: mtx.LockTime, 18 | Vin: createVinList(mtx), 19 | Vout: createVoutList(mtx, chainParams, nil), 20 | } 21 | } 22 | 23 | func createVinList(mtx *wire.MsgTx) []btcjson.Vin { 24 | // Coinbase transactions only have a single txin by definition. 25 | vinList := make([]btcjson.Vin, len(mtx.TxIn)) 26 | if blockchain.IsCoinBaseTx(mtx) { 27 | txIn := mtx.TxIn[0] 28 | vinList[0].Coinbase = hex.EncodeToString(txIn.SignatureScript) 29 | vinList[0].Sequence = txIn.Sequence 30 | vinList[0].Witness = witnessToHex(txIn.Witness) 31 | return vinList 32 | } 33 | 34 | for i, txIn := range mtx.TxIn { 35 | // The disassembled string will contain [error] inline 36 | // if the script doesn't fully parse, so ignore the 37 | // error here. 38 | disbuf, _ := txscript.DisasmString(txIn.SignatureScript) 39 | 40 | vinEntry := &vinList[i] 41 | vinEntry.Txid = txIn.PreviousOutPoint.Hash.String() 42 | vinEntry.Vout = txIn.PreviousOutPoint.Index 43 | vinEntry.Sequence = txIn.Sequence 44 | vinEntry.ScriptSig = &btcjson.ScriptSig{ 45 | Asm: disbuf, 46 | Hex: hex.EncodeToString(txIn.SignatureScript), 47 | } 48 | 49 | if mtx.HasWitness() { 50 | vinEntry.Witness = witnessToHex(txIn.Witness) 51 | } 52 | } 53 | 54 | return vinList 55 | } 56 | 57 | func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params, filterAddrMap map[string]struct{}) []btcjson.Vout { 58 | voutList := make([]btcjson.Vout, 0, len(mtx.TxOut)) 59 | for i, v := range mtx.TxOut { 60 | // The disassembled string will contain [error] inline if the 61 | // script doesn't fully parse, so ignore the error here. 62 | disbuf, _ := txscript.DisasmString(v.PkScript) 63 | 64 | // Ignore the error here since an error means the script 65 | // couldn't parse and there is no additional information about 66 | // it anyways. 67 | scriptClass, addrs, reqSigs, _ := txscript.ExtractPkScriptAddrs( 68 | v.PkScript, chainParams) 69 | 70 | // Encode the addresses while checking if the address passes the 71 | // filter when needed. 72 | passesFilter := len(filterAddrMap) == 0 73 | encodedAddrs := make([]string, len(addrs)) 74 | for j, addr := range addrs { 75 | encodedAddr := addr.EncodeAddress() 76 | encodedAddrs[j] = encodedAddr 77 | 78 | // No need to check the map again if the filter already 79 | // passes. 80 | if passesFilter { 81 | continue 82 | } 83 | if _, exists := filterAddrMap[encodedAddr]; exists { 84 | passesFilter = true 85 | } 86 | } 87 | 88 | if !passesFilter { 89 | continue 90 | } 91 | 92 | var vout btcjson.Vout 93 | vout.N = uint32(i) 94 | vout.Value = btcutil.Amount(v.Value).ToBTC() 95 | vout.ScriptPubKey.Addresses = encodedAddrs 96 | vout.ScriptPubKey.Asm = disbuf 97 | vout.ScriptPubKey.Hex = hex.EncodeToString(v.PkScript) 98 | vout.ScriptPubKey.Type = scriptClass.String() 99 | vout.ScriptPubKey.ReqSigs = int32(reqSigs) 100 | 101 | voutList = append(voutList, vout) 102 | } 103 | 104 | return voutList 105 | } 106 | 107 | func witnessToHex(witness wire.TxWitness) []string { 108 | // Ensure nil is returned when there are no entries versus an empty 109 | // slice so it can properly be omitted as necessary. 110 | if len(witness) == 0 { 111 | return nil 112 | } 113 | 114 | result := make([]string, 0, len(witness)) 115 | for _, wit := range witness { 116 | result = append(result, hex.EncodeToString(wit)) 117 | } 118 | 119 | return result 120 | } 121 | -------------------------------------------------------------------------------- /btc/rpcclient.go: -------------------------------------------------------------------------------- 1 | package btc 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "github.com/btcsuite/btcd/rpcclient" 7 | "github.com/lizc2003/hdwallet/wallet" 8 | "net/url" 9 | ) 10 | 11 | type BtcClient struct { 12 | RpcClient *rpcclient.Client 13 | } 14 | 15 | func NewBtcClient(URL string, user string, pass string, chainId int) (*BtcClient, error) { 16 | chainParams, err := wallet.GetBtcChainParams(chainId) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | connCfg := &rpcclient.ConnConfig{ 22 | User: user, 23 | Pass: pass, 24 | Params: chainParams.Name, 25 | } 26 | 27 | u, err := url.Parse(URL) 28 | if err != nil { 29 | return nil, err 30 | } 31 | if u.Scheme == "http" || u.Scheme == "https" { 32 | connCfg.HTTPPostMode = true 33 | connCfg.Host = u.Host + u.Path 34 | if u.Scheme == "http" { 35 | connCfg.DisableTLS = true 36 | } 37 | } else if u.Scheme == "ws" || u.Scheme == "wss" { 38 | connCfg.Host = u.Host 39 | if u.Path != "" { 40 | connCfg.Endpoint = u.Path[1:] 41 | } 42 | if u.Scheme == "ws" { 43 | connCfg.DisableTLS = true 44 | } 45 | } 46 | 47 | // Notice the notification parameter is nil since notifications are 48 | // not supported in HTTP POST mode. 49 | client, err := rpcclient.New(connCfg, nil) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return &BtcClient{RpcClient: client}, nil 55 | } 56 | 57 | func (this *BtcClient) EstimateFeePerKb() (int64, error) { 58 | feeResult, err := this.RpcClient.EstimateSmartFee(6, nil) 59 | if err != nil { 60 | return 0, err 61 | } 62 | 63 | if feeResult.FeeRate != nil && *feeResult.FeeRate > 0 { 64 | return BtcToSatoshi(*feeResult.FeeRate), nil 65 | } else { 66 | return 0, errors.New("Fee not available") 67 | } 68 | } 69 | 70 | // https://bitcoincore.org/en/doc/0.21.0/rpc/rawtransactions/sendrawtransaction/ 71 | func (this *BtcClient) SendRawTransaction(signedHex string, allowHighFees bool) (string, error) { 72 | hex, _ := json.Marshal(signedHex) 73 | params := []json.RawMessage{hex} 74 | if allowHighFees { 75 | maxFeeRate, _ := json.Marshal(0) 76 | params = append(params, maxFeeRate) 77 | } 78 | 79 | resp, err := this.RpcClient.RawRequest("sendrawtransaction", params) 80 | if err != nil { 81 | return "", err 82 | } 83 | 84 | var txid string 85 | err = json.Unmarshal(resp, &txid) 86 | if err == nil && txid == "" { 87 | err = errors.New("unknown response") 88 | } 89 | if err != nil { 90 | return "", err 91 | } 92 | return txid, nil 93 | } 94 | -------------------------------------------------------------------------------- /btc/transact.go: -------------------------------------------------------------------------------- 1 | package btc 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "errors" 7 | "fmt" 8 | "github.com/btcsuite/btcd/btcjson" 9 | "github.com/btcsuite/btcd/btcutil" 10 | "github.com/btcsuite/btcd/chaincfg" 11 | "github.com/btcsuite/btcd/chaincfg/chainhash" 12 | "github.com/btcsuite/btcd/rpcclient" 13 | "github.com/btcsuite/btcd/txscript" 14 | "github.com/btcsuite/btcd/wire" 15 | "github.com/btcsuite/btcwallet/wallet/txauthor" 16 | "github.com/lizc2003/hdwallet/wallet" 17 | ) 18 | 19 | type BtcUnspent struct { 20 | TxID string `json:"txid"` 21 | Vout uint32 `json:"vout"` 22 | ScriptPubKey string `json:"scriptPubKey"` 23 | RedeemScript string `json:"redeemScript,omitempty"` 24 | Amount float64 `json:"amount"` 25 | } 26 | 27 | type BtcOutput struct { 28 | Address btcutil.Address `json:"address"` 29 | Amount int64 `json:"amount"` 30 | } 31 | 32 | type BtcTransaction struct { 33 | txauthor.AuthoredTx 34 | chainParams *chaincfg.Params 35 | feePerKb int64 36 | } 37 | 38 | func NewBtcTransaction(unspents []BtcUnspent, outputs []BtcOutput, 39 | changeAddress btcutil.Address, feePerKb int64, chainCfg *chaincfg.Params) (*BtcTransaction, error) { 40 | 41 | if len(unspents) == 0 || changeAddress == nil || feePerKb <= 0 { 42 | return nil, errors.New("wrong params") 43 | } 44 | if !changeAddress.IsForNet(chainCfg) { 45 | return nil, errors.New("change address is not the corresponding network address") 46 | } 47 | changeBytes, err := txscript.PayToAddrScript(changeAddress) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | feeRatePerKb := btcutil.Amount(feePerKb) 53 | 54 | txOuts, err := makeTxOutputs(outputs, feeRatePerKb, chainCfg) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | changeSource := txauthor.ChangeSource{ 60 | NewScript: func() ([]byte, error) { 61 | return changeBytes, nil 62 | }, 63 | ScriptSize: len(changeBytes), 64 | } 65 | 66 | unsignedTx, err := txauthor.NewUnsignedTransaction(txOuts, feeRatePerKb, makeInputSource(unspents), &changeSource) 67 | if err != nil { 68 | return nil, err 69 | } 70 | // Randomize change position, if change exists, before signing. This 71 | // doesn't affect the serialize size, so the change amount will still 72 | // be valid. 73 | if unsignedTx.ChangeIndex >= 0 { 74 | unsignedTx.RandomizeChangePosition() 75 | } 76 | 77 | return &BtcTransaction{*unsignedTx, chainCfg, feePerKb}, nil 78 | } 79 | 80 | func (t *BtcTransaction) Sign(wallet *wallet.BtcWallet) error { 81 | return t.SignWithSecretsSource(wallet) 82 | } 83 | 84 | func (t *BtcTransaction) SignWithSecretsSource(secretsSource txauthor.SecretsSource) error { 85 | err := t.AddAllInputScripts(secretsSource) 86 | if err != nil { 87 | return err 88 | } 89 | err = validateMsgTx(t.Tx, t.PrevScripts, t.PrevInputValues) 90 | if err != nil { 91 | return err 92 | } 93 | 94 | return nil 95 | } 96 | 97 | func (t *BtcTransaction) GetFee() int64 { 98 | fee := t.TotalInput - txauthor.SumOutputValues(t.Tx.TxOut) 99 | return int64(fee) 100 | } 101 | 102 | func (t *BtcTransaction) GetFeePerKb() int64 { 103 | return t.feePerKb 104 | } 105 | 106 | func (t *BtcTransaction) HasChange() bool { 107 | return t.ChangeIndex >= 0 108 | } 109 | 110 | func (t *BtcTransaction) Serialize() (string, error) { 111 | // Serialize the transaction and convert to hex string. 112 | buf := bytes.NewBuffer(make([]byte, 0, t.Tx.SerializeSize())) 113 | if err := t.Tx.Serialize(buf); err != nil { 114 | return "", err 115 | } 116 | return hex.EncodeToString(buf.Bytes()), nil 117 | } 118 | 119 | func (t *BtcTransaction) GetTxid() string { 120 | return t.Tx.TxHash().String() 121 | } 122 | 123 | func (t *BtcTransaction) Decode() *btcjson.TxRawDecodeResult { 124 | return DecodeMsgTx(t.Tx, t.chainParams) 125 | } 126 | 127 | func (t *BtcTransaction) Send(client *rpcclient.Client, allowHighFees bool) (*chainhash.Hash, error) { 128 | hash, err := client.SendRawTransaction(t.Tx, allowHighFees) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return hash, nil 133 | } 134 | 135 | func makeTxOutputs(outputs []BtcOutput, relayFeePerKb btcutil.Amount, chainCfg *chaincfg.Params) ([]*wire.TxOut, error) { 136 | outLen := len(outputs) 137 | if outLen == 0 { 138 | return nil, errors.New("tx output is empty") 139 | } 140 | 141 | txOuts := make([]*wire.TxOut, 0, outLen) 142 | for i := 0; i < outLen; i++ { 143 | out := &outputs[i] 144 | 145 | if !out.Address.IsForNet(chainCfg) { 146 | return nil, errors.New("out address is not the corresponding network address") 147 | } 148 | 149 | // Create a new script which pays to the provided address. 150 | pkScript, err := txscript.PayToAddrScript(out.Address) 151 | if err != nil { 152 | return nil, err 153 | } 154 | txOut := &wire.TxOut{ 155 | Value: out.Amount, 156 | PkScript: pkScript, 157 | } 158 | 159 | //if err = txrules.CheckOutput(txOut, relayFeePerKb); err != nil { 160 | // return nil, err 161 | //} 162 | 163 | txOuts = append(txOuts, txOut) 164 | } 165 | return txOuts, nil 166 | } 167 | 168 | func makeInputSource(unspents []BtcUnspent) txauthor.InputSource { 169 | sz := len(unspents) 170 | // Current inputs and their total value. These are closed over by the 171 | // returned input source and reused across multiple calls. 172 | currentTotal := btcutil.Amount(0) 173 | currentInputs := make([]*wire.TxIn, 0, sz) 174 | currentInputValues := make([]btcutil.Amount, 0, sz) 175 | currentScripts := make([][]byte, 0, sz) 176 | 177 | return func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn, []btcutil.Amount, [][]byte, error) { 178 | for currentTotal < target && len(unspents) != 0 { 179 | u := unspents[0] 180 | unspents = unspents[1:] 181 | 182 | hash, _ := chainhash.NewHashFromStr(u.TxID) 183 | nextInput := wire.NewTxIn(&wire.OutPoint{ 184 | Hash: *hash, 185 | Index: u.Vout, 186 | }, nil, nil) 187 | 188 | amount, _ := btcutil.NewAmount(u.Amount) 189 | s, _ := hex.DecodeString(u.ScriptPubKey) 190 | 191 | currentTotal += amount 192 | currentInputs = append(currentInputs, nextInput) 193 | currentInputValues = append(currentInputValues, amount) 194 | currentScripts = append(currentScripts, s) 195 | } 196 | return currentTotal, currentInputs, currentInputValues, currentScripts, nil 197 | } 198 | } 199 | 200 | // validateMsgTx verifies transaction input scripts for tx. All previous output 201 | // scripts from outputs redeemed by the transaction, in the same order they are 202 | // spent, must be passed in the prevScripts slice. 203 | func validateMsgTx(tx *wire.MsgTx, prevScripts [][]byte, 204 | inputValues []btcutil.Amount) error { 205 | 206 | inputFetcher, err := txauthor.TXPrevOutFetcher( 207 | tx, prevScripts, inputValues, 208 | ) 209 | if err != nil { 210 | return err 211 | } 212 | 213 | hashCache := txscript.NewTxSigHashes(tx, inputFetcher) 214 | for i, prevScript := range prevScripts { 215 | vm, err := txscript.NewEngine( 216 | prevScript, tx, i, txscript.StandardVerifyFlags, nil, 217 | hashCache, int64(inputValues[i]), inputFetcher, 218 | ) 219 | if err != nil { 220 | return fmt.Errorf("cannot create script engine: %s", err) 221 | } 222 | err = vm.Execute() 223 | if err != nil { 224 | return fmt.Errorf("cannot validate transaction: %s", err) 225 | } 226 | } 227 | return nil 228 | } 229 | -------------------------------------------------------------------------------- /btc/util.go: -------------------------------------------------------------------------------- 1 | package btc 2 | 3 | import ( 4 | "github.com/btcsuite/btcd/btcutil" 5 | "github.com/btcsuite/btcd/chaincfg" 6 | "github.com/btcsuite/btcd/chaincfg/chainhash" 7 | "github.com/btcsuite/btcwallet/wallet/txauthor" 8 | "github.com/btcsuite/btcwallet/wallet/txrules" 9 | "github.com/btcsuite/btcwallet/wallet/txsizes" 10 | ) 11 | 12 | func DecodeAddress(addr string, chainParams *chaincfg.Params) (btcutil.Address, error) { 13 | return btcutil.DecodeAddress(addr, chainParams) 14 | } 15 | 16 | func HexToHash(s string) (*chainhash.Hash, error) { 17 | return chainhash.NewHashFromStr(s) 18 | } 19 | 20 | func BtcToSatoshi(v float64) int64 { 21 | amt, _ := btcutil.NewAmount(v) 22 | return int64(amt) 23 | } 24 | 25 | func SatoshiToBtc(v int64) float64 { 26 | a := btcutil.Amount(v) 27 | return a.ToBTC() 28 | } 29 | 30 | func EstimateFee(numP2PKHIns, numP2WPKHIns, numNestedP2WPKHIns int, 31 | outputs []BtcOutput, feePerKb int64, changeScriptSize int, chainCfg *chaincfg.Params) (int64, int64, error) { 32 | 33 | feeRatePerKb := btcutil.Amount(feePerKb) 34 | if changeScriptSize < 0 { 35 | // using P2WPKH as change output. 36 | changeScriptSize = txsizes.P2WPKHPkScriptSize 37 | } 38 | 39 | txOuts, err := makeTxOutputs(outputs, feeRatePerKb, chainCfg) 40 | if err != nil { 41 | return 0, 0, err 42 | } 43 | 44 | maxSignedSize := txsizes.EstimateVirtualSize(numP2PKHIns, 0, numP2WPKHIns, 45 | numNestedP2WPKHIns, txOuts, changeScriptSize) 46 | 47 | targetFee := txrules.FeeForSerializeSize(feeRatePerKb, maxSignedSize) 48 | targetAmount := txauthor.SumOutputValues(txOuts) 49 | 50 | return int64(targetFee), int64(targetAmount), nil 51 | } 52 | -------------------------------------------------------------------------------- /eth/contract.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/ethereum/go-ethereum" 7 | "github.com/ethereum/go-ethereum/accounts/abi" 8 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/core/types" 11 | "math/big" 12 | "strings" 13 | ) 14 | 15 | func DeployContract(opts *bind.TransactOpts, backend bind.ContractBackend, jsonAbi string, bytecode string, params ...interface{}) (common.Address, *types.Transaction, error) { 16 | parsed, err := abi.JSON(strings.NewReader(jsonAbi)) 17 | if err != nil { 18 | return common.Address{}, nil, err 19 | } 20 | address, tx, _, err := bind.DeployContract(opts, parsed, common.FromHex(bytecode), backend, params...) 21 | if err != nil { 22 | return common.Address{}, nil, err 23 | } 24 | return address, tx, nil 25 | } 26 | 27 | func EstimateContractMethodGas(param TransactBaseParam, backend bind.ContractBackend, contractAddress common.Address, input []byte) (int64, error) { 28 | err := param.EnsureGasPrice(backend) 29 | if err != nil { 30 | return 0, err 31 | } 32 | 33 | ethValue := param.EthValue 34 | if ethValue == nil { 35 | ethValue = big.NewInt(0) 36 | } 37 | msg := ethereum.CallMsg{From: param.From, To: &contractAddress, 38 | GasPrice: param.GasPrice, 39 | GasFeeCap: param.GasFeeCap, 40 | GasTipCap: param.GasTipCap, 41 | Value: ethValue, Data: input} 42 | 43 | gasLimit, err := backend.EstimateGas(context.Background(), msg) 44 | if err != nil { 45 | return 0, fmt.Errorf("failed to estimate gas needed: %v", err) 46 | } 47 | return int64(gasLimit), nil 48 | } 49 | -------------------------------------------------------------------------------- /eth/erc20.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/accounts/abi" 5 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/core/types" 8 | "math/big" 9 | "strings" 10 | ) 11 | 12 | const ERC20InterfaceABI = `[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]` 13 | 14 | // Erc20Contract tool for contract abi 15 | type Erc20Contract struct { 16 | abi abi.ABI 17 | contractAddress common.Address 18 | backend bind.ContractBackend 19 | contract *bind.BoundContract 20 | opts *bind.CallOpts 21 | } 22 | 23 | func NewErc20Contract(address common.Address, backend bind.ContractBackend) *Erc20Contract { 24 | parsed, _ := abi.JSON(strings.NewReader(ERC20InterfaceABI)) 25 | c := bind.NewBoundContract(address, parsed, backend, backend, backend) 26 | return &Erc20Contract{abi: parsed, contractAddress: address, backend: backend, contract: c, opts: &bind.CallOpts{}} 27 | } 28 | 29 | func (this *Erc20Contract) TotalSupply() (*big.Int, error) { 30 | var ( 31 | ret0 = new(*big.Int) 32 | ) 33 | out := []interface{}{ret0} 34 | err := this.contract.Call(this.opts, &out, "totalSupply") 35 | if err != nil { 36 | return nil, err 37 | } 38 | return *ret0, err 39 | } 40 | 41 | func (this *Erc20Contract) Name() (string, error) { 42 | var ( 43 | ret0 = new(string) 44 | ) 45 | out := []interface{}{ret0} 46 | err := this.contract.Call(this.opts, &out, "name") 47 | if err != nil { 48 | return "", err 49 | } 50 | return *ret0, err 51 | } 52 | 53 | func (this *Erc20Contract) Symbol() (string, error) { 54 | var ( 55 | ret0 = new(string) 56 | ) 57 | out := []interface{}{ret0} 58 | err := this.contract.Call(this.opts, &out, "symbol") 59 | if err != nil { 60 | return "", err 61 | } 62 | return *ret0, err 63 | } 64 | 65 | func (this *Erc20Contract) Decimals() (int, error) { 66 | var ( 67 | ret0 = new(uint8) 68 | ) 69 | out := []interface{}{ret0} 70 | err := this.contract.Call(this.opts, &out, "decimals") 71 | if err != nil { 72 | return 0, err 73 | } 74 | return int(*ret0), err 75 | } 76 | 77 | func (this *Erc20Contract) BalanceOf(tokenOwner common.Address) (*big.Int, error) { 78 | var ( 79 | ret0 = new(*big.Int) 80 | ) 81 | out := []interface{}{ret0} 82 | err := this.contract.Call(this.opts, &out, "balanceOf", tokenOwner) 83 | if err != nil { 84 | return nil, err 85 | } 86 | return *ret0, err 87 | } 88 | 89 | func (this *Erc20Contract) Allowance(tokenOwner common.Address, spender common.Address) (*big.Int, error) { 90 | var ( 91 | ret0 = new(*big.Int) 92 | ) 93 | out := []interface{}{ret0} 94 | err := this.contract.Call(this.opts, &out, "allowance", tokenOwner, spender) 95 | if err != nil { 96 | return nil, err 97 | } 98 | return *ret0, err 99 | } 100 | 101 | func (this *Erc20Contract) Transfer(opts *bind.TransactOpts, to common.Address, tokens *big.Int) (*types.Transaction, error) { 102 | return this.contract.Transact(opts, "transfer", to, tokens) 103 | } 104 | 105 | func (this *Erc20Contract) Approve(opts *bind.TransactOpts, spender common.Address, tokens *big.Int) (*types.Transaction, error) { 106 | return this.contract.Transact(opts, "approve", spender, tokens) 107 | } 108 | 109 | func (this *Erc20Contract) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, tokens *big.Int) (*types.Transaction, error) { 110 | return this.contract.Transact(opts, "transferFrom", from, to, tokens) 111 | } 112 | 113 | func (this *Erc20Contract) EstimateTransferGas(param TransactBaseParam, to common.Address, tokens *big.Int) (int64, error) { 114 | input, err := this.abi.Pack("transfer", to, tokens) 115 | if err != nil { 116 | return 0, err 117 | } 118 | return EstimateContractMethodGas(param, this.backend, this.contractAddress, input) 119 | } 120 | 121 | func (this *Erc20Contract) EstimateApproveGas(param TransactBaseParam, spender common.Address, tokens *big.Int) (int64, error) { 122 | input, err := this.abi.Pack("approve", spender, tokens) 123 | if err != nil { 124 | return 0, err 125 | } 126 | return EstimateContractMethodGas(param, this.backend, this.contractAddress, input) 127 | } 128 | 129 | func (this *Erc20Contract) EstimateTransferFromGas(param TransactBaseParam, from common.Address, to common.Address, tokens *big.Int) (int64, error) { 130 | input, err := this.abi.Pack("transferFrom", from, to, tokens) 131 | if err != nil { 132 | return 0, err 133 | } 134 | return EstimateContractMethodGas(param, this.backend, this.contractAddress, input) 135 | } 136 | -------------------------------------------------------------------------------- /eth/erc20_meta.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | type Erc20Meta struct { 4 | Symbol string `toml:"symbol" json:"symbol"` 5 | Address string `toml:"address" json:"address"` 6 | Decimals int `toml:"decimals" json:"decimals"` 7 | } 8 | -------------------------------------------------------------------------------- /eth/erc721.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/accounts/abi" 5 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/core/types" 8 | "math/big" 9 | "strings" 10 | ) 11 | 12 | const ERC721InterfaceABI = `[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"operator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]` 13 | 14 | // Erc721Contract tool for contract abi 15 | type Erc721Contract struct { 16 | abi abi.ABI 17 | contractAddress common.Address 18 | backend bind.ContractBackend 19 | contract *bind.BoundContract 20 | opts *bind.CallOpts 21 | } 22 | 23 | func NewErc721Contract(address common.Address, backend bind.ContractBackend) *Erc721Contract { 24 | parsed, _ := abi.JSON(strings.NewReader(ERC721InterfaceABI)) 25 | c := bind.NewBoundContract(address, parsed, backend, backend, backend) 26 | return &Erc721Contract{abi: parsed, contractAddress: address, backend: backend, contract: c, opts: &bind.CallOpts{}} 27 | } 28 | 29 | // IERC721Enumerable 30 | func (this *Erc721Contract) TotalSupply() (*big.Int, error) { 31 | var ( 32 | ret0 = new(*big.Int) 33 | ) 34 | out := []interface{}{ret0} 35 | err := this.contract.Call(this.opts, &out, "totalSupply") 36 | if err != nil { 37 | return nil, err 38 | } 39 | return *ret0, err 40 | } 41 | 42 | func (this *Erc721Contract) TokenOfOwnerByIndex(owner common.Address, index *big.Int) (*big.Int, error) { 43 | var ( 44 | ret0 = new(*big.Int) 45 | ) 46 | out := []interface{}{ret0} 47 | err := this.contract.Call(this.opts, &out, "tokenOfOwnerByIndex", owner, index) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return *ret0, err 52 | } 53 | 54 | func (this *Erc721Contract) TokenByIndex(index *big.Int) (*big.Int, error) { 55 | var ( 56 | ret0 = new(*big.Int) 57 | ) 58 | out := []interface{}{ret0} 59 | err := this.contract.Call(this.opts, &out, "tokenByIndex", index) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return *ret0, err 64 | } 65 | 66 | // IERC721Metadata 67 | func (this *Erc721Contract) Name() (string, error) { 68 | var ( 69 | ret0 = new(string) 70 | ) 71 | out := []interface{}{ret0} 72 | err := this.contract.Call(this.opts, &out, "name") 73 | if err != nil { 74 | return "", err 75 | } 76 | return *ret0, err 77 | } 78 | 79 | func (this *Erc721Contract) Symbol() (string, error) { 80 | var ( 81 | ret0 = new(string) 82 | ) 83 | out := []interface{}{ret0} 84 | err := this.contract.Call(this.opts, &out, "symbol") 85 | if err != nil { 86 | return "", err 87 | } 88 | return *ret0, err 89 | } 90 | 91 | func (this *Erc721Contract) TokenURI(tokenId *big.Int) (string, error) { 92 | var ( 93 | ret0 = new(string) 94 | ) 95 | out := []interface{}{ret0} 96 | err := this.contract.Call(this.opts, &out, "tokenURI", tokenId) 97 | if err != nil { 98 | return "", err 99 | } 100 | return *ret0, err 101 | } 102 | 103 | // IERC165 104 | func (this *Erc721Contract) SupportsInterface(interfaceId [4]byte) (bool, error) { 105 | var ( 106 | ret0 = new(bool) 107 | ) 108 | out := []interface{}{ret0} 109 | err := this.contract.Call(this.opts, &out, "supportsInterface", interfaceId) 110 | if err != nil { 111 | return false, err 112 | } 113 | return *ret0, err 114 | } 115 | 116 | // IERC721 117 | func (this *Erc721Contract) BalanceOf(owner common.Address) (*big.Int, error) { 118 | var ( 119 | ret0 = new(*big.Int) 120 | ) 121 | out := []interface{}{ret0} 122 | err := this.contract.Call(this.opts, &out, "balanceOf", owner) 123 | if err != nil { 124 | return nil, err 125 | } 126 | return *ret0, err 127 | } 128 | 129 | func (this *Erc721Contract) OwnerOf(tokenId *big.Int) (common.Address, error) { 130 | var ( 131 | ret0 = new(common.Address) 132 | ) 133 | out := []interface{}{ret0} 134 | err := this.contract.Call(this.opts, &out, "ownerOf", tokenId) 135 | if err != nil { 136 | return *ret0, err 137 | } 138 | return *ret0, err 139 | } 140 | 141 | func (this *Erc721Contract) GetApproved(tokenId *big.Int) (common.Address, error) { 142 | var ( 143 | ret0 = new(common.Address) 144 | ) 145 | out := []interface{}{ret0} 146 | err := this.contract.Call(this.opts, &out, "getApproved", tokenId) 147 | if err != nil { 148 | return *ret0, err 149 | } 150 | return *ret0, err 151 | } 152 | 153 | func (this *Erc721Contract) IsApprovedForAll(owner common.Address, operator common.Address) (bool, error) { 154 | var ( 155 | ret0 = new(bool) 156 | ) 157 | out := []interface{}{ret0} 158 | err := this.contract.Call(this.opts, &out, "isApprovedForAll", owner, operator) 159 | if err != nil { 160 | return false, err 161 | } 162 | return *ret0, err 163 | } 164 | 165 | func (this *Erc721Contract) SafeTransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, tokenId *big.Int) (*types.Transaction, error) { 166 | return this.contract.Transact(opts, "safeTransferFrom", from, to, tokenId) 167 | } 168 | 169 | func (this *Erc721Contract) SafeTransferFrom2(opts *bind.TransactOpts, from common.Address, to common.Address, tokenId *big.Int, data []byte) (*types.Transaction, error) { 170 | return this.contract.Transact(opts, "safeTransferFrom", from, to, tokenId, data) 171 | } 172 | 173 | func (this *Erc721Contract) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, tokenId *big.Int) (*types.Transaction, error) { 174 | return this.contract.Transact(opts, "transferFrom", from, to, tokenId) 175 | } 176 | 177 | func (this *Erc721Contract) Approve(opts *bind.TransactOpts, to common.Address, tokenId *big.Int) (*types.Transaction, error) { 178 | return this.contract.Transact(opts, "approve", to, tokenId) 179 | } 180 | 181 | func (this *Erc721Contract) SetApprovalForAll(opts *bind.TransactOpts, operator common.Address, approved bool) (*types.Transaction, error) { 182 | return this.contract.Transact(opts, "setApprovalForAll", operator, approved) 183 | } 184 | 185 | // EstimateGas 186 | func (this *Erc721Contract) EstimateSafeTransferFromGas(param TransactBaseParam, from common.Address, to common.Address, tokenId *big.Int) (int64, error) { 187 | input, err := this.abi.Pack("safeTransferFrom", from, to, tokenId) 188 | if err != nil { 189 | return 0, err 190 | } 191 | return EstimateContractMethodGas(param, this.backend, this.contractAddress, input) 192 | } 193 | 194 | func (this *Erc721Contract) EstimateSafeTransferFrom2Gas(param TransactBaseParam, from common.Address, to common.Address, tokenId *big.Int, data []byte) (int64, error) { 195 | input, err := this.abi.Pack("safeTransferFrom", from, to, tokenId, data) 196 | if err != nil { 197 | return 0, err 198 | } 199 | return EstimateContractMethodGas(param, this.backend, this.contractAddress, input) 200 | } 201 | 202 | func (this *Erc721Contract) EstimateTransferFromGas(param TransactBaseParam, from common.Address, to common.Address, tokenId *big.Int) (int64, error) { 203 | input, err := this.abi.Pack("transferFrom", from, to, tokenId) 204 | if err != nil { 205 | return 0, err 206 | } 207 | return EstimateContractMethodGas(param, this.backend, this.contractAddress, input) 208 | } 209 | 210 | func (this *Erc721Contract) EstimateApproveGas(param TransactBaseParam, to common.Address, tokenId *big.Int) (int64, error) { 211 | input, err := this.abi.Pack("approve", to, tokenId) 212 | if err != nil { 213 | return 0, err 214 | } 215 | return EstimateContractMethodGas(param, this.backend, this.contractAddress, input) 216 | } 217 | 218 | func (this *Erc721Contract) EstimateSetApprovalForAllGas(param TransactBaseParam, operator common.Address, approved bool) (int64, error) { 219 | input, err := this.abi.Pack("setApprovalForAll", operator, approved) 220 | if err != nil { 221 | return 0, err 222 | } 223 | return EstimateContractMethodGas(param, this.backend, this.contractAddress, input) 224 | } 225 | -------------------------------------------------------------------------------- /eth/ethlog.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "context" 5 | "encoding/hex" 6 | "github.com/ethereum/go-ethereum" 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/ethclient" 9 | "math/big" 10 | "strings" 11 | ) 12 | 13 | const ( 14 | TokenTypeErc20 = 1 15 | TokenTypeErc721 = 2 16 | ) 17 | 18 | type TransferEvent struct { 19 | Txid string 20 | BlockNumber int64 21 | TokenType int 22 | ContractAddress string 23 | From string 24 | To string 25 | Value string 26 | } 27 | 28 | func FilterTransferLog(cli *ethclient.Client, fromBlock int64, toBlock int64, contractAddresses []common.Address) ([]*TransferEvent, error) { 29 | //hex.EncodeToString(crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")).Bytes())) 30 | transferTopic := "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" 31 | logs, err := cli.FilterLogs(context.Background(), ethereum.FilterQuery{ 32 | FromBlock: big.NewInt(fromBlock), 33 | ToBlock: big.NewInt(toBlock), 34 | Addresses: contractAddresses, 35 | Topics: [][]common.Hash{[]common.Hash{HexToHash(transferTopic)}}, 36 | }) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | var evts []*TransferEvent 42 | sz := len(logs) 43 | if sz > 0 { 44 | evts = make([]*TransferEvent, 0, sz) 45 | for i := 0; i < sz; i++ { 46 | log := &logs[i] 47 | topicLen := len(log.Topics) 48 | if topicLen == 3 || topicLen == 4 { 49 | if hex.EncodeToString(log.Topics[0].Bytes()) != transferTopic { // Transfer event 50 | continue 51 | } 52 | 53 | from := parseEthLogAddress(hex.EncodeToString(log.Topics[1].Bytes())) 54 | to := parseEthLogAddress(hex.EncodeToString(log.Topics[2].Bytes())) 55 | 56 | var val string 57 | var tokenType int 58 | if topicLen == 3 { // ERC20 Transfer event 59 | tokenType = TokenTypeErc20 60 | val = hex.EncodeToString(log.Data) 61 | if len(val) == 64 { 62 | val = parseEthLogValue(val) 63 | } 64 | } else if topicLen == 4 { // ERC721 Transfer event 65 | tokenType = TokenTypeErc721 66 | val = parseEthLogValue(hex.EncodeToString(log.Topics[3].Bytes())) 67 | } 68 | if val != "" { 69 | evt := TransferEvent{ 70 | Txid: log.TxHash.String(), 71 | BlockNumber: int64(log.BlockNumber), 72 | TokenType: tokenType, 73 | ContractAddress: strings.ToLower(log.Address.String()), 74 | From: from, To: to, Value: val, 75 | } 76 | evts = append(evts, &evt) 77 | } 78 | } 79 | } 80 | } 81 | return evts, nil 82 | } 83 | 84 | func parseEthLogAddress(addr string) string { 85 | sz := len(addr) 86 | if sz >= 40 { 87 | return "0x" + strings.ToLower(addr[sz-40:]) 88 | } else { 89 | return "" 90 | } 91 | } 92 | 93 | func parseEthLogValue(val string) string { 94 | value := strings.TrimLeft(val, "0") 95 | sz := len(value) 96 | if sz == 0 { 97 | value = "00" 98 | } else if sz%2 == 1 { 99 | value = "0" + value 100 | } 101 | return value 102 | } 103 | -------------------------------------------------------------------------------- /eth/rpcclient.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/ethereum/go-ethereum" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/common/hexutil" 11 | "github.com/ethereum/go-ethereum/core/types" 12 | "github.com/ethereum/go-ethereum/ethclient" 13 | "github.com/ethereum/go-ethereum/rpc" 14 | "math/big" 15 | "strconv" 16 | ) 17 | 18 | type EthClient struct { 19 | RpcClient *ethclient.Client 20 | client *rpc.Client 21 | } 22 | 23 | func NewEthClient(URL string) (*EthClient, error) { 24 | client, err := rpc.Dial(URL) 25 | if err != nil { 26 | return nil, err 27 | } 28 | rpcClient := ethclient.NewClient(client) 29 | return &EthClient{RpcClient: rpcClient, client: client}, nil 30 | } 31 | 32 | func (this *EthClient) SetHeader(key, value string) { 33 | if this.client != nil { 34 | this.client.SetHeader(key, value) 35 | } 36 | } 37 | 38 | func (this *EthClient) GetTransactionCountByNumber(ctx context.Context, blockNumber int64) (uint, error) { 39 | var num hexutil.Uint 40 | err := this.client.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", hexutil.EncodeBig(big.NewInt(blockNumber))) 41 | return uint(num), err 42 | } 43 | 44 | func (this *EthClient) SendRawTransaction(ctx context.Context, signedHex string) (string, error) { 45 | var txid string 46 | err := this.client.CallContext(ctx, &txid, "eth_sendRawTransaction", signedHex) 47 | if err == nil && txid == "" { 48 | err = errors.New("SendRawTransaction: txid is empty") 49 | } 50 | return txid, err 51 | } 52 | 53 | type rpcTransaction struct { 54 | tx *types.Transaction 55 | txExtraInfo 56 | } 57 | 58 | type txExtraInfo struct { 59 | BlockNumber *string `json:"blockNumber,omitempty"` 60 | BlockHash *common.Hash `json:"blockHash,omitempty"` 61 | From *common.Address `json:"from,omitempty"` 62 | } 63 | 64 | func (tx *rpcTransaction) UnmarshalJSON(msg []byte) error { 65 | if err := json.Unmarshal(msg, &tx.tx); err != nil { 66 | return err 67 | } 68 | return json.Unmarshal(msg, &tx.txExtraInfo) 69 | } 70 | 71 | func (this *EthClient) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, from *common.Address, blockNumber *big.Int, err error) { 72 | var json *rpcTransaction 73 | err = this.client.CallContext(ctx, &json, "eth_getTransactionByHash", hash) 74 | if err != nil { 75 | return nil, nil, nil, err 76 | } else if json == nil { 77 | return nil, nil, nil, ethereum.NotFound 78 | } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { 79 | return nil, nil, nil, fmt.Errorf("server returned transaction without signature") 80 | } 81 | if json.BlockNumber != nil { 82 | if tmp, err := strconv.ParseInt(*json.BlockNumber, 0, 64); err == nil { 83 | blockNumber = big.NewInt(tmp) 84 | } 85 | } 86 | return json.tx, json.From, blockNumber, nil 87 | } 88 | -------------------------------------------------------------------------------- /eth/transact.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | "github.com/lizc2003/hdwallet/wallet" 10 | "math/big" 11 | ) 12 | 13 | type TransactBaseParam struct { 14 | From common.Address 15 | EthValue *big.Int 16 | GasPrice *big.Int 17 | GasFeeCap *big.Int 18 | GasTipCap *big.Int 19 | BaseFee *big.Int 20 | } 21 | 22 | func (this *TransactBaseParam) EnsureGasPrice(backend bind.ContractBackend) error { 23 | head, err := backend.HeaderByNumber(context.Background(), nil) 24 | if err != nil { 25 | return err 26 | } 27 | this.BaseFee = head.BaseFee 28 | 29 | if head.BaseFee == nil { 30 | if this.GasPrice == nil { 31 | price, err := backend.SuggestGasPrice(context.Background()) 32 | if err != nil { 33 | return err 34 | } 35 | this.GasPrice = price 36 | } 37 | } else { 38 | if this.GasTipCap == nil { 39 | tip, err := backend.SuggestGasTipCap(context.Background()) 40 | if err != nil { 41 | return err 42 | } 43 | this.GasTipCap = tip 44 | } 45 | if this.GasFeeCap == nil { 46 | gasFeeCap := new(big.Int).Add( 47 | this.GasTipCap, 48 | new(big.Int).Mul(head.BaseFee, big.NewInt(2)), 49 | ) 50 | this.GasFeeCap = gasFeeCap 51 | } 52 | if this.GasFeeCap.Cmp(this.GasTipCap) < 0 { 53 | return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", this.GasFeeCap, this.GasTipCap) 54 | } 55 | } 56 | return nil 57 | } 58 | 59 | func (this *TransactBaseParam) GetGasPrice() *big.Int { 60 | if this.BaseFee != nil { 61 | return new(big.Int).Add(this.BaseFee, this.GasTipCap) 62 | } else { 63 | return this.GasPrice 64 | } 65 | } 66 | 67 | func SignTx(w *wallet.EthWallet, tx *types.Transaction) (*types.Transaction, error) { 68 | signer := types.LatestSigner(w.ChainParams()) 69 | signedTx, err := types.SignTx(tx, signer, w.DeriveNativePrivateKey()) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return signedTx, nil 75 | } 76 | 77 | func MakeTransactOpts(w *wallet.EthWallet, param TransactBaseParam, gasLimit int64, nonce int64) (*bind.TransactOpts, error) { 78 | var theNonce *big.Int 79 | if nonce >= 0 { 80 | theNonce = big.NewInt(nonce) 81 | } 82 | 83 | if gasLimit < 0 { 84 | gasLimit = 0 85 | } 86 | 87 | txOpts := &bind.TransactOpts{ 88 | From: param.From, 89 | Nonce: theNonce, 90 | Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { 91 | return SignTx(w, tx) 92 | }, 93 | Value: param.EthValue, 94 | GasPrice: param.GasPrice, 95 | GasFeeCap: param.GasFeeCap, 96 | GasTipCap: param.GasTipCap, 97 | GasLimit: uint64(gasLimit), 98 | Context: context.Background(), 99 | } 100 | return txOpts, nil 101 | } 102 | 103 | func TransferEther(opts *bind.TransactOpts, backend bind.ContractBackend, addressTo common.Address) (*types.Transaction, error) { 104 | var nonce uint64 105 | if opts.Nonce != nil { 106 | nonce = opts.Nonce.Uint64() 107 | } else { 108 | tmp, err := backend.PendingNonceAt(context.Background(), opts.From) 109 | if err != nil { 110 | return nil, err 111 | } 112 | nonce = tmp 113 | } 114 | 115 | gasLimit := opts.GasLimit 116 | if gasLimit == 0 { 117 | gasLimit = wallet.EtherTransferGas 118 | } 119 | 120 | param := TransactBaseParam{GasPrice: opts.GasPrice, 121 | GasFeeCap: opts.GasFeeCap, 122 | GasTipCap: opts.GasTipCap, 123 | } 124 | err := param.EnsureGasPrice(backend) 125 | if err != nil { 126 | return nil, err 127 | } 128 | opts.GasPrice = param.GasPrice 129 | opts.GasFeeCap = param.GasFeeCap 130 | opts.GasTipCap = param.GasTipCap 131 | 132 | var tx *types.Transaction 133 | var input []byte 134 | if opts.GasFeeCap == nil { 135 | baseTx := &types.LegacyTx{ 136 | Nonce: nonce, 137 | To: &addressTo, 138 | GasPrice: opts.GasPrice, 139 | Gas: gasLimit, 140 | Value: opts.Value, 141 | Data: input, 142 | } 143 | tx = types.NewTx(baseTx) 144 | } else { 145 | baseTx := &types.DynamicFeeTx{ 146 | Nonce: nonce, 147 | To: &addressTo, 148 | GasFeeCap: opts.GasFeeCap, 149 | GasTipCap: opts.GasTipCap, 150 | Gas: gasLimit, 151 | Value: opts.Value, 152 | Data: input, 153 | } 154 | tx = types.NewTx(baseTx) 155 | } 156 | 157 | signedTx, err := opts.Signer(opts.From, tx) 158 | if err != nil { 159 | return nil, err 160 | } 161 | 162 | if opts.NoSend { 163 | return signedTx, nil 164 | } 165 | 166 | err = backend.SendTransaction(context.Background(), signedTx) 167 | if err != nil { 168 | return nil, err 169 | } 170 | 171 | return signedTx, nil 172 | } 173 | -------------------------------------------------------------------------------- /eth/util.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "errors" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | "github.com/ethereum/go-ethereum/core/types" 8 | "math/big" 9 | ) 10 | 11 | var BigIntEthGWei = big.NewInt(1e9) 12 | 13 | func HexToAddress(addr string) (common.Address, error) { 14 | if !common.IsHexAddress(addr) { 15 | return common.Address{}, errors.New("invalid address") 16 | } 17 | 18 | return common.HexToAddress(addr), nil 19 | } 20 | 21 | func HexToHash(s string) common.Hash { 22 | return common.HexToHash(s) 23 | } 24 | 25 | func WeiToGwei(v *big.Int) int64 { 26 | return big.NewInt(0).Div(v, BigIntEthGWei).Int64() 27 | } 28 | 29 | func GweiToWei(v int64) *big.Int { 30 | return big.NewInt(0).Mul(big.NewInt(v), BigIntEthGWei) 31 | } 32 | 33 | func CalcEthFee(gasPrice *big.Int, gas int64) int64 { 34 | return WeiToGwei(big.NewInt(0).Mul(big.NewInt(gas), gasPrice)) 35 | } 36 | 37 | func SerializeTransaction(tx *types.Transaction) (string, error) { 38 | data, err := tx.MarshalBinary() 39 | if err != nil { 40 | return "", err 41 | } 42 | return hexutil.Encode(data), nil 43 | } 44 | -------------------------------------------------------------------------------- /example/exam_wallet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lizc2003/hdwallet/wallet" 6 | "log" 7 | ) 8 | 9 | func main() { 10 | mnemonic, err := wallet.NewMnemonic(128) 11 | if err != nil { 12 | log.Fatal(err) 13 | } 14 | fmt.Println("mnemonic:", mnemonic) 15 | 16 | hdw, err := wallet.NewHDWallet(mnemonic, "", wallet.BtcChainMainNet, wallet.ChainMainNet) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | w, err := hdw.NewWallet(wallet.SymbolBtc, 0, 0, 0) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Printf("wallet: %s\n\tprivatekey: %s\n\tpublickey: %s\n\taddress: %s\n", 26 | w.Symbol(), 27 | w.DerivePrivateKey(), 28 | w.DerivePublicKey(), 29 | w.DeriveAddress()) 30 | 31 | w, err = hdw.NewSegWitWallet(0, 0, 0) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | fmt.Printf("segwit wallet: %s\n\tprivatekey: %s\n\tpublickey: %s\n\taddress: %s\n", 36 | w.Symbol(), 37 | w.DerivePrivateKey(), 38 | w.DerivePublicKey(), 39 | w.DeriveAddress()) 40 | 41 | w, err = hdw.NewNativeSegWitWallet(0, 0, 0) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | fmt.Printf("native segwit wallet: %s\n\tprivatekey: %s\n\tpublickey: %s\n\taddress: %s\n", 46 | w.Symbol(), 47 | w.DerivePrivateKey(), 48 | w.DerivePublicKey(), 49 | w.DeriveAddress()) 50 | 51 | w, err = hdw.NewWallet(wallet.SymbolEth, 0, 0, 0) 52 | if err != nil { 53 | log.Fatal(err) 54 | } 55 | fmt.Printf("wallet: %s\n\tprivatekey: %s\n\tpublickey: %s\n\taddress: %s\n", 56 | w.Symbol(), 57 | w.DerivePrivateKey(), 58 | w.DerivePublicKey(), 59 | w.DeriveAddress()) 60 | } 61 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lizc2003/hdwallet 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/btcsuite/btcd v0.23.4 7 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 8 | github.com/btcsuite/btcd/btcutil v1.1.3 9 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 10 | github.com/btcsuite/btcwallet/wallet/txauthor v1.3.3 11 | github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 12 | github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 13 | github.com/ethereum/go-ethereum v1.13.0 14 | github.com/lizc2003/gotron-sdk v0.0.0-20221010131620-2fa8f18bda85 15 | github.com/stretchr/testify v1.8.4 16 | github.com/tyler-smith/go-bip39 v1.1.0 17 | google.golang.org/grpc v1.37.0 18 | google.golang.org/protobuf v1.27.1 19 | ) 20 | 21 | require ( 22 | github.com/Microsoft/go-winio v0.6.1 // indirect 23 | github.com/StackExchange/wmi v1.2.1 // indirect 24 | github.com/aead/siphash v1.0.1 // indirect 25 | github.com/bits-and-blooms/bitset v1.5.0 // indirect 26 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect 27 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect 28 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect 29 | github.com/consensys/bavard v0.1.13 // indirect 30 | github.com/consensys/gnark-crypto v0.10.0 // indirect 31 | github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect 32 | github.com/davecgh/go-spew v1.1.1 // indirect 33 | github.com/deckarep/golang-set v1.8.0 // indirect 34 | github.com/deckarep/golang-set/v2 v2.1.0 // indirect 35 | github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect 36 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect 37 | github.com/ethereum/c-kzg-4844 v0.3.1 // indirect 38 | github.com/fsnotify/fsnotify v1.6.0 // indirect 39 | github.com/go-ole/go-ole v1.2.5 // indirect 40 | github.com/go-stack/stack v1.8.1 // indirect 41 | github.com/golang/protobuf v1.5.2 // indirect 42 | github.com/google/uuid v1.3.0 // indirect 43 | github.com/gorilla/websocket v1.4.2 // indirect 44 | github.com/holiman/uint256 v1.2.3 // indirect 45 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect 46 | github.com/mitchellh/go-homedir v1.1.0 // indirect 47 | github.com/mmcloughlin/addchain v0.4.0 // indirect 48 | github.com/pborman/uuid v1.2.1 // indirect 49 | github.com/pkg/errors v0.9.1 // indirect 50 | github.com/pmezard/go-difflib v1.0.0 // indirect 51 | github.com/rjeczalik/notify v0.9.2 // indirect 52 | github.com/shengdoushi/base58 v1.0.0 // indirect 53 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect 54 | github.com/supranational/blst v0.3.11 // indirect 55 | github.com/tklauser/go-sysconf v0.3.12 // indirect 56 | github.com/tklauser/numcpus v0.6.1 // indirect 57 | go.uber.org/atomic v1.6.0 // indirect 58 | go.uber.org/multierr v1.5.0 // indirect 59 | go.uber.org/zap v1.15.0 // indirect 60 | golang.org/x/crypto v0.12.0 // indirect 61 | golang.org/x/exp v0.0.0-20230810033253-352e893a4cad // indirect 62 | golang.org/x/mod v0.11.0 // indirect 63 | golang.org/x/net v0.10.0 // indirect 64 | golang.org/x/sync v0.3.0 // indirect 65 | golang.org/x/sys v0.11.0 // indirect 66 | golang.org/x/text v0.12.0 // indirect 67 | golang.org/x/tools v0.9.1 // indirect 68 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect 69 | gopkg.in/yaml.v3 v3.0.1 // indirect 70 | rsc.io/tmplfunc v0.0.3 // indirect 71 | ) 72 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= 5 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 6 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 7 | github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= 8 | github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= 9 | github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= 10 | github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= 11 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 12 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 13 | github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= 14 | github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 15 | github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= 16 | github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= 17 | github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g= 18 | github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= 19 | github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= 20 | github.com/btcsuite/btcd v0.23.4 h1:IzV6qqkfwbItOS/sg/aDfPDsjPP8twrCOE2R93hxMlQ= 21 | github.com/btcsuite/btcd v0.23.4/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= 22 | github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= 23 | github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= 24 | github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= 25 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= 26 | github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= 27 | github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= 28 | github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= 29 | github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= 30 | github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= 31 | github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= 32 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 33 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 34 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= 35 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 36 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= 37 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 38 | github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 39 | github.com/btcsuite/btcwallet/wallet/txauthor v1.3.3 h1:A4vz5wCONhxawdWs5y41gluZUVARhK/Pty1Uu1c5zkw= 40 | github.com/btcsuite/btcwallet/wallet/txauthor v1.3.3/go.mod h1:k8f2FcgUQUdfpkY98vWvDYpXLb83g17EupcLv1KcNJw= 41 | github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg= 42 | github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0= 43 | github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 h1:PszOub7iXVYbtGybym5TGCp9Dv1h1iX4rIC3HICZGLg= 44 | github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= 45 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= 46 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 47 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 48 | github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= 49 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 50 | github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 51 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= 52 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 53 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 54 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 55 | github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= 56 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 57 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 58 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 59 | github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= 60 | github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= 61 | github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM/P5NAw0e2Rf1FGvzDau1h54MKvx8G7w= 62 | github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= 63 | github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= 64 | github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= 65 | github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= 66 | github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= 67 | github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= 68 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 69 | github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= 70 | github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= 71 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 72 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 73 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 74 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 75 | github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= 76 | github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= 77 | github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= 78 | github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= 79 | github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= 80 | github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= 81 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= 82 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= 83 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= 84 | github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= 85 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 86 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 87 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 88 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 89 | github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= 90 | github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= 91 | github.com/ethereum/go-ethereum v1.13.0 h1:dZALM0PlDTtNITTECPiqSrFo0iEYVDfby+mSVc0LxIs= 92 | github.com/ethereum/go-ethereum v1.13.0/go.mod h1:0TDsBNJ7j8jR01vKpk4j2zfVKyAbQuKzy6wLwb5ZMuU= 93 | github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= 94 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 95 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 96 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 97 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 98 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= 99 | github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= 100 | github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 101 | github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= 102 | github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= 103 | github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= 104 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 105 | github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= 106 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 107 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 108 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 109 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 110 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 111 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 112 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 113 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 114 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 115 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 116 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 117 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 118 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 119 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 120 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 121 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= 122 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 123 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 124 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 125 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 126 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 127 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 128 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 129 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 130 | github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 131 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 132 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 133 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 134 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 135 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 136 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 137 | github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= 138 | github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= 139 | github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= 140 | github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= 141 | github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= 142 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 143 | github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= 144 | github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= 145 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 146 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 147 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 148 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 149 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= 150 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 151 | github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= 152 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 153 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 154 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 155 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 156 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 157 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 158 | github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= 159 | github.com/lizc2003/gotron-sdk v0.0.0-20221010131620-2fa8f18bda85 h1:Fks/NKp1O5hhuq8E3O//lL63HfNqEhHUIk95IwVyJ3g= 160 | github.com/lizc2003/gotron-sdk v0.0.0-20221010131620-2fa8f18bda85/go.mod h1:+oefs9FxyKXctgp3Hdc3FUE8URQJJdWMZVdm2+r27tE= 161 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 162 | github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 163 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 164 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= 165 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 166 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 167 | github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= 168 | github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= 169 | github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= 170 | github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= 171 | github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= 172 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 173 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 174 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 175 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 176 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 177 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 178 | github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 179 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 180 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 181 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 182 | github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= 183 | github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 184 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 185 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 186 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 187 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 188 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 189 | github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= 190 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 191 | github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= 192 | github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= 193 | github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= 194 | github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= 195 | github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= 196 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 197 | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= 198 | github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= 199 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 200 | github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= 201 | github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= 202 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= 203 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 204 | github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= 205 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 206 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 207 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 208 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 209 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 210 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 211 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 212 | github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= 213 | github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 214 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= 215 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= 216 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 217 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 218 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 219 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 220 | github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= 221 | github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= 222 | github.com/urfave/cli/v2 v2.24.1 h1:/QYYr7g0EhwXEML8jO+8OYt5trPnLHS0p3mrgExJ5NU= 223 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 224 | go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= 225 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 226 | go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= 227 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 228 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= 229 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 230 | go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= 231 | go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= 232 | golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 233 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 234 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 235 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 236 | golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= 237 | golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 238 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 239 | golang.org/x/exp v0.0.0-20230810033253-352e893a4cad h1:g0bG7Z4uG+OgH2QDODnjp6ggkk1bJDsINcuWmJN1iJU= 240 | golang.org/x/exp v0.0.0-20230810033253-352e893a4cad/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 241 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 242 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 243 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 244 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 245 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 246 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 247 | golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= 248 | golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 249 | golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 250 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 251 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 252 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 253 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 254 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 255 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 256 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 257 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 258 | golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 259 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 260 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 261 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 262 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 263 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 264 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 265 | golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= 266 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 267 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 268 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 269 | golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 270 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 271 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 272 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 273 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 274 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 275 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 276 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 277 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 278 | golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 279 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 280 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 281 | golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= 282 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 283 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 284 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 285 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 286 | golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= 287 | golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 288 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 289 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 290 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 291 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 292 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 293 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 294 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 295 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 296 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 297 | golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= 298 | golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 299 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 300 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 301 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 302 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 303 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 304 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 305 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 306 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 307 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= 308 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 309 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 310 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 311 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 312 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 313 | google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= 314 | google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 315 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 316 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 317 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 318 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 319 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 320 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 321 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 322 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 323 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 324 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 325 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 326 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 327 | google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= 328 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 329 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 330 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 331 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 332 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 333 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 334 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 335 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 336 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 337 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 338 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 339 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 340 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 341 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 342 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 343 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 344 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 345 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 346 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 347 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 348 | rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= 349 | rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= 350 | -------------------------------------------------------------------------------- /qa/btc/run_bitcoind.go: -------------------------------------------------------------------------------- 1 | package btc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lizc2003/hdwallet/btc" 6 | "github.com/lizc2003/hdwallet/qa" 7 | "github.com/lizc2003/hdwallet/wallet" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | const ( 15 | RPCPortRegtest = 18443 16 | BitcoinBinPathEnv = "BITCOIN_BIN_PATH" 17 | ) 18 | 19 | // RunOptions . 20 | type RunOptions struct { 21 | NewTmpDir bool 22 | RPCPort uint 23 | Args []string 24 | } 25 | 26 | func RunBitcoind(optionsPtr *RunOptions) (*btc.BtcClient, func(), error) { 27 | var options RunOptions 28 | if optionsPtr == nil { 29 | options = RunOptions{} 30 | } else { 31 | options = *optionsPtr 32 | } 33 | 34 | if options.RPCPort == 0 { 35 | options.RPCPort = RPCPortRegtest 36 | } 37 | 38 | if qa.IsProcessRunning(options.RPCPort, "bitcoin") { 39 | return nil, nil, fmt.Errorf("bitcoind already running on port %d", options.RPCPort) 40 | } 41 | 42 | var killHooks []killHook 43 | 44 | var dataDir string 45 | if options.NewTmpDir { 46 | for _, arg := range options.Args { 47 | if strings.Contains(arg, "-datadir=") { 48 | return nil, nil, fmt.Errorf("already provide param datadir >> %v", arg) 49 | } 50 | } 51 | 52 | tmpDir := strings.TrimRight(os.TempDir(), "/") 53 | dataDir = tmpDir + "/btccli_bitcoind_datatmp_" + time.Now().Format(time.RFC3339) + "/" 54 | err := os.MkdirAll(dataDir, 0777) 55 | if err != nil { 56 | return nil, nil, fmt.Errorf("cannot create tmp dir: %v, err: %v", dataDir, err) 57 | } 58 | 59 | options.Args = append(options.Args, "-datadir="+dataDir) 60 | 61 | killHooks = append(killHooks, func() error { 62 | return os.RemoveAll(dataDir) 63 | }) 64 | } 65 | 66 | //bitcoin/share/rpcauth$ python3 rpcauth.py rpcusr 233 67 | //String to be appended to bitcoin.conf: 68 | //rpcauth=rpcusr:656f9dabc62f0eb697c801369617dc60$422d7fca742d4a59460f941dc9247c782558367edcbf1cd790b2b7ff5624fc1b 69 | //Your password: 233 70 | args := []string{ 71 | "-regtest", 72 | "-txindex", 73 | "-fallbackfee=0.0002", 74 | "-rpcauth=rpcusr:656f9dabc62f0eb697c801369617dc60$422d7fca742d4a59460f941dc9247c782558367edcbf1cd790b2b7ff5624fc1b", 75 | fmt.Sprintf("-rpcport=%d", options.RPCPort), 76 | } 77 | args = append(args, options.Args...) 78 | 79 | cmd := exec.Command(getCmdBitcoind(), args...) 80 | fmt.Println(cmd.Args) 81 | cmd.Stdout = os.Stdout 82 | cmd.Stderr = os.Stderr 83 | err := cmd.Start() 84 | if err != nil { 85 | for _, hook := range killHooks { 86 | hook() 87 | } 88 | return nil, nil, err 89 | } 90 | 91 | closeChan := make(chan struct{}) 92 | go func() { 93 | fmt.Println("Wait for message to kill bitcoind") 94 | <-closeChan 95 | fmt.Println("Received message, killing bitcoind ...") 96 | 97 | if e := cmd.Process.Kill(); e != nil { 98 | fmt.Println("kill bitcoind error:", e) 99 | } 100 | for _, hook := range killHooks { 101 | hook() 102 | } 103 | 104 | fmt.Println("bitcoind exited.") 105 | closeChan <- struct{}{} 106 | }() 107 | 108 | fmt.Println("waiting for bitcoind starting, 5 seconds") 109 | time.Sleep(5 * time.Second) 110 | 111 | killFunc := func() { 112 | closeChan <- struct{}{} 113 | time.Sleep(1 * time.Second) 114 | <-closeChan 115 | } 116 | 117 | host := fmt.Sprintf("http://127.0.0.1:%d", options.RPCPort) 118 | cli, err := btc.NewBtcClient(host, "rpcusr", "233", wallet.BtcChainRegtest) 119 | if err != nil { 120 | killFunc() 121 | return nil, nil, err 122 | } 123 | 124 | return cli, killFunc, nil 125 | } 126 | 127 | type killHook func() error 128 | 129 | func getCmdBitcoind() string { 130 | p := os.Getenv(BitcoinBinPathEnv) 131 | if p == "" { 132 | panic("run bitcoind need set env: BITCOIN_BIN_PATH") 133 | } 134 | cmdBitcoind := p + "/bitcoind" 135 | fmt.Println("bitcoin bin path:", cmdBitcoind) 136 | return cmdBitcoind 137 | } 138 | -------------------------------------------------------------------------------- /qa/btc/transact_test.go: -------------------------------------------------------------------------------- 1 | package btc 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/btcsuite/btcd/btcjson" 7 | "github.com/btcsuite/btcd/btcutil" 8 | "github.com/lizc2003/hdwallet/btc" 9 | "github.com/lizc2003/hdwallet/wallet" 10 | "github.com/stretchr/testify/require" 11 | "testing" 12 | ) 13 | 14 | func TestTransaction(t *testing.T) { 15 | rq := require.New(t) 16 | 17 | cli, killBitcoind, err := RunBitcoind(&RunOptions{NewTmpDir: true}) 18 | rq.Nil(err) 19 | defer killBitcoind() 20 | 21 | mnemonic, err := wallet.NewMnemonic(128) 22 | rq.Nil(err) 23 | 24 | btcChainId := wallet.BtcChainRegtest 25 | hdw, err := wallet.NewHDWallet(mnemonic, "", btcChainId, wallet.ChainMainNet) 26 | rq.Nil(err) 27 | 28 | w0, err := hdw.NewWallet(wallet.SymbolBtc, 0, 0, 0) 29 | //w0, err := hdw.NewSegWitWallet(0, 0, 0) 30 | //w0, err := hdw.NewNativeSegWitWallet(0, 0, 0) 31 | rq.Nil(err) 32 | 33 | w1, err := hdw.NewWallet(wallet.SymbolBtc, 0, 0, 1) 34 | rq.Nil(err) 35 | w2, err := hdw.NewSegWitWallet(0, 0, 1) 36 | rq.Nil(err) 37 | w3, err := hdw.NewNativeSegWitWallet(0, 0, 1) 38 | rq.Nil(err) 39 | 40 | chainParams, _ := wallet.GetBtcChainParams(btcChainId) 41 | a0 := w0.DeriveAddress() 42 | a1 := w1.DeriveAddress() 43 | a2 := w2.DeriveAddress() 44 | a3 := w3.DeriveAddress() 45 | fmt.Printf("a0: %s\na1: %s\na2: %s\na3: %s\n", a0, a1, a2, a3) 46 | addrA0, _ := btc.DecodeAddress(a0, chainParams) 47 | addrA1, _ := btc.DecodeAddress(a1, chainParams) 48 | addrA2, _ := btc.DecodeAddress(a2, chainParams) 49 | addrA3, _ := btc.DecodeAddress(a3, chainParams) 50 | 51 | { 52 | for _, ad := range []string{a0, a1, a2, a3} { 53 | err = cli.RpcClient.ImportAddress(ad) 54 | rq.Nil(err) 55 | } 56 | } 57 | 58 | var utxo btcjson.ListUnspentResult 59 | { 60 | // Generate 101 blocks for a0 (produce utxo) 61 | _, err = cli.RpcClient.GenerateToAddress(101, addrA0, nil) 62 | rq.Nil(err) 63 | 64 | cliUnspents, err := cli.RpcClient.ListUnspentMinMaxAddresses(1, 999, []btcutil.Address{addrA0}) 65 | rq.Nil(err) 66 | rq.Equal(1, len(cliUnspents), "") 67 | 68 | utxo = cliUnspents[0] 69 | rq.Equal(50.0, utxo.Amount, "coinbase amount should be 50") 70 | fmt.Println(utxo) 71 | } 72 | 73 | transferAmount := 2.2 74 | var tx *btc.BtcTransaction 75 | 76 | { // build tx 77 | //feePerKb, err := cli.EstimateFeePerKb() 78 | //rq.Nil(err) 79 | feePerKb := int64(80 * 1000) 80 | 81 | unspent := btc.BtcUnspent{TxID: utxo.TxID, Vout: utxo.Vout, 82 | ScriptPubKey: utxo.ScriptPubKey, RedeemScript: utxo.RedeemScript, 83 | Amount: utxo.Amount} 84 | 85 | out1 := btc.BtcOutput{Address: addrA1, Amount: btc.BtcToSatoshi(transferAmount)} 86 | out2 := btc.BtcOutput{Address: addrA2, Amount: btc.BtcToSatoshi(transferAmount)} 87 | out3 := btc.BtcOutput{Address: addrA3, Amount: btc.BtcToSatoshi(transferAmount)} 88 | 89 | tx, err = btc.NewBtcTransaction([]btc.BtcUnspent{unspent}, []btc.BtcOutput{out1, out2, out3}, 90 | addrA0, feePerKb, chainParams) 91 | rq.Nil(err) 92 | } 93 | 94 | { // fee 95 | fee := tx.GetFee() 96 | fmt.Println("fee:", fee) 97 | } 98 | 99 | { // sign 100 | err = tx.Sign(w0.(*wallet.BtcWallet)) 101 | rq.Nil(err) 102 | } 103 | 104 | { // decode 105 | ret := tx.Decode() 106 | b, _ := json.MarshalIndent(ret, "", " ") 107 | fmt.Println("decoded tx:", string(b)) 108 | } 109 | 110 | { // send 111 | hash, err := tx.Send(cli.RpcClient, false) 112 | rq.Nil(err) 113 | 114 | txid := hash.String() 115 | fmt.Println("txid:", txid) 116 | rq.Equal(txid, tx.GetTxid(), "txid mismatch") 117 | 118 | rawtx, err := cli.RpcClient.GetRawTransactionVerbose(hash) 119 | rq.Nil(err) 120 | b, _ := json.MarshalIndent(rawtx, "", " ") 121 | fmt.Println("raw tx:", string(b)) 122 | 123 | hex, _ := tx.Serialize() 124 | rq.Equal(hex, rawtx.Hex, "wrong hex") 125 | } 126 | 127 | { //generate 1 block 128 | _, err = cli.RpcClient.GenerateToAddress(1, addrA0, nil) 129 | rq.Nil(err) 130 | } 131 | 132 | { // query utxo of a1 133 | utxos, err := cli.RpcClient.ListUnspentMinMaxAddresses(0, 999, []btcutil.Address{addrA1}) 134 | rq.Nil(err) 135 | rq.True(len(utxos) > 0, "No utxo of a1 fund!") 136 | b, _ := json.MarshalIndent(utxos, "", " ") 137 | fmt.Println("utxo for a1:", string(b)) 138 | rq.Equal(transferAmount, utxos[0].Amount, "Wrong amount") 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /qa/check_running.go: -------------------------------------------------------------------------------- 1 | package qa 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os/exec" 7 | "runtime" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func IsProcessRunning(port uint, name string) bool { 13 | if strings.Contains(runtime.GOOS, "linux") { 14 | checkPortCmd := exec.Command("netstat", "-ntpl") 15 | 16 | cmdPrint := executeCmd(checkPortCmd) 17 | if strings.Contains(cmdPrint, strconv.Itoa(int(port))) && strings.Contains(cmdPrint, name) { 18 | return true 19 | } 20 | return false 21 | } else if strings.Contains(runtime.GOOS, "darwin") { 22 | checkPortCmd := exec.Command("lsof", "-i", "tcp:"+strconv.FormatInt(int64(port), 10)) 23 | cmdPrint := executeCmd(checkPortCmd) 24 | if strings.Contains(cmdPrint, strconv.Itoa(int(port))) && strings.Contains(cmdPrint, "bitcoin") { 25 | return true 26 | } 27 | return false 28 | } else { 29 | panic("unsupported platform") 30 | } 31 | } 32 | 33 | func executeCmd(cmd *exec.Cmd) string { 34 | stderr, err := cmd.StderrPipe() 35 | panicIf(err, "Failed to get stderr pip ") 36 | stdout, err := cmd.StdoutPipe() 37 | panicIf(err, fmt.Sprintf("Failed to get stdout pipe %v", err)) 38 | 39 | err = cmd.Start() 40 | panicIf(err, fmt.Sprintf("Failed to start cmd %v", err)) 41 | 42 | b, err := ioutil.ReadAll(stdout) 43 | panicIf(err, fmt.Sprintf("Failed to read cmd (%v) stdout, %v", cmd, err)) 44 | out := string(b) 45 | 46 | bo, err := ioutil.ReadAll(stderr) 47 | panicIf(err, "Failed to read stderr") 48 | out += string(bo) 49 | 50 | cmd.Wait() 51 | stdout.Close() 52 | stderr.Close() 53 | return strings.TrimSpace(out) 54 | } 55 | 56 | func panicIf(e error, msg string) { 57 | if e != nil { 58 | panic(fmt.Errorf("【ERR】 %s %v", msg, e)) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /qa/eth/run_ganache.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lizc2003/hdwallet/eth" 6 | "github.com/lizc2003/hdwallet/qa" 7 | "os" 8 | "os/exec" 9 | "time" 10 | ) 11 | 12 | // ganache-cli --account="0xc356cfe48d1ddcd2320b62553fe8739978d0478e2e940d6c30190e7637f51c76,200" 13 | 14 | const ganacheCliPort = 8545 15 | 16 | func RunGanache(accountPrivateKeys ...string) (*eth.EthClient, func(), error) { 17 | if qa.IsProcessRunning(ganacheCliPort, "ganache-cli") { 18 | return nil, nil, fmt.Errorf("ganache already running on port %d", ganacheCliPort) 19 | } 20 | 21 | args := []string{} 22 | if len(accountPrivateKeys) > 0 { 23 | for _, a := range accountPrivateKeys { 24 | args = append(args, fmt.Sprintf("--account=0x%s,100000000000000000000", a)) //100 Ether 25 | } 26 | } 27 | 28 | cmd := exec.Command("ganache-cli", args...) 29 | cmd.Stdout = os.Stdout 30 | cmd.Stderr = os.Stderr 31 | fmt.Println(cmd.Args) 32 | err := cmd.Start() 33 | if err != nil { 34 | return nil, nil, err 35 | } 36 | 37 | closeChan := make(chan struct{}) 38 | 39 | go func() { 40 | fmt.Println("Wait for message to kill ganache-cli") 41 | <-closeChan 42 | fmt.Println("Received message,killing ganache-cli ...") 43 | 44 | if e := cmd.Process.Kill(); e != nil { 45 | fmt.Println("kill ganache-cli error:", e) 46 | } 47 | 48 | fmt.Println("ganache-cli exited.") 49 | closeChan <- struct{}{} 50 | }() 51 | 52 | fmt.Println("waiting for ganache-cli starting, 3 seconds") 53 | time.Sleep(3 * time.Second) 54 | 55 | killFunc := func() { 56 | closeChan <- struct{}{} 57 | time.Sleep(1 * time.Second) 58 | <-closeChan 59 | } 60 | 61 | cli, err := eth.NewEthClient(fmt.Sprintf("http://127.0.0.1:%d", ganacheCliPort)) 62 | if err != nil { 63 | killFunc() 64 | return nil, nil, err 65 | } 66 | 67 | return cli, killFunc, nil 68 | } 69 | -------------------------------------------------------------------------------- /qa/eth/transact_test.go: -------------------------------------------------------------------------------- 1 | package eth 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | "github.com/lizc2003/hdwallet/eth" 10 | "github.com/lizc2003/hdwallet/wallet" 11 | "github.com/stretchr/testify/require" 12 | "math/big" 13 | "testing" 14 | ) 15 | 16 | func TestTransaction(t *testing.T) { 17 | const tetherAbi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_upgradedAddress","type":"address"}],"name":"deprecate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"deprecated","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_evilUser","type":"address"}],"name":"addBlackList","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"upgradedAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maximumFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_maker","type":"address"}],"name":"getBlackListStatus","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newBasisPoints","type":"uint256"},{"name":"newMaxFee","type":"uint256"}],"name":"setParams","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"issue","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"redeem","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"basisPointsRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBlackListed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_clearedUser","type":"address"}],"name":"removeBlackList","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_UINT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_blackListedUser","type":"address"}],"name":"destroyBlackFunds","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_initialSupply","type":"uint256"},{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"Issue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newAddress","type":"address"}],"name":"Deprecate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"feeBasisPoints","type":"uint256"},{"indexed":false,"name":"maxFee","type":"uint256"}],"name":"Params","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_blackListedUser","type":"address"},{"indexed":false,"name":"_balance","type":"uint256"}],"name":"DestroyedBlackFunds","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_user","type":"address"}],"name":"AddedBlackList","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_user","type":"address"}],"name":"RemovedBlackList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"}]` 18 | const tetherBytecode = `606060405260008060146101000a81548160ff0219169083151502179055506000600355600060045534156200003457600080fd5b60405162002d7a38038062002d7a83398101604052808051906020019091908051820191906020018051820191906020018051906020019091905050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550836001819055508260079080519060200190620000cf9291906200017a565b508160089080519060200190620000e89291906200017a565b508060098190555083600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506000600a60146101000a81548160ff0219169083151502179055505050505062000229565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620001bd57805160ff1916838001178555620001ee565b82800160010185558215620001ee579182015b82811115620001ed578251825591602001919060010190620001d0565b5b509050620001fd919062000201565b5090565b6200022691905b808211156200022257600081600090555060010162000208565b5090565b90565b612b4180620002396000396000f30060606040523615610194576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146101995780630753c30c14610227578063095ea7b3146102605780630e136b19146102a25780630ecb93c0146102cf57806318160ddd1461030857806323b872dd1461033157806326976e3f1461039257806327e235e3146103e7578063313ce56714610434578063353907141461045d5780633eaaf86b146104865780633f4ba83a146104af57806359bf1abe146104c45780635c658165146105155780635c975abb1461058157806370a08231146105ae5780638456cb59146105fb578063893d20e8146106105780638da5cb5b1461066557806395d89b41146106ba578063a9059cbb14610748578063c0324c771461078a578063cc872b66146107b6578063db006a75146107d9578063dd62ed3e146107fc578063dd644f7214610868578063e47d606014610891578063e4997dc5146108e2578063e5b5019a1461091b578063f2fde38b14610944578063f3bdc2281461097d575b600080fd5b34156101a457600080fd5b6101ac6109b6565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101ec5780820151818401526020810190506101d1565b50505050905090810190601f1680156102195780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561023257600080fd5b61025e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a54565b005b341561026b57600080fd5b6102a0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610b71565b005b34156102ad57600080fd5b6102b5610cbf565b604051808215151515815260200191505060405180910390f35b34156102da57600080fd5b610306600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cd2565b005b341561031357600080fd5b61031b610deb565b6040518082815260200191505060405180910390f35b341561033c57600080fd5b610390600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610ebb565b005b341561039d57600080fd5b6103a561109b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156103f257600080fd5b61041e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506110c1565b6040518082815260200191505060405180910390f35b341561043f57600080fd5b6104476110d9565b6040518082815260200191505060405180910390f35b341561046857600080fd5b6104706110df565b6040518082815260200191505060405180910390f35b341561049157600080fd5b6104996110e5565b6040518082815260200191505060405180910390f35b34156104ba57600080fd5b6104c26110eb565b005b34156104cf57600080fd5b6104fb600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506111a9565b604051808215151515815260200191505060405180910390f35b341561052057600080fd5b61056b600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506111ff565b6040518082815260200191505060405180910390f35b341561058c57600080fd5b610594611224565b604051808215151515815260200191505060405180910390f35b34156105b957600080fd5b6105e5600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611237565b6040518082815260200191505060405180910390f35b341561060657600080fd5b61060e611346565b005b341561061b57600080fd5b610623611406565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561067057600080fd5b61067861142f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156106c557600080fd5b6106cd611454565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561070d5780820151818401526020810190506106f2565b50505050905090810190601f16801561073a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561075357600080fd5b610788600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506114f2565b005b341561079557600080fd5b6107b4600480803590602001909190803590602001909190505061169c565b005b34156107c157600080fd5b6107d76004808035906020019091905050611781565b005b34156107e457600080fd5b6107fa6004808035906020019091905050611978565b005b341561080757600080fd5b610852600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611b0b565b6040518082815260200191505060405180910390f35b341561087357600080fd5b61087b611c50565b6040518082815260200191505060405180910390f35b341561089c57600080fd5b6108c8600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611c56565b604051808215151515815260200191505060405180910390f35b34156108ed57600080fd5b610919600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611c76565b005b341561092657600080fd5b61092e611d8f565b6040518082815260200191505060405180910390f35b341561094f57600080fd5b61097b600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611db3565b005b341561098857600080fd5b6109b4600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611e88565b005b60078054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a4c5780601f10610a2157610100808354040283529160200191610a4c565b820191906000526020600020905b815481529060010190602001808311610a2f57829003601f168201915b505050505081565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610aaf57600080fd5b6001600a60146101000a81548160ff02191690831515021790555080600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fcc358699805e9a8b7f77b522628c7cb9abd07d9efb86b6fb616af1609036a99e81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b604060048101600036905010151515610b8957600080fd5b600a60149054906101000a900460ff1615610caf57600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663aee92d333385856040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050600060405180830381600087803b1515610c9657600080fd5b6102c65a03f11515610ca757600080fd5b505050610cba565b610cb9838361200c565b5b505050565b600a60149054906101000a900460ff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610d2d57600080fd5b6001600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055507f42e160154868087d6bfdc0ca23d96a1c1cfa32f1b72ba9ba27b69b98a0d819dc81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b6000600a60149054906101000a900460ff1615610eb257600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1515610e9057600080fd5b6102c65a03f11515610ea157600080fd5b505050604051805190509050610eb8565b60015490505b90565b600060149054906101000a900460ff16151515610ed757600080fd5b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610f3057600080fd5b600a60149054906101000a900460ff161561108a57600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16638b477adb338585856040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001945050505050600060405180830381600087803b151561107157600080fd5b6102c65a03f1151561108257600080fd5b505050611096565b6110958383836121a9565b5b505050565b600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60026020528060005260406000206000915090505481565b60095481565b60045481565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561114657600080fd5b600060149054906101000a900460ff16151561116157600080fd5b60008060146101000a81548160ff0219169083151502179055507f7805862f689e2f13df9f062ff482ad3ad112aca9e0847911ed832e158c525b3360405160405180910390a1565b6000600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff169050919050565b6005602052816000526040600020602052806000526040600020600091509150505481565b600060149054906101000a900460ff1681565b6000600a60149054906101000a900460ff161561133557600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231836000604051602001526040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b151561131357600080fd5b6102c65a03f1151561132457600080fd5b505050604051805190509050611341565b61133e82612650565b90505b919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156113a157600080fd5b600060149054906101000a900460ff161515156113bd57600080fd5b6001600060146101000a81548160ff0219169083151502179055507f6985a02210a168e66602d3235cb6db0e70f92b3ba4d376a33c0f3d9434bff62560405160405180910390a1565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60088054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156114ea5780601f106114bf576101008083540402835291602001916114ea565b820191906000526020600020905b8154815290600101906020018083116114cd57829003601f168201915b505050505081565b600060149054906101000a900460ff1615151561150e57600080fd5b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561156757600080fd5b600a60149054906101000a900460ff161561168d57600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636e18980a3384846040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050600060405180830381600087803b151561167457600080fd5b6102c65a03f1151561168557600080fd5b505050611698565b6116978282612699565b5b5050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156116f757600080fd5b60148210151561170657600080fd5b60328110151561171557600080fd5b81600381905550611734600954600a0a82612a0190919063ffffffff16565b6004819055507fb044a1e409eac5c48e5af22d4af52670dd1a99059537a78b31b48c6500a6354e600354600454604051808381526020018281526020019250505060405180910390a15050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156117dc57600080fd5b60015481600154011115156117f057600080fd5b600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205481600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054011115156118c057600080fd5b80600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550806001600082825401925050819055507fcb8241adb0c3fdb35b70c24ce35c5eb0c17af7431c99f827d44a445ca624176a816040518082815260200191505060405180910390a150565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156119d357600080fd5b80600154101515156119e457600080fd5b80600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515611a5357600080fd5b8060016000828254039250508190555080600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507f702d5967f45f6513a38ffc42d6ba9bf230bd40e8f53b16363c7eb4fd2deb9a44816040518082815260200191505060405180910390a150565b6000600a60149054906101000a900460ff1615611c3d57600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e84846000604051602001526040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600087803b1515611c1b57600080fd5b6102c65a03f11515611c2c57600080fd5b505050604051805190509050611c4a565b611c478383612a3c565b90505b92915050565b60035481565b60066020528060005260406000206000915054906101000a900460ff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611cd157600080fd5b6000600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055507fd7e9ec6e6ecd65492dce6bf513cd6867560d49544421d0783ddf06e76c24470c81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611e0e57600080fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515611e8557806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611ee557600080fd5b600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611f3d57600080fd5b611f4682611237565b90506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550806001600082825403925050819055507f61e6e66b0d6339b2980aecc6ccc0039736791f0ccde9ed512e789a7fbdd698c68282604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15050565b60406004810160003690501015151561202457600080fd5b600082141580156120b257506000600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b1515156120be57600080fd5b81600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a3505050565b60008060006060600481016000369050101515156121c657600080fd5b600560008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054935061226e61271061226060035488612a0190919063ffffffff16565b612ac390919063ffffffff16565b92506004548311156122805760045492505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84101561233c576122bb8585612ade90919063ffffffff16565b600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b61234f8386612ade90919063ffffffff16565b91506123a385600260008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612ade90919063ffffffff16565b600260008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061243882600260008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612af790919063ffffffff16565b600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060008311156125e2576124f783600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612af790919063ffffffff16565b600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a35b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a350505050505050565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000806040600481016000369050101515156126b457600080fd5b6126dd6127106126cf60035487612a0190919063ffffffff16565b612ac390919063ffffffff16565b92506004548311156126ef5760045492505b6127028385612ade90919063ffffffff16565b915061275684600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612ade90919063ffffffff16565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506127eb82600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612af790919063ffffffff16565b600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506000831115612995576128aa83600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612af790919063ffffffff16565b600260008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a35b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a35050505050565b6000806000841415612a165760009150612a35565b8284029050828482811515612a2757fe5b04141515612a3157fe5b8091505b5092915050565b6000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6000808284811515612ad157fe5b0490508091505092915050565b6000828211151515612aec57fe5b818303905092915050565b6000808284019050838110151515612b0b57fe5b80915050929150505600a165627a7a72305820e4bd01aa0dd2dae0e4de0b96f1709135b71dd2f706600c8c931159f686eb91140029` 19 | 20 | rq := require.New(t) 21 | 22 | mnemonic, err := wallet.NewMnemonic(128) 23 | rq.Nil(err) 24 | 25 | ethChainId := wallet.ChainPrivate 26 | chainParam, err := wallet.GetEthChainParams(ethChainId) 27 | rq.Nil(err) 28 | hdw, err := wallet.NewHDWallet(mnemonic, "", wallet.BtcChainMainNet, ethChainId) 29 | rq.Nil(err) 30 | 31 | w0, err := hdw.NewWallet(wallet.SymbolEth, 0, 0, 0) 32 | rq.Nil(err) 33 | w1, err := hdw.NewWallet(wallet.SymbolEth, 0, 0, 1) 34 | rq.Nil(err) 35 | 36 | a0 := w0.DeriveAddress() 37 | a1 := w1.DeriveAddress() 38 | fmt.Printf("a0: %s\na1: %s\n", a0, a1) 39 | 40 | addrA0, _ := eth.HexToAddress(a0) 41 | addrA1, _ := eth.HexToAddress(a1) 42 | 43 | cli, killGanache, err := RunGanache(w0.DerivePrivateKey()) // w0 init 100 ehter 44 | rq.Nil(err) 45 | defer killGanache() 46 | 47 | { 48 | fmt.Println("Get balance --------") 49 | 50 | rightBalances := []struct { 51 | address common.Address 52 | balance *big.Int 53 | }{ 54 | {address: addrA0, balance: big.NewInt(0).Mul(big.NewInt(wallet.WeiPerEther), big.NewInt(100))}, 55 | {address: addrA1, balance: big.NewInt(0)}, 56 | } 57 | 58 | for _, rightBal := range rightBalances { 59 | bal, err := cli.RpcClient.BalanceAt(context.Background(), rightBal.address, nil) 60 | rq.Nil(err, "get balance fail") 61 | fmt.Println("addr balance:", rightBal.address, bal) 62 | rq.True(bal.Cmp(rightBal.balance) == 0, "Wrong balance") 63 | } 64 | } 65 | 66 | transferAmount := big.NewInt(6 * wallet.WeiPerEther) 67 | rq.Nil(err) 68 | 69 | { // Transfer ether 70 | 71 | baseParam := eth.TransactBaseParam{ 72 | From: addrA0, 73 | EthValue: transferAmount, 74 | } 75 | err = baseParam.EnsureGasPrice(cli.RpcClient) 76 | rq.Nil(err) 77 | fmt.Printf("baseParam: %+v\n", baseParam) 78 | opts, err := eth.MakeTransactOpts(w0.(*wallet.EthWallet), baseParam, -1, -1) 79 | rq.Nil(err) 80 | fmt.Printf("opts: %+v\n", opts) 81 | 82 | tx, err := eth.TransferEther(opts, cli.RpcClient, addrA1) 83 | rq.Nil(err) 84 | 85 | fmt.Printf("transfer ether, txid: %s, gas: %d, fee: %d\n", tx.Hash().String(), tx.Gas(), 86 | eth.CalcEthFee(baseParam.GetGasPrice(), int64(tx.Gas()))) 87 | 88 | b, _ := json.MarshalIndent(tx, "", " ") 89 | fmt.Println("tx:", string(b)) 90 | 91 | fmt.Println("get transaction receipt ----------") 92 | 93 | tx2, from, blockNumber, err := cli.TransactionByHash(context.Background(), tx.Hash()) 94 | rq.Nil(err) 95 | rq.True(from.String() == a0) 96 | 97 | receipt, err := cli.RpcClient.TransactionReceipt(context.Background(), tx.Hash()) 98 | rq.Nil(err) 99 | fmt.Println("tx block number:", receipt.BlockNumber) 100 | rq.True(receipt.BlockNumber.Cmp(blockNumber) == 0) 101 | block, err := cli.RpcClient.BlockByNumber(context.Background(), blockNumber) 102 | rq.Nil(err) 103 | sig := types.MakeSigner(chainParam, receipt.BlockNumber, block.Time()) 104 | from2, err := types.Sender(sig, tx2) 105 | rq.Nil(err) 106 | fmt.Println("tx from address:", from2) 107 | rq.True(from2.String() == a0) 108 | } 109 | 110 | { // confirm transfer 111 | fmt.Println("Get balance --------") 112 | rightBalances := []struct { 113 | address common.Address 114 | balance *big.Int 115 | }{ 116 | {address: addrA0, balance: nil}, 117 | {address: addrA1, balance: transferAmount}, 118 | } 119 | 120 | for _, rightBal := range rightBalances { 121 | bal, err := cli.RpcClient.BalanceAt(context.Background(), rightBal.address, nil) 122 | rq.Nil(err, "get balance fail") 123 | fmt.Println("addr balance:", rightBal.address, bal) 124 | if rightBal.balance != nil { 125 | rq.True(bal.Cmp(rightBal.balance) == 0, "Wrong balance") 126 | } 127 | } 128 | } 129 | 130 | totalSupply := big.NewInt(1000 * 1000000) 131 | var contractAddr common.Address 132 | 133 | { // deploy contract 134 | baseParam := eth.TransactBaseParam{ 135 | From: addrA0, 136 | EthValue: nil, 137 | } 138 | err = baseParam.EnsureGasPrice(cli.RpcClient) 139 | rq.Nil(err) 140 | opts, err := eth.MakeTransactOpts(w0.(*wallet.EthWallet), baseParam, -1, -1) 141 | rq.Nil(err) 142 | 143 | var tx *types.Transaction 144 | contractAddr, tx, err = eth.DeployContract(opts, cli.RpcClient, tetherAbi, tetherBytecode, 145 | totalSupply, "Tether USD", "USDT", big.NewInt(6)) 146 | rq.Nil(err) 147 | 148 | fmt.Println("contract address:", contractAddr) 149 | receipt, err := cli.RpcClient.TransactionReceipt(context.Background(), tx.Hash()) 150 | rq.Nil(err) 151 | fmt.Printf("depoly contract, txid: %s, gas limit: %d, gas used: %d\n", tx.Hash().String(), tx.Gas(), receipt.GasUsed) 152 | } 153 | 154 | tokenAmount := big.NewInt(1000000) 155 | contract := eth.NewErc20Contract(contractAddr, cli.RpcClient) 156 | 157 | { // transfer erc20 158 | symbol, _ := contract.Symbol() 159 | name, _ := contract.Name() 160 | decimals, _ := contract.Decimals() 161 | total, _ := contract.TotalSupply() 162 | fmt.Println("contract symbol:", symbol) 163 | fmt.Println("contract name:", name) 164 | fmt.Println("contract decimals:", decimals) 165 | fmt.Println("contract total supply:", total) 166 | rq.True(total.Cmp(totalSupply) == 0, "Wrong total supply") 167 | 168 | baseParam := eth.TransactBaseParam{ 169 | From: addrA0, 170 | EthValue: nil, 171 | } 172 | err = baseParam.EnsureGasPrice(cli.RpcClient) 173 | rq.Nil(err) 174 | opts, err := eth.MakeTransactOpts(w0.(*wallet.EthWallet), baseParam, -1, -1) 175 | rq.Nil(err) 176 | 177 | tx, err := contract.Transfer(opts, addrA1, tokenAmount) 178 | rq.Nil(err) 179 | 180 | receipt, err := cli.RpcClient.TransactionReceipt(context.Background(), tx.Hash()) 181 | rq.Nil(err) 182 | fmt.Printf("transfer token, txid: %s, gas limit: %d, gas used: %d\n", tx.Hash().String(), tx.Gas(), receipt.GasUsed) 183 | 184 | blockNum := receipt.BlockNumber.Int64() 185 | evts, err := eth.FilterTransferLog(cli.RpcClient, blockNum, blockNum, []common.Address{contractAddr}) 186 | rq.Nil(err) 187 | fmt.Printf("query transfer log: %+v\n", evts) 188 | rq.True(len(evts) == 1) 189 | } 190 | 191 | { // confirm transfer erc20 192 | amount, err := contract.BalanceOf(addrA1) 193 | rq.Nil(err) 194 | rq.True(tokenAmount.Cmp(amount) == 0, "Wrong balance") 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /qa/trx/transact_test.go: -------------------------------------------------------------------------------- 1 | package trx 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lizc2003/hdwallet/trx" 6 | "github.com/lizc2003/hdwallet/wallet" 7 | "github.com/stretchr/testify/require" 8 | "math/big" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | /* 14 | install: 15 | docker pull trontools/quickstart 16 | 17 | run: 18 | docker run -d -it -p 9090:9090 -p 50051:50051 -p 50052:50052 --rm --name tron \ 19 | -e "mnemonic=purse cheese cage reason cost flat jump usage hospital grit delay loan" \ 20 | -e "defaultBalance=1000000" -e "formatJson=true" \ 21 | trontools/quickstart 22 | 23 | check: 24 | curl "http://127.0.0.1:9090/admin/accounts?format=all" 25 | */ 26 | 27 | func TestTransaction(t *testing.T) { 28 | // TestTRC20Abi compile https://shasta.tronscan.org/?#/contracts/contract-compiler 29 | const TestTRC20Abi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"burn","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_value","type":"uint256"}],"name":"burnFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","type":"event"}]` 30 | // TestTRC20ContractBytecode base on trc20.sol 31 | const TestTRC20ContractBytecode = "60806040526002805460ff1916600317905534801561001d57600080fd5b50d3801561002a57600080fd5b50d2801561003757600080fd5b5060025460ff16600a0a6103e80260038190553360009081526004602081815260408084209490945583518085019094528184527f5454524300000000000000000000000000000000000000000000000000000000930192835261009b92906100e6565b506040805180820190915260048082527f545452430000000000000000000000000000000000000000000000000000000060209092019182526100e0916001916100e6565b50610181565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012757805160ff1916838001178555610154565b82800160010185558215610154579182015b82811115610154578251825591602001919060010190610139565b50610160929150610164565b5090565b61017e91905b80821115610160576000815560010161016a565b90565b610990806101906000396000f3006080604052600436106100a05763ffffffff60e060020a60003504166306fdde0381146100a5578063095ea7b31461014957806318160ddd1461019b57806323b872dd146101dc578063313ce5671461022057806342966c681461026557806370a082311461029757806379cc6790146102d257806395d89b4114610310578063a9059cbb1461033f578063cae9ca511461037f578063dd62ed3e14610402575b600080fd5b3480156100b157600080fd5b50d380156100be57600080fd5b50d280156100cb57600080fd5b506100d4610443565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561010e5781810151838201526020016100f6565b50505050905090810190601f16801561013b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015557600080fd5b50d3801561016257600080fd5b50d2801561016f57600080fd5b50610187600160a060020a03600435166024356104d1565b604080519115158252519081900360200190f35b3480156101a757600080fd5b50d380156101b457600080fd5b50d280156101c157600080fd5b506101ca6104fe565b60408051918252519081900360200190f35b3480156101e857600080fd5b50d380156101f557600080fd5b50d2801561020257600080fd5b50610187600160a060020a0360043581169060243516604435610504565b34801561022c57600080fd5b50d3801561023957600080fd5b50d2801561024657600080fd5b5061024f610573565b6040805160ff9092168252519081900360200190f35b34801561027157600080fd5b50d3801561027e57600080fd5b50d2801561028b57600080fd5b5061018760043561057c565b3480156102a357600080fd5b50d380156102b057600080fd5b50d280156102bd57600080fd5b506101ca600160a060020a03600435166105e2565b3480156102de57600080fd5b50d380156102eb57600080fd5b50d280156102f857600080fd5b50610187600160a060020a03600435166024356105f4565b34801561031c57600080fd5b50d3801561032957600080fd5b50d2801561033657600080fd5b506100d46106b3565b34801561034b57600080fd5b50d3801561035857600080fd5b50d2801561036557600080fd5b5061037d600160a060020a036004351660243561070d565b005b34801561038b57600080fd5b50d3801561039857600080fd5b50d280156103a557600080fd5b50604080516020600460443581810135601f8101849004840285018401909552848452610187948235600160a060020a031694602480359536959460649492019190819084018382808284375094975061071c9650505050505050565b34801561040e57600080fd5b50d3801561041b57600080fd5b50d2801561042857600080fd5b506101ca600160a060020a036004358116906024351661081f565b6000805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156104c95780601f1061049e576101008083540402835291602001916104c9565b820191906000526020600020905b8154815290600101906020018083116104ac57829003601f168201915b505050505081565b336000908152600560209081526040808320600160a060020a039590951683529390529190912055600190565b60035481565b600160a060020a038316600090815260056020908152604080832033845290915281205482111561053457600080fd5b600160a060020a038416600090815260056020908152604080832033845290915290208054839003905561056984848461083c565b5060019392505050565b60025460ff1681565b3360009081526004602052604081205482111561059857600080fd5b3360008181526004602090815260409182902080548690039055600380548690039055815185815291516000805160206109458339815191529281900390910190a2506001919050565b60046020526000908152604090205481565b600160a060020a03821660009081526004602052604081205482111561061957600080fd5b600160a060020a038316600090815260056020908152604080832033845290915290205482111561064957600080fd5b600160a060020a0383166000818152600460209081526040808320805487900390556005825280832033845282529182902080548690039055600380548690039055815185815291516000805160206109458339815191529281900390910190a250600192915050565b60018054604080516020600284861615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156104c95780601f1061049e576101008083540402835291602001916104c9565b61071833838361083c565b5050565b60008361072981856104d1565b156108175760405160e060020a638f4ffcb10281523360048201818152602483018790523060448401819052608060648501908152875160848601528751600160a060020a03871695638f4ffcb195948b94938b939192909160a490910190602085019080838360005b838110156107ab578181015183820152602001610793565b50505050905090810190601f1680156107d85780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b1580156107fa57600080fd5b505af115801561080e573d6000803e3d6000fd5b50505050600191505b509392505050565b600560209081526000928352604080842090915290825290205481565b6000600160a060020a038316151561085357600080fd5b600160a060020a03841660009081526004602052604090205482111561087857600080fd5b600160a060020a038316600090815260046020526040902054828101101561089f57600080fd5b50600160a060020a038083166000818152600460209081526040808320805495891680855282852080548981039091559486905281548801909155815187815291519390950194927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3600160a060020a0380841660009081526004602052604080822054928716825290205401811461093e57fe5b505050505600cc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5a165627a7a723058204e5a4088448d9024bd87ed8cc6e598a3aa522a31093e400297459449f5f59d810029" 32 | 33 | waitTime := 3 * time.Second 34 | 35 | mnemonic := "purse cheese cage reason cost flat jump usage hospital grit delay loan" 36 | hdw, err := wallet.NewHDWallet(mnemonic, "", wallet.BtcChainMainNet, wallet.ChainMainNet) 37 | require.NoError(t, err) 38 | 39 | wtmp, err := hdw.NewWallet(wallet.SymbolTrx, 0, 0, 0) 40 | require.NoError(t, err) 41 | w := wtmp.(*wallet.TrxWallet) 42 | fmt.Println(w.DeriveAddress()) 43 | 44 | //client, err := trx.NewTrxClient("grpc.trongrid.io:50051", "ac0823c5-adf0-4e8d-b2b6-322036a3b6e5") 45 | //client, err := trx.NewTrxClient("grpc.shasta.trongrid.io:50051", "") 46 | client, err := trx.NewTrxClient("127.0.0.1:50051", "") 47 | require.NoError(t, err) 48 | 49 | height, err := client.GetBlockHeight() 50 | require.NoError(t, err) 51 | fmt.Println("block height:", height) 52 | 53 | acct, err := client.RpcClient.GetAccount(w.DeriveAddress()) 54 | require.NoError(t, err) 55 | fmt.Println("Trx balance", acct.Balance, trx.SunToTrx(acct.Balance)) 56 | fmt.Println("Energy usage", acct.AccountResource.EnergyUsage) 57 | 58 | { // Trx transfer 59 | amount := int64(12000) 60 | fmt.Println("------------- transfer trx:", amount) 61 | _, err := trx.TransferTrx(w, client.RpcClient, "TUU9bqm9CCA1dAU9iaa6HcJF4twMBi5N86", amount) 62 | require.NoError(t, err) 63 | time.Sleep(waitTime) 64 | 65 | acct2, err := client.RpcClient.GetAccount(w.DeriveAddress()) 66 | require.NoError(t, err) 67 | fmt.Println("Trx balance after transfer:", acct2.Balance, trx.SunToTrx(acct2.Balance)) 68 | require.Greater(t, acct.Balance, acct2.Balance) 69 | } 70 | 71 | if acct.AccountResource.EnergyUsage < 100 { 72 | fmt.Println("------------- freeze balance for energy") 73 | _, err := trx.FreezeEnergyBalance(w, client.RpcClient, "", trx.TrxToSun(500)) 74 | require.NoError(t, err) 75 | time.Sleep(waitTime) 76 | } 77 | 78 | var trc20Addr string 79 | var baseTrc20Supply *big.Int 80 | { // deploy trc20 contract 81 | fmt.Println("------------- deploy trc20 token") 82 | txId, err := trx.DeployContract(w, client.RpcClient, "trx20test", 83 | TestTRC20Abi, TestTRC20ContractBytecode, 1e9, 20, 9e10) 84 | require.NoError(t, err) 85 | 86 | time.Sleep(waitTime) 87 | trc20Addr, err = trx.GetContractAddress(client.RpcClient, txId) 88 | require.NoError(t, err) 89 | fmt.Println("trc20 contract address:", trc20Addr) 90 | 91 | baseTrc20Supply, err = trx.NewErc20Contract(trc20Addr, client.RpcClient).BalanceOf(w.DeriveAddress()) 92 | require.NoError(t, err) 93 | fmt.Println("baseTrc20Supply:", baseTrc20Supply.String()) 94 | } 95 | 96 | { // trc20 transfer 97 | fmt.Println("------------- transfer trc20 token") 98 | to := "TSGYZ3VAVsa2SgoYhV7mfqnde89zTU7zNh" 99 | sendAmount := big.NewInt(50) 100 | contract := trx.NewErc20Contract(trc20Addr, client.RpcClient) 101 | fmt.Print("Contract name: ") 102 | fmt.Println(contract.Name()) 103 | fmt.Print("Contract symbol: ") 104 | fmt.Println(contract.Symbol()) 105 | fmt.Print("Contract decimals: ") 106 | fmt.Println(contract.Decimals()) 107 | 108 | txId, err := contract.Transfer(w, to, sendAmount, 999999) 109 | require.NoError(t, err) 110 | 111 | time.Sleep(waitTime) 112 | txinfo, err := client.RpcClient.GetTransactionInfoByID(txId) 113 | require.NoError(t, err) 114 | 115 | fmt.Println("Result:", txinfo.Result) 116 | fmt.Println("Block number:", txinfo.BlockNumber) 117 | fmt.Println("Fee:", txinfo.Fee) 118 | fmt.Println("Energy:", txinfo.Receipt.EnergyUsageTotal) 119 | fmt.Println("Receipt result:", txinfo.Receipt.Result) 120 | 121 | bal, err := contract.BalanceOf(w.DeriveAddress()) 122 | require.NoError(t, err) 123 | fmt.Println("bal", bal.String()) 124 | require.Equal(t, big.NewInt(0).Sub(baseTrc20Supply, sendAmount).Text(10), bal.Text(10)) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /qa/trx/trc20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | 3 | interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; } 4 | 5 | contract TokenTRC20 { 6 | // Public variables of the token 7 | string public name; 8 | string public symbol; 9 | uint8 public decimals = 3; 10 | // 18 decimals is the strongly suggested default, avoid changing it 11 | uint256 public totalSupply; 12 | 13 | // This creates an array with all balances 14 | mapping (address => uint256) public balanceOf; 15 | mapping (address => mapping (address => uint256)) public allowance; 16 | 17 | // This generates a public event on the blockchain that will notify clients 18 | event Transfer(address indexed from, address indexed to, uint256 value); 19 | 20 | // This notifies clients about the amount burnt 21 | event Burn(address indexed from, uint256 value); 22 | 23 | /** 24 | * Constructor function 25 | * 26 | * Initializes contract with initial supply tokens to the creator of the contract 27 | */ 28 | function TokenTRC20() public { 29 | totalSupply = 1000 * 10 ** uint256(decimals); // Update total supply with the decimal amount 30 | balanceOf[msg.sender] = totalSupply; // Give the creator all initial tokens 31 | name = "TTRC"; // Set the name for display purposes 32 | symbol = "TTRC"; // Set the symbol for display purposes 33 | } 34 | 35 | /** 36 | * Internal transfer, only can be called by this contract 37 | */ 38 | function _transfer(address _from, address _to, uint _value) internal { 39 | // Prevent transfer to 0x0 address. Use burn() instead 40 | require(_to != 0x0); 41 | // Check if the sender has enough 42 | require(balanceOf[_from] >= _value); 43 | // Check for overflows 44 | require(balanceOf[_to] + _value >= balanceOf[_to]); 45 | // Save this for an assertion in the future 46 | uint previousBalances = balanceOf[_from] + balanceOf[_to]; 47 | // Subtract from the sender 48 | balanceOf[_from] -= _value; 49 | // Add the same to the recipient 50 | balanceOf[_to] += _value; 51 | emit Transfer(_from, _to, _value); 52 | // Asserts are used to use static analysis to find bugs in your code. They should never fail 53 | assert(balanceOf[_from] + balanceOf[_to] == previousBalances); 54 | } 55 | 56 | /** 57 | * Transfer tokens 58 | * 59 | * Send `_value` tokens to `_to` from your account 60 | * 61 | * @param _to The address of the recipient 62 | * @param _value the amount to send 63 | */ 64 | function transfer(address _to, uint256 _value) public { 65 | _transfer(msg.sender, _to, _value); 66 | } 67 | 68 | /** 69 | * Transfer tokens from other address 70 | * 71 | * Send `_value` tokens to `_to` on behalf of `_from` 72 | * 73 | * @param _from The address of the sender 74 | * @param _to The address of the recipient 75 | * @param _value the amount to send 76 | */ 77 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { 78 | require(_value <= allowance[_from][msg.sender]); // Check allowance 79 | allowance[_from][msg.sender] -= _value; 80 | _transfer(_from, _to, _value); 81 | return true; 82 | } 83 | 84 | /** 85 | * Set allowance for other address 86 | * 87 | * Allows `_spender` to spend no more than `_value` tokens on your behalf 88 | * 89 | * @param _spender The address authorized to spend 90 | * @param _value the max amount they can spend 91 | */ 92 | function approve(address _spender, uint256 _value) public 93 | returns (bool success) { 94 | allowance[msg.sender][_spender] = _value; 95 | return true; 96 | } 97 | 98 | /** 99 | * Set allowance for other address and notify 100 | * 101 | * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it 102 | * 103 | * @param _spender The address authorized to spend 104 | * @param _value the max amount they can spend 105 | * @param _extraData some extra information to send to the approved contract 106 | */ 107 | function approveAndCall(address _spender, uint256 _value, bytes _extraData) 108 | public 109 | returns (bool success) { 110 | tokenRecipient spender = tokenRecipient(_spender); 111 | if (approve(_spender, _value)) { 112 | spender.receiveApproval(msg.sender, _value, this, _extraData); 113 | return true; 114 | } 115 | } 116 | 117 | /** 118 | * Destroy tokens 119 | * 120 | * Remove `_value` tokens from the system irreversibly 121 | * 122 | * @param _value the amount of money to burn 123 | */ 124 | function burn(uint256 _value) public returns (bool success) { 125 | require(balanceOf[msg.sender] >= _value); // Check if the sender has enough 126 | balanceOf[msg.sender] -= _value; // Subtract from the sender 127 | totalSupply -= _value; // Updates totalSupply 128 | emit Burn(msg.sender, _value); 129 | return true; 130 | } 131 | 132 | /** 133 | * Destroy tokens from other account 134 | * 135 | * Remove `_value` tokens from the system irreversibly on behalf of `_from`. 136 | * 137 | * @param _from the address of the sender 138 | * @param _value the amount of money to burn 139 | */ 140 | function burnFrom(address _from, uint256 _value) public returns (bool success) { 141 | require(balanceOf[_from] >= _value); // Check if the targeted balance is enough 142 | require(_value <= allowance[_from][msg.sender]); // Check allowance 143 | balanceOf[_from] -= _value; // Subtract from the targeted balance 144 | allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance 145 | totalSupply -= _value; // Update totalSupply 146 | emit Burn(_from, _value); 147 | return true; 148 | } 149 | } -------------------------------------------------------------------------------- /trx/account.go: -------------------------------------------------------------------------------- 1 | package trx 2 | 3 | import ( 4 | "github.com/lizc2003/gotron-sdk/pkg/client" 5 | "github.com/lizc2003/gotron-sdk/pkg/proto/core" 6 | "github.com/lizc2003/hdwallet/wallet" 7 | ) 8 | 9 | func FreezeEnergyBalance(w *wallet.TrxWallet, client *client.GrpcClient, delegateTo string, frozenBalance int64) (string, error) { 10 | txExt, err := client.FreezeBalance(w.DeriveAddress(), delegateTo, core.ResourceCode_ENERGY, frozenBalance) 11 | if err != nil { 12 | return "", err 13 | } 14 | return SignAndSendTx(w, client, txExt) 15 | } 16 | 17 | func UnfreezeEnergyBalance(w *wallet.TrxWallet, client *client.GrpcClient, delegateTo string) (string, error) { 18 | txExt, err := client.UnfreezeBalance(w.DeriveAddress(), delegateTo, core.ResourceCode_ENERGY) 19 | if err != nil { 20 | return "", err 21 | } 22 | return SignAndSendTx(w, client, txExt) 23 | } 24 | 25 | func FreezeBandwidthBalance(w *wallet.TrxWallet, client *client.GrpcClient, delegateTo string, frozenBalance int64) (string, error) { 26 | txExt, err := client.FreezeBalance(w.DeriveAddress(), delegateTo, core.ResourceCode_BANDWIDTH, frozenBalance) 27 | if err != nil { 28 | return "", err 29 | } 30 | return SignAndSendTx(w, client, txExt) 31 | } 32 | 33 | func UnfreezeBandwidthBalance(w *wallet.TrxWallet, client *client.GrpcClient, delegateTo string) (string, error) { 34 | txExt, err := client.UnfreezeBalance(w.DeriveAddress(), delegateTo, core.ResourceCode_BANDWIDTH) 35 | if err != nil { 36 | return "", err 37 | } 38 | return SignAndSendTx(w, client, txExt) 39 | } 40 | -------------------------------------------------------------------------------- /trx/contract.go: -------------------------------------------------------------------------------- 1 | package trx 2 | 3 | import ( 4 | "github.com/lizc2003/gotron-sdk/pkg/client" 5 | "github.com/lizc2003/gotron-sdk/pkg/contract" 6 | "github.com/lizc2003/hdwallet/wallet" 7 | ) 8 | 9 | func DeployContract(w *wallet.TrxWallet, client *client.GrpcClient, contractName string, 10 | jsonAbi string, bytecode string, 11 | feeLimit, consumeUserResourcePercent, originEnergyLimit int64) (string, error) { 12 | 13 | abi, err := contract.JSONtoABI(jsonAbi) 14 | if err != nil { 15 | return "", err 16 | } 17 | txExt, err := client.DeployContract(w.DeriveAddress(), contractName, abi, bytecode, 18 | feeLimit, consumeUserResourcePercent, originEnergyLimit) 19 | if err != nil { 20 | return "", err 21 | } 22 | 23 | return SignAndSendTx(w, client, txExt) 24 | } 25 | 26 | func GetContractAddress(client *client.GrpcClient, txId string) (string, error) { 27 | info, err := client.GetTransactionInfoByID(txId) 28 | if err != nil { 29 | return "", err 30 | } 31 | 32 | return EncodeAddress(info.ContractAddress), nil 33 | } 34 | -------------------------------------------------------------------------------- /trx/rpcclient.go: -------------------------------------------------------------------------------- 1 | package trx 2 | 3 | import ( 4 | "github.com/lizc2003/gotron-sdk/pkg/client" 5 | "google.golang.org/grpc" 6 | "time" 7 | ) 8 | 9 | type TrxClient struct { 10 | RpcClient *client.GrpcClient 11 | } 12 | 13 | func NewTrxClient(grpcHost string, apiKey string) (*TrxClient, error) { 14 | cli := client.NewGrpcClient(grpcHost) 15 | if apiKey != "" { 16 | cli.SetAPIKey(apiKey) 17 | } 18 | cli.SetTimeout(30 * time.Second) 19 | err := cli.Start(grpc.WithInsecure()) 20 | if err != nil { 21 | return nil, err 22 | } 23 | return &TrxClient{RpcClient: cli}, nil 24 | } 25 | 26 | func (this *TrxClient) GetBlockHeight() (int64, error) { 27 | b, err := this.RpcClient.GetNowBlock() 28 | if err != nil { 29 | return 0, err 30 | } 31 | return b.BlockHeader.RawData.Number, nil 32 | } 33 | 34 | /* 35 | func (this *TrxClient) GetAccount(addr string) (int64, error) { 36 | a, err := this.RpcClient.GetAccount(addr) 37 | if err != nil { 38 | return 0, err 39 | } 40 | 41 | return b.BlockHeader.RawData.Number, nil 42 | } 43 | */ 44 | /* 45 | const ( 46 | UrlVisible = "?visible=true" 47 | ) 48 | 49 | type TrxClient struct { 50 | httpClient *http.Client 51 | baseUrl string 52 | headers http.Header 53 | } 54 | 55 | func NewTrxClient(baseUrl string, apiKey string) (*TrxClient, error) { 56 | timeout := 30 * time.Second 57 | hc := &http.Client{ 58 | Timeout: timeout + 2*time.Second, 59 | Transport: &http.Transport{ 60 | MaxIdleConns: 200, 61 | MaxIdleConnsPerHost: 100, 62 | IdleConnTimeout: 60 * time.Second, 63 | DisableCompression: true, 64 | ResponseHeaderTimeout: timeout, 65 | DialContext: (&net.Dialer{ 66 | Timeout: 5 * time.Second, 67 | }).DialContext, 68 | }, 69 | } 70 | 71 | contentType := "application/json" 72 | headers := make(http.Header, 2) 73 | headers.Set("accept", contentType) 74 | headers.Set("content-type", contentType) 75 | if apiKey != "" { 76 | headers.Set("TRON-PRO-API-KEY", apiKey) 77 | } 78 | 79 | return &TrxClient{httpClient: hc, baseUrl: baseUrl, headers: headers}, nil 80 | } 81 | 82 | func (this *TrxClient) Call(method string, req interface{}, result interface{}) error { 83 | var err error 84 | var httpReq *http.Request 85 | 86 | if req == nil { 87 | httpReq, err = http.NewRequest("GET", this.baseUrl+method+UrlVisible, nil) 88 | } else { 89 | b, err := json.Marshal(req) 90 | if err != nil { 91 | return err 92 | } 93 | //fmt.Println(string(b)) 94 | 95 | httpReq, err = http.NewRequest(http.MethodPost, this.baseUrl+method, bytes.NewReader(b)) 96 | } 97 | if err != nil { 98 | return err 99 | } 100 | 101 | httpReq.Header = this.headers.Clone() 102 | resp, err := this.httpClient.Do(httpReq) 103 | if err != nil { 104 | return err 105 | } 106 | defer resp.Body.Close() 107 | body, err := ioutil.ReadAll(resp.Body) 108 | if err != nil { 109 | return err 110 | } 111 | fmt.Println(string(body)) 112 | 113 | if resp.StatusCode != http.StatusOK { 114 | return fmt.Errorf("trx request fail: %d, %s", resp.StatusCode, string(body)) 115 | } 116 | 117 | err = json.Unmarshal(body, &result) 118 | if err != nil { 119 | return err 120 | } 121 | return nil 122 | } 123 | 124 | func (this *TrxClient) GetNowBlock() (*core.Block, error) { 125 | var b core.Block 126 | err := this.Call("wallet/getnowblock", nil, &b) 127 | if err != nil { 128 | return nil, err 129 | } 130 | return &b, nil 131 | } 132 | */ 133 | -------------------------------------------------------------------------------- /trx/transact.go: -------------------------------------------------------------------------------- 1 | package trx 2 | 3 | import "C" 4 | import ( 5 | "crypto/sha256" 6 | "encoding/hex" 7 | "fmt" 8 | "github.com/ethereum/go-ethereum/crypto" 9 | "github.com/lizc2003/gotron-sdk/pkg/client" 10 | "github.com/lizc2003/gotron-sdk/pkg/common" 11 | "github.com/lizc2003/gotron-sdk/pkg/proto/api" 12 | "github.com/lizc2003/gotron-sdk/pkg/proto/core" 13 | "github.com/lizc2003/hdwallet/wallet" 14 | "google.golang.org/protobuf/proto" 15 | ) 16 | 17 | type TrxTransaction struct { 18 | tx *core.Transaction 19 | txId string 20 | } 21 | 22 | func NewTransaction(txExt *api.TransactionExtention) (*TrxTransaction, error) { 23 | return &TrxTransaction{tx: txExt.Transaction, txId: hex.EncodeToString(txExt.Txid)}, nil 24 | } 25 | 26 | func (this *TrxTransaction) TxHash() ([]byte, error) { 27 | rawData, err := proto.Marshal(this.tx.GetRawData()) 28 | if err != nil { 29 | return nil, err 30 | } 31 | txHash := sha256.Sum256(rawData) 32 | return txHash[:], nil 33 | } 34 | 35 | func (this *TrxTransaction) Sign(w *wallet.TrxWallet) error { 36 | txHash, err := this.TxHash() 37 | if err != nil { 38 | return err 39 | } 40 | signature, err := crypto.Sign(txHash, w.DeriveNativePrivateKey()) 41 | if err != nil { 42 | return err 43 | } 44 | this.tx.Signature = append(this.tx.Signature, signature) 45 | return nil 46 | } 47 | 48 | func (this *TrxTransaction) Send(client *client.GrpcClient) (string, error) { 49 | result, err := client.Broadcast(this.tx) 50 | if err != nil { 51 | return "", err 52 | } 53 | if result.Code != api.Return_SUCCESS { 54 | return "", fmt.Errorf("send transaction fail: %s, %s", result.Code.String(), string(result.GetMessage())) 55 | } 56 | 57 | h, _ := this.TxHash() 58 | return common.BytesToHexString(h), nil 59 | } 60 | 61 | func SignAndSendTx(w *wallet.TrxWallet, client *client.GrpcClient, txExt *api.TransactionExtention) (string, error) { 62 | tx, err := NewTransaction(txExt) 63 | if err != nil { 64 | return "", err 65 | } 66 | err = tx.Sign(w) 67 | if err != nil { 68 | return "", err 69 | } 70 | return tx.Send(client) 71 | } 72 | -------------------------------------------------------------------------------- /trx/transfer.go: -------------------------------------------------------------------------------- 1 | package trx 2 | 3 | import ( 4 | "github.com/lizc2003/gotron-sdk/pkg/client" 5 | "github.com/lizc2003/hdwallet/wallet" 6 | ) 7 | 8 | func TransferTrx(w *wallet.TrxWallet, client *client.GrpcClient, toAddr string, amount int64) (string, error) { 9 | txExt, err := client.Transfer(w.DeriveAddress(), toAddr, amount) 10 | if err != nil { 11 | return "", err 12 | } 13 | 14 | return SignAndSendTx(w, client, txExt) 15 | } 16 | -------------------------------------------------------------------------------- /trx/trc20.go: -------------------------------------------------------------------------------- 1 | package trx 2 | 3 | import ( 4 | "github.com/lizc2003/gotron-sdk/pkg/client" 5 | "github.com/lizc2003/hdwallet/wallet" 6 | "math/big" 7 | ) 8 | 9 | type Trc20Contract struct { 10 | contractAddress string 11 | client *client.GrpcClient 12 | } 13 | 14 | func NewErc20Contract(address string, client *client.GrpcClient) *Trc20Contract { 15 | return &Trc20Contract{contractAddress: address, client: client} 16 | } 17 | 18 | func (this *Trc20Contract) Name() (string, error) { 19 | return this.client.TRC20GetName(this.contractAddress) 20 | } 21 | 22 | func (this *Trc20Contract) Symbol() (string, error) { 23 | return this.client.TRC20GetSymbol(this.contractAddress) 24 | } 25 | 26 | func (this *Trc20Contract) Decimals() (int, error) { 27 | n, err := this.client.TRC20GetDecimals(this.contractAddress) 28 | if err != nil { 29 | return 0, err 30 | } 31 | return int(n.Int64()), nil 32 | } 33 | 34 | func (this *Trc20Contract) BalanceOf(tokenOwner string) (*big.Int, error) { 35 | return this.client.TRC20ContractBalance(tokenOwner, this.contractAddress) 36 | } 37 | 38 | func (this *Trc20Contract) Transfer(w *wallet.TrxWallet, toAddr string, amount *big.Int, feeLimit int64) (string, error) { 39 | txExt, err := this.client.TRC20Send(w.DeriveAddress(), toAddr, this.contractAddress, amount, feeLimit) 40 | if err != nil { 41 | return "", err 42 | } 43 | 44 | return SignAndSendTx(w, this.client, txExt) 45 | } 46 | -------------------------------------------------------------------------------- /trx/util.go: -------------------------------------------------------------------------------- 1 | package trx 2 | 3 | import ( 4 | "github.com/lizc2003/gotron-sdk/pkg/address" 5 | "github.com/lizc2003/gotron-sdk/pkg/common" 6 | "github.com/lizc2003/hdwallet/wallet" 7 | "math" 8 | ) 9 | 10 | func DecodeAddress(addr string) ([]byte, error) { 11 | return common.DecodeCheck(addr) 12 | } 13 | 14 | func EncodeAddress(a []byte) string { 15 | return address.Address(a).String() 16 | } 17 | 18 | func TrxToSun(v float64) int64 { 19 | return int64(math.Round(v * wallet.SunPerTrx)) 20 | } 21 | 22 | func SunToTrx(v int64) float64 { 23 | return float64(v) / wallet.SunPerTrx 24 | } 25 | -------------------------------------------------------------------------------- /wallet/common.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/btcsuite/btcd/chaincfg" 7 | "github.com/btcsuite/btcd/wire" 8 | "github.com/ethereum/go-ethereum/params" 9 | "github.com/tyler-smith/go-bip39" 10 | "math" 11 | "strconv" 12 | ) 13 | 14 | type SegWitType int 15 | 16 | const ( 17 | SymbolEth = "ETH" 18 | SymbolBtc = "BTC" 19 | SymbolTrx = "TRX" 20 | 21 | BtcChainMainNet = int(wire.MainNet) 22 | BtcChainTestNet3 = int(wire.TestNet3) 23 | BtcChainRegtest = int(wire.TestNet) 24 | BtcChainSimNet = int(wire.SimNet) 25 | 26 | ChainMainNet = 1 // for ETH 27 | ChainRopsten = 3 // for ETH 28 | ChainRinkeby = 4 // for ETH 29 | ChainGoerli = 5 // for ETH 30 | ChainHolesky = 17000 31 | ChainSepolia = 11155111 32 | ChainBsc = 56 // for Binance Smart Chain Mainnet 33 | ChainBscTestnet = 97 // for Binance Smart Chain Testnet 34 | ChainMatic = 137 // for Polygon Matic Mainnet 35 | ChainMaticTestnet = 80001 // for Polygon Matic Testnet 36 | ChainPrivate = 1337 // for ETH 37 | 38 | SegWitNone SegWitType = 0 39 | SegWitScript SegWitType = 1 40 | SegWitNative SegWitType = 2 41 | 42 | ChangeTypeExternal = 0 43 | ChangeTypeInternal = 1 // Usually used for change, not visible to the outside world 44 | 45 | SatoshiPerBitcoin = 1e8 46 | SunPerTrx = 1e6 47 | GweiPerEther = 1e9 48 | WeiPerGwei = 1e9 49 | WeiPerEther = 1e18 50 | 51 | EtherTransferGas = 21000 52 | 53 | TokenShowDecimals = 9 54 | ) 55 | 56 | var IsFixIssue172 = false 57 | 58 | func GetBtcChainParams(chainId int) (*chaincfg.Params, error) { 59 | switch chainId { 60 | case BtcChainMainNet: 61 | return &chaincfg.MainNetParams, nil 62 | case BtcChainTestNet3: 63 | return &chaincfg.TestNet3Params, nil 64 | case BtcChainRegtest: 65 | return &chaincfg.RegressionNetParams, nil 66 | case BtcChainSimNet: 67 | return &chaincfg.SimNetParams, nil 68 | default: 69 | return nil, fmt.Errorf("unknown btc chainId: %d", chainId) 70 | } 71 | } 72 | 73 | func GetEthChainParams(chainId int) (*params.ChainConfig, error) { 74 | switch chainId { 75 | case ChainMainNet: 76 | return params.MainnetChainConfig, nil 77 | //case ChainRopsten: 78 | // return params.RopstenChainConfig, nil 79 | //case ChainRinkeby: 80 | // return params.RinkebyChainConfig, nil 81 | case ChainGoerli: 82 | return params.GoerliChainConfig, nil 83 | case ChainHolesky: 84 | return params.HoleskyChainConfig, nil 85 | case ChainSepolia: 86 | return params.SepoliaChainConfig, nil 87 | case ChainMatic: 88 | return MaticChainConfig, nil 89 | case ChainMaticTestnet: 90 | return MaticTestnetChainConfig, nil 91 | case ChainBsc: 92 | return BscChainConfig, nil 93 | case ChainBscTestnet: 94 | return BscTestnetChainConfig, nil 95 | case ChainPrivate: 96 | return params.AllEthashProtocolChanges, nil 97 | default: 98 | return nil, fmt.Errorf("unknown eth chainId: %d", chainId) 99 | } 100 | } 101 | 102 | func NewEntropy(bits int) (entropy []byte, err error) { 103 | return bip39.NewEntropy(bits) 104 | } 105 | 106 | func NewMnemonic(bits int) (mnemonic string, err error) { 107 | entropy, err := NewEntropy(bits) 108 | if err != nil { 109 | return "", err 110 | } 111 | return NewMnemonicByEntropy(entropy) 112 | } 113 | 114 | func NewMnemonicByEntropy(entropy []byte) (mnemonic string, err error) { 115 | return bip39.NewMnemonic(entropy) 116 | } 117 | 118 | func EntropyFromMnemonic(mnemonic string) (entropy []byte, err error) { 119 | return bip39.EntropyFromMnemonic(mnemonic) 120 | } 121 | 122 | func NewSeedFromMnemonic(mnemonic, password string) ([]byte, error) { 123 | if mnemonic == "" { 124 | return nil, errors.New("mnemonic is required") 125 | } 126 | return bip39.NewSeedWithErrorChecking(mnemonic, password) 127 | } 128 | 129 | func MakeBip44Path(symbol string, chainId int, accountIndex, changeType, index int) (string, error) { 130 | return MakeBipXPath(44, symbol, chainId, accountIndex, changeType, index) 131 | } 132 | 133 | func MakeBip49Path(symbol string, chainId int, accountIndex, changeType, index int) (string, error) { 134 | return MakeBipXPath(49, symbol, chainId, accountIndex, changeType, index) 135 | } 136 | 137 | func MakeBip84Path(symbol string, chainId int, accountIndex, changeType, index int) (string, error) { 138 | return MakeBipXPath(84, symbol, chainId, accountIndex, changeType, index) 139 | } 140 | 141 | func MakeBipXPath(bipType int, symbol string, chainId int, accountIndex, changeType, index int) (string, error) { 142 | var coinType int 143 | switch symbol { 144 | case SymbolEth: 145 | coinType = 60 146 | case SymbolBtc: 147 | chainParams, err := GetBtcChainParams(chainId) 148 | if err != nil { 149 | return "", err 150 | } 151 | coinType = int(chainParams.HDCoinType) 152 | case SymbolTrx: 153 | coinType = 195 154 | default: 155 | return "", fmt.Errorf("invalid symbol: %s", symbol) 156 | } 157 | 158 | if accountIndex < 0 || index < 0 { 159 | return "", errors.New("invalid account index or index") 160 | } 161 | if changeType != ChangeTypeExternal && changeType != ChangeTypeInternal { 162 | return "", errors.New("invalid change type") 163 | } 164 | return fmt.Sprintf("m/%d'/%d'/%d'/%d/%d", bipType, coinType, accountIndex, changeType, index), nil 165 | } 166 | 167 | func FormatBtc(amount int64) string { 168 | return FormatFloat(float64(amount)/SatoshiPerBitcoin, 8) 169 | } 170 | 171 | func FormatEth(amount int64) string { 172 | return FormatFloat(float64(amount)/GweiPerEther, 9) 173 | } 174 | 175 | func FormatFloat(f float64, precision int) string { 176 | d := float64(1) 177 | if precision > 0 { 178 | d = math.Pow10(precision) 179 | } 180 | return strconv.FormatFloat(math.Trunc(f*d)/d, 'f', -1, 64) 181 | } 182 | -------------------------------------------------------------------------------- /wallet/ethchain.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/params" 5 | "math/big" 6 | ) 7 | 8 | var ( 9 | BscChainConfig = ¶ms.ChainConfig{} 10 | BscTestnetChainConfig = ¶ms.ChainConfig{} 11 | MaticChainConfig = ¶ms.ChainConfig{} 12 | MaticTestnetChainConfig = ¶ms.ChainConfig{} 13 | ) 14 | 15 | func init() { 16 | *MaticChainConfig = *params.AllEthashProtocolChanges 17 | MaticChainConfig.ChainID = big.NewInt(ChainMatic) 18 | *MaticTestnetChainConfig = *params.AllEthashProtocolChanges 19 | MaticTestnetChainConfig.ChainID = big.NewInt(ChainMaticTestnet) 20 | *BscChainConfig = *params.AllEthashProtocolChanges 21 | BscChainConfig.ChainID = big.NewInt(ChainBsc) 22 | *BscTestnetChainConfig = *params.AllEthashProtocolChanges 23 | BscTestnetChainConfig.ChainID = big.NewInt(ChainBscTestnet) 24 | } 25 | -------------------------------------------------------------------------------- /wallet/hdwallet.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type HDWallet struct { 9 | seed []byte 10 | btcChainId int 11 | ethChainId int 12 | } 13 | 14 | func NewHDWallet(mnemonic, password string, btcChainId int, ethChainId int) (*HDWallet, error) { 15 | mnemonic = strings.ReplaceAll(mnemonic, "\n", "") 16 | mnemonic = strings.ReplaceAll(mnemonic, "\r", "") 17 | 18 | seed, err := NewSeedFromMnemonic(mnemonic, password) 19 | if err != nil { 20 | return nil, err 21 | } 22 | return &HDWallet{seed: seed, btcChainId: btcChainId, ethChainId: ethChainId}, nil 23 | } 24 | 25 | func (this *HDWallet) NewWallet(symbol string, accountIndex, changeType, index int) (Wallet, error) { 26 | path, err := MakeBip44Path(symbol, this.btcChainId, accountIndex, changeType, index) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | return this.NewWalletByPath(symbol, path, SegWitNone) 32 | } 33 | 34 | func (this *HDWallet) NewSegWitWallet(accountIndex, changeType, index int) (Wallet, error) { 35 | path, err := MakeBip49Path(SymbolBtc, this.btcChainId, accountIndex, changeType, index) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return this.NewWalletByPath(SymbolBtc, path, SegWitScript) 40 | } 41 | 42 | func (this *HDWallet) NewNativeSegWitWallet(accountIndex, changeType, index int) (Wallet, error) { 43 | path, err := MakeBip84Path(SymbolBtc, this.btcChainId, accountIndex, changeType, index) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return this.NewWalletByPath(SymbolBtc, path, SegWitNative) 48 | } 49 | 50 | func (this *HDWallet) NewWalletByPath(symbol string, path string, segWitType SegWitType) (Wallet, error) { 51 | var w Wallet 52 | var err error 53 | 54 | switch symbol { 55 | case SymbolBtc: 56 | w, err = NewBtcWalletByPath(path, this.seed, this.btcChainId, segWitType) 57 | case SymbolEth: 58 | w, err = NewEthWalletByPath(path, this.seed, this.ethChainId) 59 | case SymbolTrx: 60 | w, err = NewTrxWalletByPath(path, this.seed) 61 | default: 62 | err = fmt.Errorf("invalid symbol: %s", symbol) 63 | } 64 | 65 | if err != nil { 66 | return nil, err 67 | } 68 | return w, nil 69 | } 70 | -------------------------------------------------------------------------------- /wallet/wallet.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | type Wallet interface { 4 | ChainId() int 5 | Symbol() string 6 | 7 | DeriveAddress() string 8 | DerivePublicKey() string 9 | DerivePrivateKey() string 10 | } 11 | -------------------------------------------------------------------------------- /wallet/wallet_btc.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "github.com/btcsuite/btcd/btcec/v2" 7 | "github.com/btcsuite/btcd/btcutil" 8 | "github.com/btcsuite/btcd/btcutil/hdkeychain" 9 | "github.com/btcsuite/btcd/chaincfg" 10 | "github.com/btcsuite/btcd/txscript" 11 | "github.com/ethereum/go-ethereum/accounts" 12 | "log" 13 | ) 14 | 15 | var ErrAddressNotMatch = errors.New("address not match") 16 | 17 | type BtcWallet struct { 18 | symbol string 19 | segWitType SegWitType 20 | chainParams *chaincfg.Params 21 | privateKey *btcec.PrivateKey 22 | publicKey *btcec.PublicKey 23 | } 24 | 25 | func NewBtcWallet(privateKey string, chainId int, segWitType SegWitType) (*BtcWallet, error) { 26 | chainParams, err := GetBtcChainParams(chainId) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | wif, err := btcutil.DecodeWIF(privateKey) 32 | if err != nil { 33 | return nil, err 34 | } 35 | if !wif.IsForNet(chainParams) { 36 | return nil, errors.New("key network doesn't match") 37 | } 38 | 39 | return &BtcWallet{symbol: SymbolBtc, 40 | chainParams: chainParams, segWitType: segWitType, 41 | privateKey: wif.PrivKey, 42 | publicKey: wif.PrivKey.PubKey()}, nil 43 | } 44 | 45 | func NewBtcWalletByPath(path string, seed []byte, chainId int, segWitType SegWitType) (*BtcWallet, error) { 46 | chainParams, err := GetBtcChainParams(chainId) 47 | if err != nil { 48 | return nil, err 49 | } 50 | masterKey, err := hdkeychain.NewMaster(seed, chainParams) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | privateKey, err := DerivePrivateKeyByPath(masterKey, path, IsFixIssue172) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | return &BtcWallet{symbol: SymbolBtc, 61 | chainParams: chainParams, segWitType: segWitType, 62 | privateKey: privateKey, 63 | publicKey: privateKey.PubKey()}, nil 64 | } 65 | 66 | func (w *BtcWallet) ChainId() int { 67 | return int(w.chainParams.Net) 68 | } 69 | 70 | func (w *BtcWallet) ChainParams() *chaincfg.Params { 71 | return w.chainParams 72 | } 73 | 74 | func (w *BtcWallet) Symbol() string { 75 | return w.symbol 76 | } 77 | 78 | func (w *BtcWallet) DeriveAddress() string { 79 | addr := w.DeriveNativeAddress() 80 | if addr != nil { 81 | return addr.EncodeAddress() 82 | } 83 | return "" 84 | } 85 | 86 | func (w *BtcWallet) DerivePublicKey() string { 87 | return hex.EncodeToString(w.publicKey.SerializeCompressed()) 88 | } 89 | 90 | func (w *BtcWallet) DerivePrivateKey() string { 91 | wif, err := btcutil.NewWIF(w.privateKey, w.chainParams, true) 92 | if err != nil { 93 | log.Println("DerivePrivateKey error:", err) 94 | return "" 95 | } 96 | return wif.String() 97 | } 98 | 99 | func (w *BtcWallet) DeriveNativeAddress() btcutil.Address { 100 | switch w.segWitType { 101 | case SegWitNone: 102 | pk := w.publicKey.SerializeCompressed() 103 | keyHash := btcutil.Hash160(pk) 104 | p2pkhAddr, err := btcutil.NewAddressPubKeyHash(keyHash, w.chainParams) 105 | if err != nil { 106 | log.Println("DeriveAddress error:", err) 107 | return nil 108 | } 109 | return p2pkhAddr 110 | case SegWitScript: 111 | pk := w.publicKey.SerializeCompressed() 112 | keyHash := btcutil.Hash160(pk) 113 | scriptSig, err := txscript.NewScriptBuilder().AddOp(txscript.OP_0).AddData(keyHash).Script() 114 | if err != nil { 115 | log.Println("DeriveAddress error:", err) 116 | return nil 117 | } 118 | addr, err := btcutil.NewAddressScriptHash(scriptSig, w.chainParams) 119 | if err != nil { 120 | log.Println("DeriveAddress error:", err) 121 | return nil 122 | } 123 | return addr 124 | case SegWitNative: 125 | pk := w.publicKey.SerializeCompressed() 126 | keyHash := btcutil.Hash160(pk) 127 | p2wpkh, err := btcutil.NewAddressWitnessPubKeyHash(keyHash, w.chainParams) 128 | if err != nil { 129 | log.Println("DeriveAddress error:", err) 130 | return nil 131 | } 132 | return p2wpkh 133 | } 134 | return nil 135 | } 136 | 137 | func (w *BtcWallet) DeriveNativePrivateKey() *btcec.PrivateKey { 138 | return w.privateKey 139 | } 140 | 141 | func DerivePrivateKeyByPath(masterKey *hdkeychain.ExtendedKey, path string, fixIssue172 bool) (*btcec.PrivateKey, error) { 142 | dpath, err := accounts.ParseDerivationPath(path) 143 | if err != nil { 144 | return nil, err 145 | } 146 | 147 | key := masterKey 148 | for _, n := range dpath { 149 | if fixIssue172 && key.IsAffectedByIssue172() { 150 | key, err = key.Derive(n) 151 | } else { 152 | key, err = key.DeriveNonStandard(n) 153 | } 154 | if err != nil { 155 | return nil, err 156 | } 157 | } 158 | 159 | privateKey, err := key.ECPrivKey() 160 | if err != nil { 161 | return nil, err 162 | } 163 | return privateKey, nil 164 | } 165 | 166 | // txauthor.SecretsSource 167 | func (w *BtcWallet) GetKey(addr btcutil.Address) (*btcec.PrivateKey, bool, error) { 168 | if w.DeriveAddress() == addr.EncodeAddress() { 169 | return w.privateKey, true, nil 170 | } 171 | return nil, false, ErrAddressNotMatch 172 | } 173 | 174 | func (w *BtcWallet) GetScript(addr btcutil.Address) ([]byte, error) { 175 | return nil, errors.New("GetScript not supported") 176 | } 177 | -------------------------------------------------------------------------------- /wallet/wallet_eth.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "encoding/hex" 6 | "errors" 7 | "github.com/btcsuite/btcd/btcutil/hdkeychain" 8 | "github.com/btcsuite/btcd/chaincfg" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/crypto" 11 | "github.com/ethereum/go-ethereum/params" 12 | ) 13 | 14 | type EthWallet struct { 15 | symbol string 16 | chainId int 17 | chainParams *params.ChainConfig 18 | privateKey *ecdsa.PrivateKey 19 | publicKey *ecdsa.PublicKey 20 | } 21 | 22 | func NewEthWallet(privateKey string, chainId int) (*EthWallet, error) { 23 | chainParams, err := GetEthChainParams(chainId) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | privKey, err := crypto.HexToECDSA(privateKey) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | publicKey, err := DerivePublicKey(privKey) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | return &EthWallet{symbol: SymbolEth, 39 | chainId: chainId, chainParams: chainParams, 40 | privateKey: privKey, publicKey: publicKey}, nil 41 | } 42 | 43 | func NewEthWalletByPath(path string, seed []byte, chainId int) (*EthWallet, error) { 44 | chainParams, err := GetEthChainParams(chainId) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | privKey, err := DerivePrivateKeyByPath(masterKey, path, IsFixIssue172) 55 | if err != nil { 56 | return nil, err 57 | } 58 | privateKey := privKey.ToECDSA() 59 | 60 | publicKey, err := DerivePublicKey(privateKey) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | return &EthWallet{symbol: SymbolEth, 66 | chainId: chainId, chainParams: chainParams, 67 | privateKey: privateKey, publicKey: publicKey}, nil 68 | } 69 | 70 | func (w *EthWallet) ChainId() int { 71 | return w.chainId 72 | } 73 | 74 | func (w *EthWallet) ChainParams() *params.ChainConfig { 75 | return w.chainParams 76 | } 77 | 78 | func (w *EthWallet) Symbol() string { 79 | return w.symbol 80 | } 81 | 82 | func (w *EthWallet) DeriveAddress() string { 83 | return crypto.PubkeyToAddress(*w.publicKey).Hex() 84 | } 85 | 86 | func (w *EthWallet) DerivePublicKey() string { 87 | return hex.EncodeToString(crypto.FromECDSAPub(w.publicKey)) 88 | } 89 | 90 | func (w *EthWallet) DerivePrivateKey() string { 91 | return hex.EncodeToString(crypto.FromECDSA(w.privateKey)) 92 | } 93 | 94 | func (w *EthWallet) DeriveNativeAddress() common.Address { 95 | return crypto.PubkeyToAddress(*w.publicKey) 96 | } 97 | 98 | func (w *EthWallet) DeriveNativePrivateKey() *ecdsa.PrivateKey { 99 | return w.privateKey 100 | } 101 | 102 | func DerivePublicKey(privateKey *ecdsa.PrivateKey) (*ecdsa.PublicKey, error) { 103 | publicKey := privateKey.Public() 104 | publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) 105 | if !ok { 106 | return nil, errors.New("failed to get public key") 107 | } 108 | 109 | return publicKeyECDSA, nil 110 | } 111 | -------------------------------------------------------------------------------- /wallet/wallet_test.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | ) 8 | 9 | func TestCoin_MakeAddress(t *testing.T) { 10 | mnemonic, err := NewMnemonic(256) 11 | require.NoError(t, err) 12 | t.Log(mnemonic) 13 | 14 | btcChainId := BtcChainMainNet 15 | ethChainId := ChainMainNet 16 | 17 | hdw, err := NewHDWallet(mnemonic, "", btcChainId, ethChainId) 18 | require.NoError(t, err) 19 | 20 | w, err := hdw.NewWallet(SymbolBtc, 0, 0, 0) 21 | require.NoError(t, err) 22 | 23 | privateKey := w.DerivePrivateKey() 24 | publicKey := w.DerivePublicKey() 25 | address := w.DeriveAddress() 26 | t.Log(privateKey) 27 | t.Log(publicKey) 28 | t.Log(address) 29 | 30 | w2, err := NewBtcWallet(privateKey, btcChainId, SegWitNone) 31 | require.NoError(t, err) 32 | privateKey2 := w2.DerivePrivateKey() 33 | publicKey2 := w2.DerivePublicKey() 34 | address2 := w2.DeriveAddress() 35 | require.Equal(t, privateKey, privateKey2) 36 | require.Equal(t, publicKey, publicKey2) 37 | require.Equal(t, address, address2) 38 | 39 | w, err = hdw.NewWallet(SymbolEth, 0, 0, 0) 40 | require.NoError(t, err) 41 | 42 | privateKey = w.DerivePrivateKey() 43 | publicKey = w.DerivePublicKey() 44 | address = w.DeriveAddress() 45 | t.Log(privateKey) 46 | t.Log(publicKey) 47 | t.Log(address) 48 | 49 | w3, err := NewEthWallet(privateKey, ethChainId) 50 | require.NoError(t, err) 51 | privateKey3 := w3.DerivePrivateKey() 52 | publicKey3 := w3.DerivePublicKey() 53 | address3 := w3.DeriveAddress() 54 | require.Equal(t, privateKey, privateKey3) 55 | require.Equal(t, publicKey, publicKey3) 56 | require.Equal(t, address, address3) 57 | } 58 | 59 | func TestCoin_MakeAddressTestnet(t *testing.T) { 60 | mnemonic := "purse cheese cage reason cost flat jump usage hospital grit delay loan" 61 | hdw, err := NewHDWallet(mnemonic, "", BtcChainTestNet3, ChainGoerli) 62 | require.NoError(t, err) 63 | 64 | w, err := hdw.NewWallet(SymbolBtc, 0, 0, 0) 65 | require.NoError(t, err) 66 | 67 | privateKey := w.DerivePrivateKey() 68 | publicKey := w.DerivePublicKey() 69 | address := w.DeriveAddress() 70 | require.Equal(t, privateKey, "cUpt1n6hoxtYWGNX4jiTGA31nroMgFXFsYzmHs8cvDfCnGLjH5MV") 71 | require.Equal(t, publicKey, "03e8a1810e3063ccd7a3e1f40195e13dcb0678e0097433715e9cf8e31fce753b10") 72 | require.Equal(t, address, "mt1HJYiAki1FDfGh8M7MEeoKc8BKJ4hhc6") 73 | 74 | w, err = hdw.NewWallet(SymbolBtc, 0, 0, 1) 75 | require.NoError(t, err) 76 | 77 | privateKey = w.DerivePrivateKey() 78 | publicKey = w.DerivePublicKey() 79 | address = w.DeriveAddress() 80 | require.Equal(t, privateKey, "cS2B6pjUu6gdwWCHuPdFuCz1xWh47TGqnqXnvCAu8vfbf9a6fyGv") 81 | require.Equal(t, publicKey, "02d5a38f5f39dff2c8b57b0950acc285822cc1d9efca6c80abe1f0a92f90bfcc1b") 82 | require.Equal(t, address, "n2BVx6LNcFkSoqxc7EatFAByoZXwmvztxJ") 83 | 84 | w, err = hdw.NewWallet(SymbolEth, 0, 0, 0) 85 | require.NoError(t, err) 86 | 87 | privateKey = w.DerivePrivateKey() 88 | publicKey = w.DerivePublicKey() 89 | address = w.DeriveAddress() 90 | require.Equal(t, privateKey, "e16ac20fafb7de15445488f1fc6a0e5a05e9efca52acb15de559e4914c8f351d") 91 | require.Equal(t, publicKey, "040861286e63b01682f3769628934292819d37dbdacb48994ebde5d54ddc4146dccf61b578513d0a27c1970984e6def2d31832ff5f5df7b16fe16bf4b2b87d003c") 92 | require.Equal(t, address, "0x942429aA212ef7cb14DfE06ed0EE78EB82BD298f") 93 | 94 | w, err = hdw.NewWallet(SymbolEth, 0, 0, 1) 95 | require.NoError(t, err) 96 | 97 | privateKey = w.DerivePrivateKey() 98 | publicKey = w.DerivePublicKey() 99 | address = w.DeriveAddress() 100 | require.Equal(t, privateKey, "63191752898f7a7f20caa49da570d67e4fc0c2623a92c2fe3401d427a2de77aa") 101 | require.Equal(t, publicKey, "042740014857de4394a29515a7a80f192e5a335440d20ad73d662901e7f82720f6060b63b470a5a31ec45deba053b259966e3fed3c62b0fd6371abb4120d53555e") 102 | require.Equal(t, address, "0x3295Db1E775723c752511c4E4caA400dbaf2240F") 103 | 104 | w, err = hdw.NewWallet(SymbolTrx, 0, 0, 0) 105 | require.NoError(t, err) 106 | 107 | privateKey = w.DerivePrivateKey() 108 | publicKey = w.DerivePublicKey() 109 | address = w.DeriveAddress() 110 | require.Equal(t, privateKey, "9023544417f445b68ea3e334dea93337b13b562db1914d5e5a658e00803efc46") 111 | require.Equal(t, publicKey, "047a70595cb622ac822893f9cb5acc64c59286c7afd3151b8ebd094b3bf1041e6e35f0f5f3d919b7ff3af2a526f98b9bd5f0ef30d9739f29be5aa667cf7cedaf07") 112 | require.Equal(t, address, "TR1Sxne61QPnjk5o7p38JDs15fcxirSEk1") 113 | } 114 | 115 | func TestCoin_SegWitAddress(t *testing.T) { 116 | mnemonic := "example escape erode educate help cigar super chalk best inner fossil soft" 117 | hdw, err := NewHDWallet(mnemonic, "", BtcChainMainNet, ChainMainNet) 118 | require.NoError(t, err) 119 | 120 | w, err := hdw.NewSegWitWallet(0, 0, 0) 121 | require.NoError(t, err) 122 | privateKey := w.DerivePrivateKey() 123 | publicKey := w.DerivePublicKey() 124 | witAddress := w.DeriveAddress() 125 | t.Log(privateKey) 126 | t.Log(publicKey) 127 | t.Log(witAddress) 128 | require.Equal(t, witAddress, "32dBJzEpeCnuk5ti8Pa2o232KYHV2WGmkc") 129 | 130 | w, err = hdw.NewNativeSegWitWallet(0, 0, 0) 131 | require.NoError(t, err) 132 | privateKey = w.DerivePrivateKey() 133 | publicKey = w.DerivePublicKey() 134 | witAddress = w.DeriveAddress() 135 | t.Log(" ") 136 | t.Log(privateKey) 137 | t.Log(publicKey) 138 | t.Log(witAddress) 139 | require.Equal(t, witAddress, "bc1qtmygf2u2n3wdgepgtfxvzy4xr83gzc9cyqf594") 140 | } 141 | 142 | func TestCoin_DeriveAddress(t *testing.T) { 143 | const mnemonic = "eternal list thank chaos trick paper sniff ridge make govern invest abandon" 144 | 145 | hdw, err := NewHDWallet(mnemonic, "lixu1234qwer", BtcChainTestNet3, ChainGoerli) 146 | require.NoError(t, err) 147 | 148 | for i, tt := range []struct { 149 | privateKey string 150 | publicKey string 151 | address string 152 | }{ 153 | { 154 | privateKey: "cVfZerHznFjwSC85exvS1B9xpqU2yb6piLHQmUN5DnsHek3JV7xn", 155 | publicKey: "031cf3493c5fcb4eabdfaa4191a02cc30429539ea6b80f5590bc4a8b6222f0d3ba", 156 | address: "mm16s7xsf8Wjwxhprc6YzLW9gVncqZNGBR", 157 | }, 158 | { 159 | privateKey: "cMf15hojxybmvPfkBD3pGVKKHj4anSqD4SxuH9egUxKmhGDPBcLh", 160 | publicKey: "02d3c0c2de32c8923be88c45aa56bf30aebd428feb13e578e946c64668425b82ee", 161 | address: "n23dZRLMrWF419JNe28WWCTatcNBe3PCA9", 162 | }, 163 | { 164 | privateKey: "cPpmQ8iDoj142QwQb2xBuK2ScCR6N2LKQBEATbSn5pTrpaKnZ6TB", 165 | publicKey: "03bf34a6d18b36459432644fd3a41a9b5ae985c95ee8627613366ec7bd13ea0af4", 166 | address: "mzxzKR9Qz3zmQmRpKChRuDAS6WSiiuscGM", 167 | }, 168 | } { 169 | w, err := hdw.NewWallet(SymbolBtc, 0, 0, i) 170 | require.NoError(t, err) 171 | 172 | privateKey := w.DerivePrivateKey() 173 | publicKey := w.DerivePublicKey() 174 | address := w.DeriveAddress() 175 | require.Equal(t, privateKey, tt.privateKey) 176 | require.Equal(t, publicKey, tt.publicKey) 177 | require.Equal(t, address, tt.address) 178 | } 179 | 180 | hdw, err = NewHDWallet(mnemonic, "lixu1234qwer", BtcChainMainNet, ChainMainNet) 181 | require.NoError(t, err) 182 | 183 | for i, tt := range []struct { 184 | privateKey string 185 | publicKey string 186 | address string 187 | }{ 188 | { 189 | privateKey: "L1evYPBu2pWjWCKkKmy5aYcvNSx1FYqhZjJDepQTVmE38RAFAboN", 190 | publicKey: "0209f66c7e3f0c42c82dcc17fd6ccd51b3a01ec55d2c3dadd0b7c5456dcaee97ff", 191 | address: "1Gh3TUC3WLhB4t6Km9fSPxJLVX4dUBFiVD", 192 | }, 193 | { 194 | privateKey: "L5EhohWQ2rm5a1aF27akRtfZTXfmHVt2Vhxrs1C6s8RS6JonVr1X", 195 | publicKey: "0263b1066c8626630e19c9d3ccd115fc808a791567b2b6abcdf9864fb62aaca9ae", 196 | address: "1H7JDuCF8QBcy28GUgNvB9wscdjw1ZrrPc", 197 | }, 198 | { 199 | privateKey: "KwX6JPubzTxQRLLS6tVDjX2aAwE4nDAqSGJoaPxqHf3EowybHRbd", 200 | publicKey: "034968dc4f6e35ff7d478edcd17af1eaedfda97bcc39545129e725e5c6f164c343", 201 | address: "12AxjDAM6fhPsf66gEiFMYcY3uSdSUi9Zj", 202 | }, 203 | } { 204 | w, err := hdw.NewWallet(SymbolBtc, 0, 0, i) 205 | require.NoError(t, err) 206 | 207 | privateKey := w.DerivePrivateKey() 208 | publicKey := w.DerivePublicKey() 209 | address := w.DeriveAddress() 210 | require.Equal(t, privateKey, tt.privateKey) 211 | require.Equal(t, publicKey, tt.publicKey) 212 | require.Equal(t, address, tt.address) 213 | } 214 | } 215 | 216 | func TestNewMnemonic(t *testing.T) { 217 | mn, err := NewMnemonic(128) 218 | assert.NoError(t, err) 219 | en, err := EntropyFromMnemonic(mn) 220 | assert.NoError(t, err) 221 | mn1, err := NewMnemonicByEntropy(en) 222 | assert.NoError(t, err) 223 | assert.EqualValues(t, mn, mn1) 224 | } 225 | -------------------------------------------------------------------------------- /wallet/wallet_trx.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "encoding/hex" 6 | "github.com/btcsuite/btcd/btcutil/base58" 7 | "github.com/btcsuite/btcd/btcutil/hdkeychain" 8 | "github.com/btcsuite/btcd/chaincfg" 9 | "github.com/ethereum/go-ethereum/crypto" 10 | ) 11 | 12 | type TrxWallet struct { 13 | symbol string 14 | privateKey *ecdsa.PrivateKey 15 | publicKey *ecdsa.PublicKey 16 | } 17 | 18 | func NewTrxWallet(privateKey string) (*TrxWallet, error) { 19 | privKey, err := crypto.HexToECDSA(privateKey) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | publicKey, err := DerivePublicKey(privKey) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | return &TrxWallet{symbol: SymbolTrx, 30 | privateKey: privKey, publicKey: publicKey}, nil 31 | } 32 | 33 | func NewTrxWalletByPath(path string, seed []byte) (*TrxWallet, error) { 34 | masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | privKey, err := DerivePrivateKeyByPath(masterKey, path, false) 40 | if err != nil { 41 | return nil, err 42 | } 43 | privateKey := privKey.ToECDSA() 44 | 45 | publicKey, err := DerivePublicKey(privateKey) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | return &TrxWallet{symbol: SymbolTrx, 51 | privateKey: privateKey, publicKey: publicKey}, nil 52 | } 53 | 54 | func (w *TrxWallet) ChainId() int { 55 | return 0 56 | } 57 | 58 | func (w *TrxWallet) Symbol() string { 59 | return w.symbol 60 | } 61 | 62 | func (w *TrxWallet) DeriveAddress() string { 63 | const addressPrefix = 0x41 64 | return base58.CheckEncode(crypto.PubkeyToAddress(*w.publicKey).Bytes(), addressPrefix) 65 | } 66 | 67 | func (w *TrxWallet) DerivePublicKey() string { 68 | return hex.EncodeToString(crypto.FromECDSAPub(w.publicKey)) 69 | } 70 | 71 | func (w *TrxWallet) DerivePrivateKey() string { 72 | return hex.EncodeToString(crypto.FromECDSA(w.privateKey)) 73 | } 74 | 75 | func (w *TrxWallet) DeriveNativePrivateKey() *ecdsa.PrivateKey { 76 | return w.privateKey 77 | } 78 | --------------------------------------------------------------------------------