以太坊批量发送代币(go语言实现)

main.go
主要是构造一个交易

package main

import (
	"crypto/ecdsa"
	"encoding/hex"
	"fmt"
	"math/big"
	"os"
	"strconv"
	"time"

	"github.com/Luxurioust/excelize"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/multivactech/MultiVAC/rlp"
)

//if isTest is true,if will send transaction to respropsten
const isTest = true

//Set your private key
var myPrikey string = ""

//Set your address
var myAccount string = "0x6Ed230dB6803551917F268a55dB4708c0AbEd7F3"

//Set constarct Address
var constractAddr = "0x85437e56772a863d5a8661938156b6dce5a01652"
var gasLimit uint64 = 300000
var gasPrice int64 = 50000000000

var apikey = "DM692A9ZMX7WRFRNREY1ES9J8WSIXRA4AR"

const MAXBIT = 64
const accountsPath = "./test.xlsx"
const txsPath = "./txFilePath.txt"

var funcName = "a9059cbb"

func main() {

	count := queryTransCount(myAccount, isTest)
	//var count uint64 = 1489835
	var chainId int64
	allAccount := getAllAddr(accountsPath)
	//var count uint64 = 0
	fileName := txsPath
	dstFile, err := os.Create(fileName)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer dstFile.Close()

	for i, account := range allAccount {
		fmt.Printf("Start transform token to the %d's account.\n", i+1)
		if !isValidAddr(account.account) {
			continue
		}
		trans := newTrans(count, constractAddr, 0, gasLimit, gasPrice, getInputData(account.account, int64(account.value)))
		privateKey := generatePrikey(myPrikey)
		if isTest {
			chainId = 3
		} else {
			chainId = 1
		}
		signType := setSignType(chainId)
		signData := signTx(trans, signType, privateKey)
		if signData == nil {
			continue
		}
		rlpData, err := rlp.EncodeToBytes(signData)
		if err != nil {
			fmt.Printf("encode data err:%s\n", err)
		}
		rawTrans := hex.EncodeToString(rlpData)
		//fmt.Println(rawTrans)
		time.Sleep(time.Second * 10)
		if i != 0 && i%10 == 0 {
			time.Sleep(time.Second * 30)
		}
		txHash, err := sendRawTrans(i, rawTrans, isTest)
		if err == nil {
			dstFile.WriteString(strconv.Itoa(i) + ":" + txHash + "\n")
		} else {
			writeErrorToFile(i, account.account, txHash)
		}
		count++
	}

}

func isValidAddr(addr string) bool {
	if _, err := hex.DecodeString(addr); err != nil {
		fmt.Println("hex decode ", err)
		return false
	} else if len(addr) != 40 {
		fmt.Println("Length error!")
		fmt.Println(len(addr))
		return false
	}
	return true
}

//这里我的账户和对应转账金额是保存在excel中的,你可以根据你的需要来更改
func getAllAddr(path string) []accountAndValue {
	var allAccount = make([]accountAndValue, 0)

	xlsx, err := excelize.OpenFile(path)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	// Get all the rows in a sheet.
	rows, _ := xlsx.GetRows("sheet1")
	for _, row := range rows {
		var res [2]string
		for i, colCell := range row {
			if colCell == "" {
				break
			}
			res[i] = colCell
		}
		if res[0] == "" {
			return allAccount
		}
		account := res[0][2:]
		value, err := strconv.Atoi(res[1])
		if err != nil {
			fmt.Println("value to int err:", err)
			continue
		}
		av := accountAndValue{
			account: account,
			value:   value,
		}
		allAccount = append(allAccount, av)
	}

	return allAccount
}

//这里是因为我的原始数据中的金额是gwei单位,所以还需要加18个零
//另外原始数据是10进制的,需要转换为16进制
func getInputData(to string, value int64) string {
	strvalue := strconv.Itoa(int(value)) + "000000000000000000"
	bigValue, _ := new(big.Int).SetString(strvalue, 10)
	str := fmt.Sprintf("%x", bigValue)
	inputData := funcName + to64Bit(to) + to64Bit(str)
	return inputData
}

//构造一个交易
func newTrans(nonce uint64, to string, amount int64, gasLimit uint64, gasPrice int64, input string) *types.Transaction {
	return types.NewTransaction(nonce, common.HexToAddress(to), big.NewInt(amount), gasLimit, big.NewInt(gasPrice), common.Hex2Bytes(input))
}

func generatePrikey(pri string) *ecdsa.PrivateKey {
	pr, _ := crypto.HexToECDSA(pri)
	return pr
}

func setSignType(index int64) types.Signer {
	return types.NewEIP155Signer(big.NewInt(index))
}

