Skip to content

Commit 2b491eb

Browse files
committed
add more example to payment gateway
1 parent de910f3 commit 2b491eb

File tree

23 files changed

+1333
-57
lines changed

23 files changed

+1333
-57
lines changed

.gitignore

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
./js-example/node_modules/
2-
./js-example/npm-debug.log
3-
./js-example/yarn-error.log
4-
./js-example/yarn-debug.log
5-
./js-example/.pnpm-debug.log
1+
./wallet-user/js-example/node_modules/
2+
./wallet-user/js-example/npm-debug.log
3+
./wallet-user/js-example/yarn-error.log
4+
./wallet-user/js-example/yarn-debug.log
5+
./wallet-user/js-example/.pnpm-debug.log
6+
./sponsor/payment-gateway/js-example/node_modules/
7+
./sponsor/payment-gateway/js-example/npm-debug.log
8+
./sponsor/payment-gateway/js-example/yarn-error.log
9+
./sponsor/payment-gateway/js-example/yarn-debug.log
10+
./sponsor/payment-gateway/js-example/.pnpm-debug.log
11+
./sponsor/cex/js-example/node_modules/
12+
./sponsor/cex/js-example/npm-debug.log
13+
./sponsor/cex/js-example/yarn-error.log
14+
./sponsor/cex/js-example/yarn-debug.log
15+
./sponsor/cex/js-example/.pnpm-debug.log
616
.idea/

go-example/readme.md

Lines changed: 0 additions & 25 deletions
This file was deleted.

readme.md

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
1-
# Paymaster Client Example
1+
# Paymaster Example
22

3-
This repository contains a Go and Js application demonstrating how to interact with an BSC testnet using a paymaster
4-
service. The application showcases how to create, sign, and send ERC20 token transfers while leveraging a paymaster
5-
for potential gas sponsorship.
6-
7-
## Workflow
8-
9-
- Connect to an Ethereum network using a paymaster client
10-
- Create and sign ERC20 token transfer transactions
11-
- Check if a transaction is sponsorable
12-
- Send transactions through a paymaster client to paymaster endpoint
3+
This repository contains several examples implemented in both Golang and JavaScript,
4+
including those for wallet integration, CEX (Centralized Exchange) integration,
5+
payment gateway integration, and more.
136

147
## Network Endpoint
15-
168
BSC testnet: https://bsc-paymaster-testnet.nodereal.io
179

1810
## Example
@@ -22,8 +14,8 @@ Please get ERC20 token for test before you start:
2214
2. Claim any kind of ERC20 token except BNB.
2315
![image](./assets/img.png)
2416

25-
- [Js Example](./js-example/readme.md)
26-
- [Go Example](./go-example)
17+
- [For wallet integration](./wallet-user/readme.md)
18+
- [For payment gateway integration](./sponsor/payment-gateway/readme.md)
2719

