Introduction
This article is similar to our previous one, USDT transaction on Solana network, but in this case we are going to transfer USDT on Polygon(previously called MATIC) network.
Requirements
- Go installed, given that we are going to keep using Go for this job.
Code with comments
Ok so let's start, first let's import the libraries, mostly will be using go-ethereum.
package main
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"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"
"golang.org/x/crypto/sha3"
)
In this small recipe we'll use Alchemy RPC provider. Alchemy support several blockchains check it out in case you will need it.
When you create a application in Alchemy they will provide you with an API-KEY, which will be needed for connecting to the RPC endpoint. Let's define some constants that we'll need.
// After imports....
const (
// replace <API-KEY> with alchemy api key provided.
AlchemyPolygonRPCEndpoint = "https://polygon-mainnet.g.alchemy.com/v2/<API-KEY>"
// USDTTokenAddress is USDT contract address for the USDT token on Polygon
// network. Can be checked in the following polygonscan link:
// https://polygonscan.com/token/0xc2132d05d31c914a87c6611c10748aeb04b58e8f
USDTTokenAddress = "0xc2132D05D31c914a87C6611C10748AEb04B58e8F"
DefaultGasLimit uint64 = 100000
)
Let's define our client and its constructor.
// 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
}
The main method will be called TransferUSDT
. One main difference in this method and the one from Solana article, is that we cannot send comment in the transaction.
// TransferUSDT make transaction of usdts to the specified address.
// The amount should be provided in 6 decimals.
// Meaning, 1 USDT should be represented as 1e6.
// ctx: context
// toAddressStrHex: hexadecimal representation of receiver address(Public Address)
// amount: usdt amount to be sent.
func (c *Client) TransferUSDT(
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(USDTTokenAddress)
// 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://polygonscan.com/token/0xc2132d05d31c914a87c6611c10748aeb04b58e8f#writeProxyContract#F11
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 6 decimals.
// which are the decimals for usdt.
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
}
Now writing the main function we got:
func main() {
ctx := context.Background()
client, err := NewWithPrivateKey("<PRIVATE-KEY>")
if err != nil {
panic(err)
}
txnHash, err := client.TransferUSDT(ctx, "<RECEIVER ADDRESS>", 1e6) // Sending 1 usdt.
if err != nil {
panic(err)
}
fmt.Println("TXN HASH: ", txnHash)
}
That's all, feel free to join the parts :).
Top comments (0)