USDT transaction on Polygon(MATIC) network
Tags: polygon, blockchain, usdt, go
Published: 2023-05-15 08:00:00

![Polygon USDT](https://res.cloudinary.com/djjd0wi5i/image/upload/v1684075876/polygon-usdt_fmg2dt.png)

## Introduction

This article is similar to our previous one, [USDT transaction on Solana network](https://www.gealber.com/usdt-solana-transaction), but in this case we are going to transfer USDT on Polygon(previously called MATIC) network.


## Requirements

1. 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](https://github.com/ethereum/go-ethereum).


```go

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](https://alchemy.com/?r=f0b915e9d018331e) RPC provider. [Alchemy](https://alchemy.com/?r=f0b915e9d018331e) support several blockchains check it out in case you will need it.

![Alchemy Dashboard](https://res.cloudinary.com/djjd0wi5i/image/upload/v1684074104/alchemy_mvzgas.jpg)

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.

```go

// 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.

```go

// 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](https://www.gealber.com/usdt-solana-transaction), is that we cannot send comment in the transaction. 


```go

// 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:

```go

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 :).