Skip to content

Commit babb35e

Browse files
committed
fix: address comments
1 parent b4f557d commit babb35e

File tree

5 files changed

+101
-116
lines changed

5 files changed

+101
-116
lines changed

src/paymasterclient.ts

Lines changed: 22 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -57,61 +57,38 @@ export type Bundle = {
5757
readonly ChainID: number
5858
}
5959

60-
export class PaymasterClient {
61-
private userClient: ethers.JsonRpcProvider
62-
private sponsorClient?: ethers.JsonRpcProvider
63-
64-
/**
65-
* Creates a new PaymasterClient with an optional sponsorUrl.
66-
* If sponsorUrl is provided, it enables the use of private policies.
67-
* The sponsorUrl is typically in the format: "https://open-platform-ap.nodereal.io/xxxx/megafuel-testnet"
68-
* IsSponsorableOptions.PrivatePolicyUUID and SendRawTransactionOptions.PrivatePolicyUUID
69-
* can only be used when sponsorUrl is provided.
70-
*
71-
* @param userUrl The URL for the user's JsonRpcProvider
72-
* @param sponsorUrl Optional URL for the sponsor's JsonRpcProvider
73-
* @param network Optional network information
74-
* @param options Optional JsonRpcApiProviderOptions
75-
*/
76-
constructor(
77-
userUrl: string | FetchRequest,
78-
sponsorUrl?: string | FetchRequest,
79-
network?: Networkish,
80-
options?: JsonRpcApiProviderOptions
81-
) {
82-
this.userClient = new ethers.JsonRpcProvider(userUrl, network, options)
83-
if (sponsorUrl) {
84-
this.sponsorClient = new ethers.JsonRpcProvider(sponsorUrl, network, options)
85-
}
60+
export class PaymasterClient extends ethers.JsonRpcProvider {
61+
constructor(url?: string | FetchRequest, network?: Networkish, options?: JsonRpcApiProviderOptions) {
62+
super(url, network, options)
8663
}
8764

8865
async chainID(): Promise<string> {
89-
return await this.userClient.send('eth_chainId', [])
66+
return await this.send('eth_chainId', [])
9067
}
9168

9269
async isSponsorable(tx: TransactionRequest, opts: IsSponsorableOptions = {}): Promise<IsSponsorableResponse> {
93-
if (this.sponsorClient && opts.PrivatePolicyUUID) {
94-
const newConnection = this.sponsorClient._getConnection();
70+
if (opts.PrivatePolicyUUID) {
71+
const newConnection = this._getConnection();
9572
newConnection.setHeader("X-MegaFuel-Policy-Uuid", opts.PrivatePolicyUUID);
9673

9774
const sponsorProviderWithHeader = new ethers.JsonRpcProvider(
9875
newConnection,
99-
(this.sponsorClient as any)._network,
76+
(this as any)._network,
10077
{
101-
staticNetwork: (this.sponsorClient as any)._network,
102-
batchMaxCount: (this.sponsorClient as any).batchMaxCount,
103-
polling: (this.sponsorClient as any).polling
78+
staticNetwork: (this as any)._network,
79+
batchMaxCount: (this as any).batchMaxCount,
80+
polling: (this as any).polling
10481
}
10582
);
10683

10784
return await sponsorProviderWithHeader.send('pm_isSponsorable', [tx]);
10885
}
109-
return await this.userClient.send('pm_isSponsorable', [tx]);
86+
return await this.send('pm_isSponsorable', [tx]);
11087
}
11188

11289
async sendRawTransaction(signedTx: string, opts: SendRawTransactionOptions = {}): Promise<string> {
113-
if (this.sponsorClient && (opts.UserAgent || opts.PrivatePolicyUUID)) {
114-
const newConnection = this.sponsorClient._getConnection();
90+
if (opts.UserAgent || opts.PrivatePolicyUUID) {
91+
const newConnection = this._getConnection();
11592

11693
if (opts.UserAgent) {
11794
newConnection.setHeader("User-Agent", opts.UserAgent);
@@ -122,42 +99,34 @@ export class PaymasterClient {
12299

123100
const sponsorProvider = new ethers.JsonRpcProvider(
124101
newConnection,
125-
(this.sponsorClient as any)._network,
102+
(this as any)._network,
126103
{
127-
staticNetwork: (this.sponsorClient as any)._network,
128-
batchMaxCount: (this.sponsorClient as any).batchMaxCount,
129-
polling: (this.sponsorClient as any).polling
104+
staticNetwork: (this as any)._network,
105+
batchMaxCount: (this as any).batchMaxCount,
106+
polling: (this as any).polling
130107
}
131108
);
132109

133110
if (opts.PrivatePolicyUUID) {
134111
return await sponsorProvider.send('eth_sendRawTransaction', [signedTx]);
135112
}
136113
}
137-
return await this.userClient.send('eth_sendRawTransaction', [signedTx]);
114+
return await this.send('eth_sendRawTransaction', [signedTx]);
138115
}
139116

140117
async getGaslessTransactionByHash(hash: string): Promise<GaslessTransaction> {
141-
return await this.userClient.send('eth_getGaslessTransactionByHash', [hash])
118+
return await this.send('eth_getGaslessTransactionByHash', [hash])
142119
}
143120

144121
async getSponsorTxByTxHash(hash: string): Promise<SponsorTx> {
145-
return await this.userClient.send('pm_getSponsorTxByTxHash', [hash])
122+
return await this.send('pm_getSponsorTxByTxHash', [hash])
146123
}
147124

148125
async getSponsorTxByBundleUuid(bundleUuid: string): Promise<SponsorTx> {
149-
return await this.userClient.send('pm_getSponsorTxByBundleUuid', [bundleUuid])
126+
return await this.send('pm_getSponsorTxByBundleUuid', [bundleUuid])
150127
}
151128

152129
async getBundleByUuid(bundleUuid: string): Promise<Bundle> {
153-
return await this.userClient.send('pm_getBundleByUuid', [bundleUuid])
154-
}
155-
156-
getSponsorProvider(): ethers.JsonRpcProvider | undefined {
157-
return this.sponsorClient
158-
}
159-
160-
getUserProvider(): ethers.JsonRpcProvider {
161-
return this.userClient
130+
return await this.send('pm_getBundleByUuid', [bundleUuid])
162131
}
163132
}

src/sponsorclient.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {ethers, FetchRequest, JsonRpcApiProviderOptions, Networkish} from 'ethers'
2-
import type {AddressLike} from 'ethers/src.ts/address'
3-
import type {BigNumberish} from 'ethers/src.ts/utils'
1+
import { FetchRequest, JsonRpcApiProviderOptions, Networkish } from "ethers"
2+
import { PaymasterClient } from "./paymasterclient"
3+
import type { AddressLike } from "ethers/src.ts/address"
4+
import type { BigNumberish } from "ethers/src.ts/utils"
45

56
export enum WhitelistType {
67
FromAccountWhitelist = 'FromAccountWhitelist',
@@ -42,8 +43,12 @@ export type PolicySpendData = {
4243
ChainID: number
4344
}
4445

45-
export class SponsorClient extends ethers.JsonRpcProvider {
46-
constructor(url?: string | FetchRequest, network?: Networkish, options?: JsonRpcApiProviderOptions) {
46+
export class SponsorClient extends PaymasterClient {
47+
constructor(
48+
url?: string | FetchRequest,
49+
network?: Networkish,
50+
options?: JsonRpcApiProviderOptions
51+
) {
4752
super(url, network, options)
4853
}
4954

@@ -63,7 +68,10 @@ export class SponsorClient extends ethers.JsonRpcProvider {
6368
return this.send('pm_getWhitelist', [params])
6469
}
6570

66-
async getUserSpendData(fromAddress: AddressLike, policyUUID: string): Promise<UserSpendData> {
71+
async getUserSpendData(
72+
fromAddress: AddressLike,
73+
policyUUID: string
74+
): Promise<UserSpendData> {
6775
return this.send('pm_getUserSpendData', [fromAddress, policyUUID])
6876
}
6977

tests/paymaster.spec.ts

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import {
77
transformToGaslessTransaction,
88
delay, transformSponsorTxResponse, transformBundleResponse,
99
} from './utils'
10-
import {IsSponsorableOptions, SendRawTransactionOptions} from '../src/paymasterclient'
11-
import {TOKEN_CONTRACT_ADDRESS, CHAIN_ID, RECIPIENT_ADDRESS, PRIVATE_POLICY_UUID} from './env'
10+
import {TOKEN_CONTRACT_ADDRESS, CHAIN_ID, RECIPIENT_ADDRESS} from './env'
1211
import {ethers} from 'ethers'
1312

1413
let TX_HASH = ''
@@ -35,7 +34,7 @@ describe('paymasterQuery', () => {
3534
test('should successfully determine if transaction is sponsorable', async () => {
3635
const tokenContract = new ethers.Contract(TOKEN_CONTRACT_ADDRESS, tokenAbi, wallet)
3736
const tokenAmount = ethers.parseUnits('0', 18)
38-
const nonce = await paymasterClient.getUserProvider().getTransactionCount(wallet.address, 'pending')
37+
const nonce = await paymasterClient.getTransactionCount(wallet.address, 'pending')
3938

4039
const transaction = await tokenContract.transfer.populateTransaction(RECIPIENT_ADDRESS.toLowerCase(), tokenAmount)
4140
transaction.from = wallet.address
@@ -97,54 +96,4 @@ describe('paymasterQuery', () => {
9796
expect(sponsorTx.TxHash).toEqual(tx.TxHash)
9897
}, 13000)
9998
})
100-
101-
102-
/**
103-
* Test for checking if a private policy transaction is sponsorable.
104-
*/
105-
describe('isSponsorable', () => {
106-
test('should successfully determine if transaction is sponsorable', async () => {
107-
const tokenContract = new ethers.Contract(TOKEN_CONTRACT_ADDRESS, tokenAbi, wallet)
108-
const tokenAmount = ethers.parseUnits('0', 18)
109-
const nonce = await paymasterClient.getUserProvider().getTransactionCount(wallet.address, 'pending')
110-
111-
const transaction = await tokenContract.transfer.populateTransaction(RECIPIENT_ADDRESS.toLowerCase(), tokenAmount)
112-
transaction.from = wallet.address
113-
transaction.nonce = nonce
114-
transaction.gasLimit = BigInt(100000)
115-
transaction.chainId = BigInt(CHAIN_ID)
116-
transaction.gasPrice = BigInt(0)
117-
118-
const safeTransaction = {
119-
...transaction,
120-
gasLimit: transaction.gasLimit.toString(),
121-
chainId: transaction.chainId.toString(),
122-
gasPrice: transaction.gasPrice.toString(),
123-
}
124-
125-
console.log('Prepared transaction:', safeTransaction)
126-
127-
const opt: IsSponsorableOptions = {
128-
PrivatePolicyUUID: PRIVATE_POLICY_UUID
129-
};
130-
131-
const resRaw = await paymasterClient.isSponsorable(safeTransaction, opt)
132-
const res = transformIsSponsorableResponse(resRaw)
133-
expect(res.Sponsorable).toEqual(true)
134-
135-
const txOpt: SendRawTransactionOptions = {
136-
PrivatePolicyUUID: PRIVATE_POLICY_UUID,
137-
UserAgent: "TEST USER AGENT"
138-
};
139-
140-
const signedTx = await wallet.signTransaction(safeTransaction)
141-
try {
142-
const tx = await paymasterClient.sendRawTransaction(signedTx,txOpt)
143-
TX_HASH = tx
144-
console.log('Transaction hash received:', TX_HASH)
145-
} catch (error) {
146-
console.error('Transaction failed:', error)
147-
}
148-
}, 100000) // Extends the default timeout as this test involves network calls
149-
})
15099
})

tests/sponsor.spec.ts

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import {describe, expect, test} from '@jest/globals'
2-
import {sponsorClient} from './utils'
32
import {WhitelistType} from '../src'
4-
import {POLICY_UUID, ACCOUNT_ADDRESS, CONTRACT_METHOD} from './env'
3+
import {POLICY_UUID, ACCOUNT_ADDRESS, CONTRACT_METHOD, TOKEN_CONTRACT_ADDRESS, CHAIN_ID, RECIPIENT_ADDRESS, PRIVATE_POLICY_UUID} from './env'
4+
import {ethers} from 'ethers'
5+
import {IsSponsorableOptions, SendRawTransactionOptions} from '../src/paymasterclient'
6+
import {
7+
sponsorClient,
8+
wallet,
9+
tokenAbi,
10+
transformIsSponsorableResponse,
11+
} from './utils'
12+
13+
let TX_HASH = ''
514

615
/**
716
* Test suite for Sponsor API methods involving whitelist management and spend data retrieval.
@@ -192,4 +201,54 @@ describe('sponsorQuery', () => {
192201
console.log('Re-addition to FromAccountWhitelist response:', res)
193202
})
194203
})
204+
205+
206+
/**
207+
* Test for checking if a private policy transaction is sponsorable.
208+
*/
209+
describe('isSponsorable', () => {
210+
test('should successfully determine if transaction is sponsorable', async () => {
211+
const tokenContract = new ethers.Contract(TOKEN_CONTRACT_ADDRESS, tokenAbi, wallet)
212+
const tokenAmount = ethers.parseUnits('0', 18)
213+
const nonce = await sponsorClient.getTransactionCount(wallet.address, 'pending')
214+
215+
const transaction = await tokenContract.transfer.populateTransaction(RECIPIENT_ADDRESS.toLowerCase(), tokenAmount)
216+
transaction.from = wallet.address
217+
transaction.nonce = nonce
218+
transaction.gasLimit = BigInt(100000)
219+
transaction.chainId = BigInt(CHAIN_ID)
220+
transaction.gasPrice = BigInt(0)
221+
222+
const safeTransaction = {
223+
...transaction,
224+
gasLimit: transaction.gasLimit.toString(),
225+
chainId: transaction.chainId.toString(),
226+
gasPrice: transaction.gasPrice.toString(),
227+
}
228+
229+
console.log('Prepared transaction:', safeTransaction)
230+
231+
const opt: IsSponsorableOptions = {
232+
PrivatePolicyUUID: PRIVATE_POLICY_UUID
233+
};
234+
235+
const resRaw = await sponsorClient.isSponsorable(safeTransaction, opt)
236+
const res = transformIsSponsorableResponse(resRaw)
237+
expect(res.Sponsorable).toEqual(true)
238+
239+
const txOpt: SendRawTransactionOptions = {
240+
PrivatePolicyUUID: PRIVATE_POLICY_UUID,
241+
UserAgent: "TEST USER AGENT"
242+
};
243+
244+
const signedTx = await wallet.signTransaction(safeTransaction)
245+
try {
246+
const tx = await sponsorClient.sendRawTransaction(signedTx,txOpt)
247+
TX_HASH = tx
248+
console.log('Transaction hash received:', TX_HASH)
249+
} catch (error) {
250+
console.error('Transaction failed:', error)
251+
}
252+
}, 100000) // Extends the default timeout as this test involves network calls
253+
})
195254
})

tests/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import {
1010
import {CHAIN_ID, SPONSOR_URL, CHAIN_URL, PAYMASTER_URL, PRIVATE_KEY, TOKEN_CONTRACT_ADDRESS} from './env'
1111
import {ethers} from 'ethers'
1212

13-
export const sponsorClient = new SponsorClient(SPONSOR_URL, undefined, {staticNetwork: ethers.Network.from(Number(CHAIN_ID))})
13+
export const sponsorClient = new SponsorClient(SPONSOR_URL+"/"+CHAIN_ID, undefined, {staticNetwork: ethers.Network.from(Number(CHAIN_ID))})
1414

1515
// Provider for assembling the transaction (e.g., testnet)
1616
export const assemblyProvider = new ethers.JsonRpcProvider(CHAIN_URL)
1717

1818
// Provider for sending the transaction (e.g., could be a different network or provider)
19-
export const paymasterClient = new PaymasterClient(PAYMASTER_URL,SPONSOR_URL+"/"+CHAIN_ID, undefined, {staticNetwork: ethers.Network.from(Number(CHAIN_ID))})
19+
export const paymasterClient = new PaymasterClient(PAYMASTER_URL, undefined, {staticNetwork: ethers.Network.from(Number(CHAIN_ID))})
2020

2121
export const wallet = new ethers.Wallet(PRIVATE_KEY, assemblyProvider)
2222
// ERC20 token ABI (only including the transfer function)

0 commit comments

Comments
 (0)