Skip to content

Commit

Permalink
fix: address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
BarryTong65 committed Oct 16, 2024
1 parent 10c4ca2 commit bedb86c
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 99 deletions.
75 changes: 30 additions & 45 deletions pkg/paymasterclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,89 +31,74 @@ type Client interface {
}

type client struct {
userClient *rpc.Client
sponsorClient *rpc.Client
c *rpc.Client
}

// New creates a new Client with an optional sponsorURL.
// If sponsorURL is provided, it enables the use of private policies.
// The sponsorURL is typically in the format: "https://open-platform-ap.nodereal.io/xxxx/megafuel-testnet"
// PrivatePolicyUUID can only be used when sponsorURL is provided.
func New(ctx context.Context, userURL string, sponsorURL *string, options ...rpc.ClientOption) (Client, error) {
userClient, err := rpc.DialOptions(ctx, userURL, options...)
func New(ctx context.Context, userURL string, options ...rpc.ClientOption) (Client, error) {
c, err := rpc.DialOptions(ctx, userURL, options...)
if err != nil {
return nil, err
}

var sponsorClient *rpc.Client
if sponsorURL != nil {
sponsorClient, err = rpc.DialOptions(ctx, *sponsorURL, options...)
if err != nil {
userClient.Close() // Close the user client if sponsor client creation fails
return nil, err
}
}

return &client{
userClient: userClient,
sponsorClient: sponsorClient,
}, nil
return &client{c}, nil
}

func (c *client) ChainID(ctx context.Context) (*big.Int, error) {
var result hexutil.Big
err := c.userClient.CallContext(ctx, &result, "eth_chainId")
err := c.c.CallContext(ctx, &result, "eth_chainId")
if err != nil {
return nil, err
}
return (*big.Int)(&result), err
}

// IsSponsorable checks if a transaction is sponsorable.
// If opts.PrivatePolicyUUID is set (for sponsor client use only), it will be included in the request headers.
func (c *client) IsSponsorable(ctx context.Context, tx TransactionArgs, opts *IsSponsorableOptions) (*IsSponsorableResponse, error) {
var result IsSponsorableResponse
if c.sponsorClient != nil && opts != nil && opts.PrivatePolicyUUID != "" {
c.sponsorClient.SetHeader("X-MegaFuel-Policy-Uuid", opts.PrivatePolicyUUID)
err := c.sponsorClient.CallContext(ctx, &result, "pm_isSponsorable", tx)
if err != nil {
return nil, err
}
return &result, nil

if opts != nil && opts.PrivatePolicyUUID != "" {
c.c.SetHeader("X-MegaFuel-Policy-Uuid", opts.PrivatePolicyUUID)
}
err := c.userClient.CallContext(ctx, &result, "pm_isSponsorable", tx)

err := c.c.CallContext(ctx, &result, "pm_isSponsorable", tx)
if err != nil {
return nil, err
}

return &result, nil
}

// SendRawTransaction sends a raw transaction to the connected domain.
// If opts.PrivatePolicyUUID is set (for sponsor client use only), it will be included in the request headers.
// opts.UserAgent can be set for both sponsor and paymaster client calls.
func (c *client) SendRawTransaction(ctx context.Context, input hexutil.Bytes, opts *SendRawTransactionOptions) (common.Hash, error) {
var result common.Hash
if c.sponsorClient != nil {
if opts != nil && opts.UserAgent != "" {
c.sponsorClient.SetHeader("User-Agent", opts.UserAgent)

if opts != nil {
if opts.UserAgent != "" {
c.c.SetHeader("User-Agent", opts.UserAgent)
}
if opts != nil && opts.PrivatePolicyUUID != "" {
c.sponsorClient.SetHeader("X-MegaFuel-Policy-Uuid", opts.PrivatePolicyUUID)
err := c.sponsorClient.CallContext(ctx, &result, "eth_sendRawTransaction", input)
if err != nil {
return common.Hash{}, err
}
return result, nil
if opts.PrivatePolicyUUID != "" {
c.c.SetHeader("X-MegaFuel-Policy-Uuid", opts.PrivatePolicyUUID)
}
}
if opts != nil && opts.UserAgent != "" {
c.userClient.SetHeader("User-Agent", opts.UserAgent)
}
err := c.userClient.CallContext(ctx, &result, "eth_sendRawTransaction", input)

err := c.c.CallContext(ctx, &result, "eth_sendRawTransaction", input)
if err != nil {
return common.Hash{}, err
}

return result, nil
}

func (c *client) GetGaslessTransactionByHash(ctx context.Context, txHash common.Hash) (*TransactionResponse, error) {
var result TransactionResponse
err := c.userClient.CallContext(ctx, &result, "eth_getGaslessTransactionByHash", txHash)
err := c.c.CallContext(ctx, &result, "eth_getGaslessTransactionByHash", txHash)
if err != nil {
return nil, err
}
Expand All @@ -122,7 +107,7 @@ func (c *client) GetGaslessTransactionByHash(ctx context.Context, txHash common.

func (c *client) GetSponsorTxByTxHash(ctx context.Context, txHash common.Hash) (*SponsorTx, error) {
var result SponsorTx
err := c.userClient.CallContext(ctx, &result, "pm_getSponsorTxByTxHash", txHash)
err := c.c.CallContext(ctx, &result, "pm_getSponsorTxByTxHash", txHash)
if err != nil {
return nil, err
}
Expand All @@ -131,7 +116,7 @@ func (c *client) GetSponsorTxByTxHash(ctx context.Context, txHash common.Hash) (

func (c *client) GetSponsorTxByBundleUUID(ctx context.Context, bundleUUID uuid.UUID) (*SponsorTx, error) {
var result SponsorTx
err := c.userClient.CallContext(ctx, &result, "pm_getSponsorTxByBundleUuid", bundleUUID)
err := c.c.CallContext(ctx, &result, "pm_getSponsorTxByBundleUuid", bundleUUID)
if err != nil {
return nil, err
}
Expand All @@ -140,7 +125,7 @@ func (c *client) GetSponsorTxByBundleUUID(ctx context.Context, bundleUUID uuid.U

func (c *client) GetBundleByUUID(ctx context.Context, bundleUUID uuid.UUID) (*Bundle, error) {
var result Bundle
err := c.userClient.CallContext(ctx, &result, "pm_getBundleByUuid", bundleUUID)
err := c.c.CallContext(ctx, &result, "pm_getBundleByUuid", bundleUUID)
if err != nil {
return nil, err
}
Expand All @@ -149,7 +134,7 @@ func (c *client) GetBundleByUUID(ctx context.Context, bundleUUID uuid.UUID) (*Bu

func (c *client) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (uint64, error) {
var result hexutil.Uint64
err := c.userClient.CallContext(ctx, &result, "eth_getTransactionCount", address, blockNrOrHash)
err := c.c.CallContext(ctx, &result, "eth_getTransactionCount", address, blockNrOrHash)
if err != nil {
return 0, err
}
Expand Down
14 changes: 13 additions & 1 deletion pkg/paymasterclient/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,25 @@ type TransactionArgs struct {
Data *hexutil.Bytes `json:"data"`
}

// IsSponsorableOptions defines the options for the IsSponsorable method.
type IsSponsorableOptions struct {
// PrivatePolicyUUID is the UUID of a private policy.
// This field should only be set when using a sponsor client.
// When set, it allows the use of a private policy for the sponsorable check.
// For paymaster client calls, this field should be left empty.
PrivatePolicyUUID string
}

// SendRawTransactionOptions defines the options for the SendRawTransaction method.
type SendRawTransactionOptions struct {
// PrivatePolicyUUID is the UUID of a private policy.
// This field should only be set when using a sponsor client.
// When set, it allows the use of a private policy for the transaction.
// For paymaster client calls, this field should be left empty.
PrivatePolicyUUID string
UserAgent string

// UserAgent is an optional field to set a custom User-Agent header for the request.
UserAgent string
}

type IsSponsorableResponse struct {
Expand Down
12 changes: 11 additions & 1 deletion pkg/sponsorclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/gofrs/uuid"

"github.com/node-real/megafuel-go-sdk/pkg/paymasterclient"
)

// Client interface defines the methods available for the sponsor client.
// This client combines sponsor-specific functionality with all paymaster client (USER API) methods.
type Client interface {
// AddToWhitelist adds a list of values to the whitelist of a policy
AddToWhitelist(ctx context.Context, args WhiteListArgs) (bool, error)
Expand All @@ -22,10 +26,12 @@ type Client interface {
GetUserSpendData(ctx context.Context, fromAddress common.Address, policyUUID uuid.UUID) (*UserSpendData, error)
// GetPolicySpendData returns the spend data of a policy
GetPolicySpendData(ctx context.Context, policyUUID uuid.UUID) (*PolicySpendData, error)
paymasterclient.Client
}

type client struct {
c *rpc.Client
paymasterclient.Client
}

func New(ctx context.Context, url string, options ...rpc.ClientOption) (Client, error) {
Expand All @@ -34,7 +40,11 @@ func New(ctx context.Context, url string, options ...rpc.ClientOption) (Client,
return nil, err
}

return &client{c}, nil
c2, err := paymasterclient.New(ctx, url, options...)
if err != nil {
return nil, err
}
return &client{c, c2}, nil
}

func (c *client) AddToWhitelist(ctx context.Context, args WhiteListArgs) (bool, error) {
Expand Down
50 changes: 1 addition & 49 deletions test/paymaster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ func paymasterSetup(t *testing.T) (*ethclient.Client, paymasterclient.Client, st
}

// Create a PaymasterClient (for transaction sending)
sponsorURL := fmt.Sprintf("https://open-platform-ap.nodereal.io/%s/megafuel-testnet/97", key)
paymasterClient, err := paymasterclient.New(context.Background(), PAYMASTER_URL, &sponsorURL)
paymasterClient, err := paymasterclient.New(context.Background(), PAYMASTER_URL)
if err != nil {
log.Fatalf("Failed to create PaymasterClient: %v", err)
}
Expand Down Expand Up @@ -158,51 +157,4 @@ func TestPaymasterAPI(t *testing.T) {
count, err := paymasterClient.GetTransactionCount(context.Background(), common.HexToAddress(RECIPIENT_ADDRESS), rpc.BlockNumberOrHash{BlockNumber: &blockNumber})
require.NoError(t, err, "failed to GetTransactionCount")
assert.Greater(t, count, hexutil.Uint64(0))

// Fetch the current nonce for the account to ensure the transaction can be processed sequentially.
nonce, err = client.PendingNonceAt(context.Background(), fromAddress)
require.NoError(t, err, "Failed to get nonce")

// Define the recipient Ethereum address.
toAddress = common.HexToAddress(RECIPIENT_ADDRESS)

// Construct a new Ethereum transaction.
tx = types.NewTx(&types.LegacyTx{
Nonce: nonce,
GasPrice: big.NewInt(0),
Gas: 21000,
To: &toAddress,
Value: big.NewInt(0),
})

// Prepare a transaction argument for checking if it's sponsorable.
gasLimit = tx.Gas()

privatePolicySponsorableTx := paymasterclient.TransactionArgs{
To: &toAddress,
From: fromAddress,
Value: (*hexutil.Big)(big.NewInt(0)),
Gas: (*hexutil.Uint64)(&gasLimit),
Data: &hexutil.Bytes{},
}

privatePolicySponsorableInfo, err := paymasterClient.IsSponsorable(context.Background(), privatePolicySponsorableTx, &paymasterclient.IsSponsorableOptions{PrivatePolicyUUID: PRIVATE_POLICY})
require.NoError(t, err, "Error checking sponsorable private policy status")
require.True(t, privatePolicySponsorableInfo.Sponsorable)

// Retrieve the blockchain ID to ensure that the transaction is signed correctly.
chainID, err = client.ChainID(context.Background())
require.NoError(t, err, "Failed to get chain ID")

// Sign the transaction using the provided private key and the current chain ID.
signedTx, err = types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
require.NoError(t, err, "Failed to sign transaction")

// Marshal the signed transaction into a binary format for transmission.
txInput, err = signedTx.MarshalBinary()
require.NoError(t, err, "Failed to marshal transaction")

_, err = paymasterClient.SendRawTransaction(context.Background(), txInput, &paymasterclient.SendRawTransactionOptions{PrivatePolicyUUID: PRIVATE_POLICY, UserAgent: "Test User Agent"})
require.NoError(t, err, "Failed to send sponsorable private policy transaction")
log.Infof("Sponsorable private policy transaction sent: %s", signedTx.Hash())
}
Loading

0 comments on commit bedb86c

Please sign in to comment.