Polygon测试网络上实现Token交易。

交易节点

InfuraAlchemy是两个最受欢迎的节点供应商。两个节点都支持Polygon主网和测试网络。

关于两个节点比较和选择选择:Alchemy vs. Infura:哪个是最好的区块链节点服务商?

以Infura节点为例,登录Infura生成APIKey,后续通过APIKey即可连接上节点。

通过申请的APIKEY连接polygon mumbai测试网络,需要在Infura上对APIKey授权访问该测试网络。

访问节点并查询最新区块。 Make requests

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
	"fmt"
	"log"

	"github.com/ethereum/go-ethereum/rpc"
)

type Block struct {
	Number string
}

func main() {
	client, err := rpc.Dial("https://polygon-mumbai.infura.io/v3/<APIKEY>")
	if err != nil {
		log.Fatalf("Could not connect to Infura: %v", err)
	}

	var lastBlock Block
	err = client.Call(&lastBlock, "eth_getBlockByNumber", "latest", true)
	if err != nil {
		fmt.Println("Cannot get the latest block:", err)
		return
	}

	fmt.Printf("Latest block: %v\n", lastBlock.Number)
}

测试网络领取Token

如果选择测试网格测试,可以免费领取到一些Token用于测试体验,下面是一些领取的方法:

Polygon Portal

  1. faucet network 打开Polygon Faucet,可以选择网络和Token,

然后输入钱包地址,提交就会领取到一定数量的Token,打开选择的测试网络即可查看到,前提是需要

登录Discord授权登录,如果需要更多的测试Token,可以单独提交申请,申请时需要填写Discord的ID。

可以领取到MATIC和ERC20 Token,领取或申请几分钟后,进入测试网络即可看到。

  1. Polygon Mumbai Faucet

  2. How to Get Sepolia ETH from a Faucet

打开 Sepolia Faucet 领取ETH

需要登录Alchemy账号

发起一笔交易

Send a regular transaction from one account to another with Go.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package main

import (
	"context"
	"crypto/ecdsa"
	"fmt"
	"log"
	"math/big"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
)

func main() {
	client, err := ethclient.Dial("https://polygon-mumbai.infura.io/v3/8c6cc3ae7d5b4449bb48ada6ef8ae892")
	if err != nil {
		log.Fatal(err)
	}

	privateKey, err := crypto.HexToECDSA("PRIVATE_KEY")
	if err != nil {
		log.Fatal(err)
	}

	publicKey := privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		log.Fatal("error casting public key to ECDSA")
	}

	fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

	nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
	if err != nil {
		log.Fatal(err)
	}

	value := big.NewInt(10000000000000000) // in wei (0.01 eth)
	gasLimit := uint64(21000)              // in units
	tip := big.NewInt(2000000000)          // maxPriorityFeePerGas = 2 Gwei
	feeCap := big.NewInt(20000000000)      // maxFeePerGas = 20 Gwei
	if err != nil {
		log.Fatal(err)
	}

	toAddress := common.HexToAddress("ADDRESS_TO")
	var data []byte

	chainID, err := client.NetworkID(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	tx := types.NewTx(&types.DynamicFeeTx{
		ChainID:   chainID,
		Nonce:     nonce,
		GasFeeCap: feeCap,
		GasTipCap: tip,
		Gas:       gasLimit,
		To:        &toAddress,
		Value:     value,
		Data:      data,
	})

	signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainID), privateKey)

	if err != nil {
		log.Fatal(err)
	}

	err = client.SendTransaction(context.Background(), signedTx)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Transaction hash: %s", signedTx.Hash().Hex())

}

指定Token交易

交易TokenDummy ERC20 (DERC20)

交易其他Token如USDT时,替换对应的Token合约地址即可。

USDT Token Transaction

在polygon mumbai网络上交易DERC20 Token。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
const (
	// replace <API-KEY> with alchemy api key provided.
	AlchemyPolygonRPCEndpoint = "https://polygon-mumbai.infura.io/v3/<API-KEY>"

	// TokenAddress is DERC20 contract address for the DERC20 token on Polygon mumbai
	// network. Can be checked in the following polygonscan link:
    // https://mumbai.polygonscan.com/address/0xfe4f5145f6e09952a5ba9e956ed0c25e3fa4c7f1
	TokenAddress = "0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1"

	DefaultGasLimit uint64 = 100000
)

// Client for making transaction.
type Client struct {
	client     *ethclient.Client
	publickKey common.Address
	privateKey *ecdsa.PrivateKey
}