func signTx(trans *types.Transaction, signType types.Signer, privateKey *ecdsa.PrivateKey) *types.Transaction {
	signData, err := types.SignTx(trans, signType, privateKey)
	if err != nil {
		fmt.Println("sign transaction err:", err)
		return nil
	} else {
		return signData
	}

}

func to64Bit(data string) string {
	length := len(data)
	for i := length; i < MAXBIT; i++ {
		data = "0" + data
	}
	return data
}

//发送过程中,如果出现错误会将错误信息记录在这里
func writeErrorToFile(index int, address string, errMsg string) error {
	dstFile, err := os.OpenFile(failedTransPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Printf("open %s error:%s\n", failedTransPath, err)
		return err
	}
	defer dstFile.Close()
	dstFile.WriteString(strconv.Itoa(index) + " account : " + address + "\n" + errMsg + "\n\n")
	return nil
}

主要负责发送交易到接口

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
)

type ethQueryCount struct {
	Jsonrpc string `json:"jsonrpc"`
	Result  string `json:"result"`
	Id      string `json:"id"`
}

type ethQueryStatus struct {
	Status   string      `json:"status"`
	Message  string      `json:"message"`
	TxResult ethTxResult `json:"result"`
}

type ethTxResult struct {
	IsError        string `json:"iserror"`
	ErrDescription string `json:"errdescription"`
}

type accountAndValue struct {
	account string
	value   int
}

type ethTxHashRes struct {
	TxHash string   `json:"result"`
	Error  ethError `json:"error"`
}

type ethError struct {
	Message string `json:"message"`
	Code    int    `json:"code:"`
}

//const sendRawTransUrl = "https://api.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=%s&apikey=%s"
const TestQueryTxStatusUrl = "https://api-ropsten.etherscan.io/api?module=transaction&action=getstatus&txhash=%s&apikey=%s"
const TestSendRawTransUrl = "https://api-ropsten.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=%s&apikey=%s"
const TestQueryTransCountUrl = "https://api-ropsten.etherscan.io/api?module=proxy&action=eth_getTransactionCount&address=%s&tag=latest&apikey=%s"

const QueryTxStatusUrl = "https://api.etherscan.io/api?module=transaction&action=getstatus&txhash=%s&apikey=%s"
const SendRawTransUrl = "https://api.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=%s&apikey=%s"
const QueryTransCountUrl = "https://api.etherscan.io/api?module=proxy&action=eth_getTransactionCount&address=%s&tag=latest&apikey=%s"
const failedTransPath = "./errorTrans.txt"

//查询账户的count
func queryTransCount(account string, isTest bool) uint64 {
	var ethqueryRes ethQueryCount
	var count int64
	var err error
	var url string
	if isTest {
		url = fmt.Sprintf(TestQueryTransCountUrl, account, apikey)
	} else {
		url = fmt.Sprintf(QueryTransCountUrl, account, apikey)
	}
	fmt.Println("queryTransCount Url:", url)
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fmt.Println("queryTransCount err:", err)
		return 0
	}
	if resp, err := http.DefaultClient.Do(request); err == nil {
		respBody, _ := ioutil.ReadAll(resp.Body)
		json.Unmarshal(respBody, &ethqueryRes)
		fmt.Println("queryTransCount result: ", string(respBody))
		count, err = strconv.ParseInt(ethqueryRes.Result[2:], 16, 64)
		if err != nil {
			fmt.Println("string to int64 error:", err)
		} else {
			fmt.Printf("count of the account %s ,count is :%d\n", account, count)
		}
	}
	return uint64(count)
}

//发送交易
func sendRawTrans(index int, rawTrans string, isTest bool) (string, error) {
	var resError ethError
	txhashRes := ethTxHashRes{
		Error: resError,
	}
	var url string
	if isTest {
		url = fmt.Sprintf(TestSendRawTransUrl, rawTrans, apikey)
	} else {
		url = fmt.Sprintf(SendRawTransUrl, rawTrans, apikey)
	}
	//fmt.Println(url)
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fmt.Println("sendRawTransaction err:", err)
		return "", nil
	}
	if resp, err := http.DefaultClient.Do(request); err == nil {
		respBody, _ := ioutil.ReadAll(resp.Body)
		//fmt.Println("sendRawTransaction result:", string(respBody))
		json.Unmarshal(respBody, &txhashRes)
		if txhashRes.Error.Message != "" {
			return txhashRes.Error.Message, errors.New("err")
		} else {
			return txhashRes.TxHash, nil
		}
	} else {
		fmt.Println("resp error when sendRawTrans,err:", err)
		return "", err
	}
}

你可能感兴趣的:(区块链)