Skip to content

Commit a5522ac

Browse files
committed
update with cex doc
1 parent 2b491eb commit a5522ac

24 files changed

+1241
-13
lines changed

.gitignore

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
./wallet-user/js-example/yarn-error.log
44
./wallet-user/js-example/yarn-debug.log
55
./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
6+
./payment-gateway/js-example/node_modules/
7+
./payment-gateway/js-example/npm-debug.log
8+
./payment-gateway/js-example/yarn-error.log
9+
./payment-gateway/js-example/yarn-debug.log
10+
./payment-gateway/js-example/.pnpm-debug.log
11+
./cex/js-example/node_modules/
12+
./cex/js-example/npm-debug.log
13+
./cex/js-example/yarn-error.log
14+
./cex/js-example/yarn-debug.log
15+
./cex/js-example/.pnpm-debug.log
1616
.idea/
File renamed without changes.
File renamed without changes.

cex/go-example/main.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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 TokenContractAddress = "0x.."
19+
const WithdrawRecipientAddress = "0x.."
20+
const SponsorPolicyId = ".."
21+
const SponsorAPIEndpoint = "https://open-platform.nodereal.io/{YOUR_API_KEY}/eoa-paymaster-testnet"
22+
const HotwalletPrivateKey = ".."
23+
24+
func main() {
25+
sponsorSetUpPolicyRules()
26+
cexDoGaslessWithdrawl()
27+
}
28+
29+
func sponsorSetUpPolicyRules() {
30+
sponsorClient, err := NewSponsorClient(SponsorAPIEndpoint)
31+
if err != nil {
32+
log.Fatal(err)
33+
}
34+
// sponsor the tx that interact with the stable coin ERC20 contract
35+
success, err := sponsorClient.AddToWhitelist(context.Background(), WhitelistParams{
36+
PolicyUUID: SponsorPolicyId,
37+
WhitelistType: ToAccountWhitelist,
38+
Values: []string{TokenContractAddress},
39+
})
40+
if err != nil || !success {
41+
log.Fatal("failed to add token contract whitelist", err)
42+
}
43+
44+
// sponsor the tx that from hotwallets
45+
fromAddress := getAddressFromPrivateKey(HotwalletPrivateKey)
46+
47+
success, err = sponsorClient.AddToWhitelist(context.Background(), WhitelistParams{
48+
PolicyUUID: SponsorPolicyId,
49+
WhitelistType: FromAccountWhitelist,
50+
Values: []string{fromAddress.String()},
51+
})
52+
if err != nil || !success {
53+
log.Fatal("failed to add contract method whitelist")
54+
}
55+
}
56+
57+
func cexDoGaslessWithdrawl() {
58+
withdrawAmount := big.NewInt(1e17)
59+
// Connect to an Ethereum node (for transaction assembly)
60+
client, err := ethclient.Dial("https://bsc-testnet-dataseed.bnbchain.org")
61+
if err != nil {
62+
log.Fatalf("Failed to connect to the Ethereum network: %v", err)
63+
}
64+
// Create a PaymasterClient (for transaction sending)
65+
paymasterClient, err := NewPaymasterClient("https://bsc-paymaster-testnet.nodereal.io")
66+
if err != nil {
67+
log.Fatalf("Failed to create PaymasterClient: %v", err)
68+
}
69+
70+
// Load your private key
71+
privateKey, err := crypto.HexToECDSA(HotwalletPrivateKey)
72+
if err != nil {
73+
log.Fatalf("Failed to load private key: %v", err)
74+
}
75+
76+
fromAddress := getAddressFromPrivateKey(HotwalletPrivateKey)
77+
78+
// Token contract address
79+
tokenAddress := common.HexToAddress(TokenContractAddress)
80+
81+
// Create ERC20 transfer data
82+
data, err := createERC20TransferData(common.HexToAddress(WithdrawRecipientAddress), withdrawAmount)
83+
if err != nil {
84+
log.Fatalf("Failed to create ERC20 transfer data: %v", err)
85+
}
86+
87+
// Get the latest nonce for the from address
88+
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
89+
if err != nil {
90+
log.Fatalf("Failed to get nonce: %v", err)
91+
}
92+
93+
// Create the transaction
94+
gasPrice := big.NewInt(0)
95+
tx := types.NewTransaction(nonce, tokenAddress, big.NewInt(0), 300000, gasPrice, data)
96+
97+
// Get the chain ID
98+
chainID, err := client.ChainID(context.Background())
99+
if err != nil {
100+
log.Fatalf("Failed to get chain ID: %v", err)
101+
}
102+
103+
// Sign the transaction
104+
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
105+
if err != nil {
106+
log.Fatalf("Failed to sign transaction: %v", err)
107+
}
108+
109+
// Convert to Transaction struct for IsSponsorable check
110+
gasLimit := tx.Gas()
111+
sponsorableTx := Transaction{
112+
To: &tokenAddress,
113+
From: fromAddress,
114+
Value: (*hexutil.Big)(big.NewInt(0)),
115+
Gas: (*hexutil.Uint64)(&gasLimit),
116+
Data: (*hexutil.Bytes)(&data),
117+
}
118+
119+
// Check if the transaction is sponsorable
120+
sponsorableInfo, err := paymasterClient.IsSponsorable(context.Background(), sponsorableTx)
121+
if err != nil {
122+
log.Fatalf("Error checking sponsorable status: %v", err)
123+
}
124+
125+
jsonInfo, _ := json.MarshalIndent(sponsorableInfo, "", " ")
126+
fmt.Printf("Sponsorable Information:\n%s\n", string(jsonInfo))
127+
128+
if sponsorableInfo.Sponsorable {
129+
// Send the transaction using PaymasterClient
130+
err := paymasterClient.SendTransaction(context.Background(), signedTx)
131+
if err != nil {
132+
log.Fatalf("Failed to send sponsorable transaction: %v", err)
133+
}
134+
fmt.Printf("Sponsorable transaction sent: %s\n", signedTx.Hash())
135+
} else {
136+
fmt.Println("Transaction is not sponsorable. You may need to send it as a regular transaction.")
137+
}
138+
}
139+
140+
func getAddressFromPrivateKey(pk string) common.Address {
141+
// Load your private key
142+
privateKey, err := crypto.HexToECDSA(pk)
143+
if err != nil {
144+
log.Fatalf("Failed to load private key: %v", err)
145+
}
146+
147+
publicKey := privateKey.Public()
148+
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
149+
if !ok {
150+
log.Fatal("Error casting public key to ECDSA")
151+
}
152+
return crypto.PubkeyToAddress(*publicKeyECDSA)
153+
}
File renamed without changes.