// NewWithPrivateKey creates a new Client with the private key
// provided.
func NewWithPrivateKey(pKeyStr string) (*Client, error) {
	client, err := ethclient.Dial(AlchemyPolygonRPCEndpoint)
	if err != nil {
		return nil, err
	}

	privateKey, err := crypto.HexToECDSA(pKeyStr)
	if err != nil {
		return nil, err
	}

	publicKey := privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		return nil, errors.New("unable to convert publicKey to *ecdsa.PublicKey type")
	}

	// extracting public address of the wallet with the supplied private key.
	pubAddrs := crypto.PubkeyToAddress(*publicKeyECDSA)

	return &Client{
		client:     client,
		publickKey: pubAddrs,
		privateKey: privateKey,
	}, nil
}

// TransferToken make transaction of tokens to the specified address.
// The amount should be provided in 18 decimals.
// Meaning, 1 DERC20 should be represented as 1e18.
// ctx: context
// toAddressStrHex: hexadecimal representation of receiver address(Public Address)
// amount: usdt amount to be sent.
func (c *Client) TransferToken(
	ctx context.Context,
	toAddressStrHex string,
	amount uint64,
) (string, error) {
	// Retrieving pending nonce. The nonce, according to
	// ethereum glossary is a:
	// "An account nonce is a transaction counter in each account,
	// which is used to prevent replay attacks."
	nonce, err := c.client.PendingNonceAt(ctx, c.publickKey)
	if err != nil {
		return "", err
	}

	// given that we are going to transfer
	// usdts we don't need eths wei (0 eth).
	value := big.NewInt(0)

	// receiver address.
	toAddress := common.HexToAddress(toAddressStrHex)
	// usdt token address.
	tokenAddress := common.HexToAddress(TokenAddress)

	// we will use the transfer method
	// on the smart contract associated with usdt token
	// in order to use this method, we need to provide the method id
	// this is how we get that number.
	// You could also check it here in this link
    // https://mumbai.polygonscan.com/address/0xfe4f5145f6e09952a5ba9e956ed0c25e3fa4c7f1#writeContract#F9
	transferFnSignature := []byte("transfer(address,uint256)")
	hash := sha3.NewLegacyKeccak256()
	hash.Write(transferFnSignature)
	methodID := hash.Sum(nil)[:4]

	// we need to add 32 bytes of zeros to our address.
	paddedAddress := common.LeftPadBytes(toAddress.Bytes(), 32)

	// we need to add 32 bytes of zeros to our amount of tokens.
	// we are assuming this amount of tokens is expressed in 18 decimals.
	// which are the decimals for DERC20.
	amountBigInt := new(big.Int)
	amountBigInt.SetUint64(amount)
	paddedAmount := common.LeftPadBytes(amountBigInt.Bytes(), 32)

	// now let's put this three parts into
	// the data we are going to pass in the transaction
	// part one: methodID
	// part two: receiver address padded 32 bytes
	// part three: padded amount to be sent
	var data []byte
	data = append(data, methodID...)
	data = append(data, paddedAddress...)
	data = append(data, paddedAmount...)

	// retrieving suggested gas fees and gas price.
	tipCap, err := c.client.SuggestGasTipCap(ctx)
	if err != nil {
		return "", err
	}

	feeCap, err := c.client.SuggestGasPrice(ctx)
	if err != nil {
		return "", err
	}

	//  network ID for this client.
	chainID, err := c.client.NetworkID(ctx)
	if err != nil {
		return "", err
	}

	// creating our transaction, in this case we are going to use
	// dynamic fees txns instead of the legacy system.
	tx := types.NewTx(&types.DynamicFeeTx{
		ChainID:   chainID,
		Nonce:     nonce,
		GasTipCap: tipCap,
		GasFeeCap: feeCap,
		Gas:       DefaultGasLimit,
		To:        &tokenAddress,
		Value:     value,
		Data:      data,
	})

	// sign the transaction with our private key.
	signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainID), c.privateKey)
	if err != nil {
		return "", err
	}

	// send the transaction.
	err = c.client.SendTransaction(ctx, signedTx)
	if err != nil {
		return "", err
	}

	// return the hexadecimal representation of the txnHash.
	return signedTx.Hash().Hex(), nil
}

func main() {
	ctx := context.Background()
	privateKey := `<PRIVATE-KEY>`
	client, err := NewWithPrivateKey(privateKey)
	if err != nil {
		panic(err)
	}

	receiverAddress := `RECEIVER-ADDRESS`
	txnHash, err := client.TransferToken(ctx, receiverAddress, 1e18) // Sending 1 DERC20.
	if err != nil {
		panic(err)
	}

	fmt.Println("TXN HASH: ", txnHash)
}

参考