2820
## More docs
2921
- [Paymaster Overview](https://docs.nodereal.io/docs/maganode-paymaster-overview)
File renamed without changes.
File renamed without changes.
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"crypto/ecdsa"
6+
"encoding/json"
7+
"fmt"
8+
"log"
9+
"math/big"
10+
11+
"github.com/ethereum/go-ethereum/common"
12+
"github.com/ethereum/go-ethereum/common/hexutil"
13+
"github.com/ethereum/go-ethereum/core/types"
14+
"github.com/ethereum/go-ethereum/crypto"
15+
"github.com/ethereum/go-ethereum/ethclient"
16+
)
17+
18+
const PaymentTokenContractAddress = "0x.."
19+
const PaymentRecipientAddress = "0x.."
20+
const PaymentSponsorPolicyId = ".."
21+
const SponsorAPIEndpoint = "https://open-platform.nodereal.io/{Your_API_key}/eoa-paymaster-testnet"
22+
const UserPrivateKey = "..."
23+
24+
func main() {
25+
receiver := common.HexToAddress(PaymentRecipientAddress)
26+
payAmount := big.NewInt(1e17)
27+
28+
paymentGatewaySetUpPolicyRules(receiver)
29+
30+
userDoGaslessPayment(receiver, payAmount)
31+
}
32+
33+
func paymentGatewaySetUpPolicyRules(receiver common.Address) {
34+
sponsorClient, err := NewSponsorClient(SponsorAPIEndpoint)
35+
if err != nil {
36+
log.Fatal(err)
37+
}
38+
39+
// sponsor the tx that interact with the stable coin ERC20 contract
40+
success, err := sponsorClient.AddToWhitelist(context.Background(), WhitelistParams{
41+
PolicyUUID: PaymentSponsorPolicyId,
42+
WhitelistType: ToAccountWhitelist,
43+
Values: []string{PaymentTokenContractAddress},
44+
})
45+
if err != nil || !success {
46+
log.Fatal("failed to add token contract whitelist")
47+
}
48+
49+
// sponsor the tx that call the "transfer" interface of ERC20 contract
50+
success, err = sponsorClient.AddToWhitelist(context.Background(), WhitelistParams{
51+
PolicyUUID: PaymentSponsorPolicyId,
52+
WhitelistType: ContractMethodSigWhitelist,
53+
Values: []string{"0xa9059cbb"},
54+
})
55+
if err != nil || !success {
56+
log.Fatal("failed to add contract method whitelist")
57+
}
58+
59+
// sponsor the tx that "transfer" stable coin to particular receiver account
60+
success, err = sponsorClient.AddToWhitelist(context.Background(), WhitelistParams{
61+
PolicyUUID: PaymentSponsorPolicyId,
62+
WhitelistType: BEP20ReceiverWhiteList,
63+
Values: []string{receiver.String()},
64+
})
65+
66+
if err != nil || !success {
67+
log.Fatal("failed to add payment receiver whitelist")
68+
}
69+
70+
receiverWhitelist := GetWhitelistParams{
71+
PolicyUUID: PaymentSponsorPolicyId,
72+
WhitelistType: BEP20ReceiverWhiteList,
73+
Offset: 0,
74+
Limit: 1000,
75+
}
76+
77+
// get the receiver whitelist, the Payment Gateway may need to update the whitelist according to its need.
78+
result, err := sponsorClient.GetWhitelist(context.Background(), receiverWhitelist)
79+
if err != nil {
80+
log.Fatal(err)
81+
}
82+
83+
fmt.Println("Whitelist addresses:")
84+
for _, address := range result {
85+
fmt.Println(address)
86+
}
87+
}
88+
89+
func userDoGaslessPayment(receiver common.Address, amount *big.Int) {
90+
// Connect to an Ethereum node (for transaction assembly)
91+
client, err := ethclient.Dial("https://bsc-testnet-dataseed.bnbchain.org")
92+
if err != nil {
93+
log.Fatalf("Failed to connect to the Ethereum network: %v", err)
94+
}
95+
// Create a PaymasterClient (for transaction sending)
96+
paymasterClient, err := NewPaymasterClient("https://bsc-paymaster-testnet.nodereal.io")
97+
if err != nil {
98+
log.Fatalf("Failed to create PaymasterClient: %v", err)
99+
}
100+
101+
// Load your private key
102+
privateKey, err := crypto.HexToECDSA(UserPrivateKey)
103+
if err != nil {
104+
log.Fatalf("Failed to load private key: %v", err)
105+
}
106+
107+
publicKey := privateKey.Public()
108+
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
109+
if !ok {
110+
log.Fatal("Error casting public key to ECDSA")
111+
}
112+
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
113+
114+
// Token contract address
115+
tokenAddress := common.HexToAddress(PaymentTokenContractAddress)
116+
117+
// Create ERC20 transfer data
118+
data, err := createERC20TransferData(receiver, amount)
119+
if err != nil {
120+
log.Fatalf("Failed to create ERC20 transfer data: %v", err)
121+
}
122+
123+
// Get the latest nonce for the from address
124+
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
125+
if err != nil {
126+
log.Fatalf("Failed to get nonce: %v", err)
127+
}
128+
129+
// Create the transaction
130+
gasPrice := big.NewInt(0)
131+
tx := types.NewTransaction(nonce, tokenAddress, big.NewInt(0), 300000, gasPrice, data)
132+
133+
// Get the chain ID
134+
chainID, err := client.ChainID(context.Background())
135+
if err != nil {
136+
log.Fatalf("Failed to get chain ID: %v", err)
137+
}
138+
139+
// Sign the transaction
140+
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
141+
if err != nil {
142+
log.Fatalf("Failed to sign transaction: %v", err)
143+
}
144+
145+
// Convert to Transaction struct for IsSponsorable check
146+
gasLimit := tx.Gas()
147+
sponsorableTx := Transaction{
148+
To: &tokenAddress,
149+
From: fromAddress,
150+
Value: (*hexutil.Big)(big.NewInt(0)),
151+
Gas: (*hexutil.Uint64)(&gasLimit),
152+
Data: (*hexutil.Bytes)(&data),
153+
}
154+
155+
// Check if the transaction is sponsorable
156+
sponsorableInfo, err := paymasterClient.IsSponsorable(context.Background(), sponsorableTx)
157+
if err != nil {
158+
log.Fatalf("Error checking sponsorable status: %v", err)
159+
}
160+
161+
jsonInfo, _ := json.MarshalIndent(sponsorableInfo, "", " ")
162+
fmt.Printf("Sponsorable Information:\n%s\n", string(jsonInfo))
163+
164+
if sponsorableInfo.Sponsorable {
165+
// Send the transaction using PaymasterClient
166+
err := paymasterClient.SendTransaction(context.Background(), signedTx)
167+
if err != nil {
168+
log.Fatalf("Failed to send sponsorable transaction: %v", err)
169+
}
170+
fmt.Printf("Sponsorable transaction sent: %s\n", signedTx.Hash())
171+
} else {
172+
fmt.Println("Transaction is not sponsorable. You may need to send it as a regular transaction.")
173+
}
174+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"math/big"
6+
7+
"github.com/ethereum/go-ethereum/common"
8+
"github.com/ethereum/go-ethereum/common/hexutil"
9+
"github.com/ethereum/go-ethereum/crypto"
10+
"github.com/ethereum/go-ethereum/ethclient"
11+
"github.com/ethereum/go-ethereum/rpc"
12+
)
13+
14+
type PaymasterClient struct {
15+
*ethclient.Client
16+
rpcClient *rpc.Client
17+
}
18+
19+
type SponsorableInfo struct {
20+
Sponsorable bool `json:"sponsorable"`
21+
SponsorName string `json:"sponsorName"`
22+
SponsorIcon string `json:"sponsorIcon"`
23+
SponsorWebsite string `json:"sponsorWebsite"`
24+
}
25+
26+
type Transaction struct {
27+
To *common.Address `json:"to"`
28+
From common.Address `json:"from"`
29+
Value *hexutil.Big `json:"value"`
30+
Gas *hexutil.Uint64 `json:"gas"`
31+
Data *hexutil.Bytes `json:"data"`
32+
}
33+
34+
func NewPaymasterClient(url string) (*PaymasterClient, error) {
35+
rpcClient, err := rpc.Dial(url)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
ethClient := ethclient.NewClient(rpcClient)
41+
42+
return &PaymasterClient{
43+
Client: ethClient,
44+
rpcClient: rpcClient,
45+
}, nil
46+
}
47+
48+
func (c *PaymasterClient) IsSponsorable(ctx context.Context, tx Transaction) (*SponsorableInfo, error) {
49+
var result SponsorableInfo
50+
err := c.rpcClient.CallContext(ctx, &result, "pm_isSponsorable", tx)
51+
if err != nil {
52+
return nil, err
53+
}
54+
return &result, nil
55+
}
56+
57+
func createERC20TransferData(to common.Address, amount *big.Int) ([]byte, error) {
58+
transferFnSignature := []byte("transfer(address,uint256)")
59+
methodID := crypto.Keccak256(transferFnSignature)[:4]
60+
paddedAddress := common.LeftPadBytes(to.Bytes(), 32)
61+
paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
62+
63+
var data []byte
64+
data = append(data, methodID...)
65+
data = append(data, paddedAddress...)
66+
data = append(data, paddedAmount...)
67+
return data, nil
68+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# GO Example
2+
This repository contains a Go application demonstrating:
3+
1. Payment Gateway manage the sponsor policy to sponsor any transaction that send BEP20 to them.
4+
2. User send ERC20 token transfers to payment gateway without pay gas fee through paymaster.
5+
6+
## Quick Start
7+
8+
The example is performed on BSC testnet, please ensure you have some test ERC20 on BSC testnet. (You can get some
9+
from the official faucet)
10+
11+
1. Install dependencies
12+
```shell
13+
$ go mod tidy
14+
```
15+
2. Configure the file
16+
Before running the application, you need to edit the `main.go` to set up the following:
17+
18+
- Set "PaymentTokenContractAddress" to the ERC20 token contract address users will use for payment.
19+
- Set "PaymentRecipientAddress" to the receiver address for the Payment Gateway's generated payment link.
20+
- Set "PaymentSponsorPolicyId" to the policy ID created by the Payment Gateway on Meganode Paymaster, create one
21+
from [here](https://docs.nodereal.io/docs/meganode-paymaster-sponsor-guidelines) if you don't have it.
22+
- Set "SponsorAPIEndpoint" to the API key created by the Payment Gateway in the Nodereal MegaNode Console.
23+
create one from [here](https://docs.nodereal.io/docs/meganode-paymaster-sponsor-guidelines) if you don't have it.
24+
- Set "UserPrivateKey" to the user's account private key, ensuring this wallet contains the required ERC20 tokens.
25+
26+
3. Run the example
27+
```
28+
$ go run main.go
29+
```
30+
31+

0 commit comments

Comments
 (0)