cex/go-example/readme.md

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. Sponsor manage the policy to sponsor any transaction sent by Cex hotwallets.
4+
2. Cex do token withdrawal 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 "TokenContractAddress" to the ERC20 token contract address that users want to withdraw.
19+
- Set "WithdrawRecipientAddress" to the receiver address of user's withdrawal request.
20+
- Set "SponsorPolicyId" to the policy ID created by the sponsor 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 sponsor 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 "HotwalletPrivateKey" to the Cex's hotwallet 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+
File renamed without changes.

cex/js-example/index.js

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
const ethers = require('ethers');
2+
3+
// Replace with the cex hot wallet private key (be cautious with private keys!)
4+
const hotwalletPrivateKey = 'HOT_WALLET_PRIVATE_KEY';
5+
// replace with user ERC20 withdraw address
6+
const userWithdrawAddress = 'USER_WITHDRAW_ADDRESS';
7+
// ERC20 token contract address (replace with the address of the token you want to send)
8+
const erc20TokenAddress = 'TOKEN_CONTRACT_ADDRESS';
9+
const sponsorEndpoint = 'https://open-platform.nodereal.io/{SPONSOR_API_KEY}/eoa-paymaster-testnet';
10+
const policyID = 'SPONSOR_POLICY_ID'
11+
12+
13+
class SponsorProvider extends ethers.providers.JsonRpcProvider {
14+
constructor(url) {
15+
super(url);
16+
}
17+
18+
async addToWhitelist(params) {
19+
return this.send('pm_addToWhitelist', [params]);
20+
}
21+
22+
async removeFromWhitelist(params) {
23+
return this.send('pm_rmFromWhitelist', [params]);
24+
}
25+
26+
async emptyWhitelist(params) {
27+
return this.send('pm_emptyWhitelist', [params]);
28+
}
29+
30+
async getWhitelist(params) {
31+
return this.send('pm_getWhitelist', [params]);
32+
}
33+
}
34+
35+
class PaymasterProvider extends ethers.providers.JsonRpcProvider {
36+
constructor(url) {
37+
super(url);
38+
}
39+
async isSponsorable(transaction) {
40+
const params = [{
41+
to: transaction.to,
42+
from: transaction.from,
43+
value: transaction.value != null ? ethers.utils.hexlify(transaction.value) : '0x0',
44+
gas: ethers.utils.hexlify(transaction.gasLimit || 0),
45+
data: transaction.data || '0x'
46+
}];
47+
48+
const result = await this.send('pm_isSponsorable', params);
49+
return result;
50+
}
51+
}
52+
53+
async function cexDoGaslessWithdrawTx() {
54+
55+
// Provider for assembling the transaction (e.g., mainnet)
56+
const assemblyProvider = new ethers.providers.JsonRpcProvider('https://bsc-testnet-dataseed.bnbchain.org');
57+
58+
// Provider for sending the transaction (e.g., could be a different network or provider)
59+
const paymasterProvider = new PaymasterProvider('https://bsc-paymaster-testnet.nodereal.io');
60+
61+
const wallet = new ethers.Wallet(hotwalletPrivateKey, assemblyProvider);
62+
// ERC20 token ABI (only including the transfer function)
63+
const tokenAbi = [
64+
"function transfer(address to, uint256 amount) returns (bool)"
65+
];
66+
67+
// Create contract instance
68+
const tokenContract = new ethers.Contract(erc20TokenAddress, tokenAbi, wallet);
69+
70+
// Transaction details
71+
const tokenAmount = ethers.utils.parseUnits('1.0', 18); // Amount of tokens to send (adjust decimals as needed)
72+
73+
try {
74+
// Get the current nonce for the sender's address
75+
const nonce = await assemblyProvider.getTransactionCount(wallet.address);
76+
77+
// Create the transaction object
78+
const transaction = await tokenContract.populateTransaction.transfer(userWithdrawAddress, tokenAmount);
79+
80+
// Add nonce and gas settings
81+
transaction.nonce = nonce;
82+
transaction.gasPrice = 0; // Set gas price to 0
83+
transaction.gasLimit = 100000; // Adjust gas limit as needed for token transfers
84+
85+
try {
86+
const sponsorableInfo = await paymasterProvider.isSponsorable(transaction);
87+
console.log('Sponsorable Information:', sponsorableInfo);
88+
} catch (error) {
89+
console.error('Error checking sponsorable status:', error);
90+
}
91+
92+
// Sign the transaction
93+
const signedTx = await wallet.signTransaction(transaction);
94+
95+
// Send the raw transaction using the sending provider
96+
const tx = await paymasterProvider.send('eth_sendRawTransaction', [signedTx]);
97+
console.log('Transaction sent:', tx);
98+
99+
} catch (error) {
100+
console.error('Error sending transaction:', error);
101+
}
102+
}
103+
104+
async function sponsorSetUpPolicyRules() {
105+
const client = new SponsorProvider(sponsorEndpoint);
106+
107+
const wallet = new ethers.Wallet(hotwalletPrivateKey)
108+
// sponsor the tx that interact with the stable coin ERC20 contract
109+
try {
110+
await client.emptyWhitelist({
111+
policyUuid: policyID,
112+
whitelistType: "FromAccountWhitelist",
113+
});
114+
await client.emptyWhitelist({
115+
policyUuid: policyID,
116+
whitelistType: "ToAccountWhitelist",
117+
});
118+
// sponsor the tx that interact with the stable coin ERC20 contract
119+
const res1 = await client.addToWhitelist({
120+
policyUuid: policyID,
121+
whitelistType: "ToAccountWhitelist",
122+
values: [erc20TokenAddress]
123+
});
124+
console.log("Added ERC20 contract address to whitelist ", res1);
125+
126+
// sponsor the tx that sent by hotwallet
127+
const res2 = await client.addToWhitelist({
128+
policyUuid: policyID,
129+
whitelistType: "FromAccountWhitelist",
130+
values: [wallet.address]
131+
});
132+
console.log("Added hotwallet to whitelist ", res2);
133+
} catch (error){
134+
console.error("Error:", error)
135+
}
136+
}
137+
138+
async function main() {
139+
try {
140+
await sponsorSetUpPolicyRules();
141+
await cexDoGaslessWithdrawTx();
142+
} catch (error) {
143+
console.error("Error:", error);
144+
}
145+
}
146+
147+
main();
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)