Skip to content

Commit

Permalink
Ensure Sponsorship Policy on Paymaster Requests (#76)
Browse files Browse the repository at this point in the history

I have disabled the free-for-all sponsoring. Now one must provide a sponsorshipPolicy at request time.
Pimlico now has a setting to enable testnet sponsorship on policies.
I am not entirely happy with the solution here, but its an ok start.
  • Loading branch information
bh2smith authored Oct 18, 2024
1 parent 21a9d82 commit f23d49b
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 20 deletions.
6 changes: 2 additions & 4 deletions examples/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,14 @@ export async function loadEnv(): Promise<ScriptEnv> {

export async function loadArgs(): Promise<UserOptions> {
return yargs(hideBin(process.argv))
.option("usePaymaster", {
type: "boolean",
.option("sponsorshipPolicy", {
type: "string",
description: "Have transaction sponsored by paymaster service",
default: false,
})
.option("recoveryAddress", {
type: "string",
description:
"Recovery address to be attached as owner of the Safe (immediately adter deployment)",
default: undefined,
})
.option("safeSaltNonce", {
type: "string",
Expand Down
6 changes: 3 additions & 3 deletions examples/send-tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dotenv.config();
async function main(): Promise<void> {
const [
{ pimlicoKey, nearAccountId, nearAccountPrivateKey },
{ mpcContractId, recoveryAddress, usePaymaster },
{ mpcContractId, recoveryAddress, sponsorshipPolicy },
] = await Promise.all([loadEnv(), loadArgs()]);
const chainId = 11155111;
const txManager = await NearSafe.create({
Expand Down Expand Up @@ -44,7 +44,7 @@ async function main(): Promise<void> {
const unsignedUserOp = await txManager.buildTransaction({
chainId,
transactions,
usePaymaster,
sponsorshipPolicy,
});
console.log("Unsigned UserOp", unsignedUserOp);
const safeOpHash = await txManager.opHash(chainId, unsignedUserOp);
Expand All @@ -56,7 +56,7 @@ async function main(): Promise<void> {
const sufficientFunded = await txManager.sufficientlyFunded(
chainId,
transactions,
usePaymaster ? 0n : gasCost
!!sponsorshipPolicy ? 0n : gasCost
);
if (!sufficientFunded) {
console.warn(
Expand Down
13 changes: 9 additions & 4 deletions src/lib/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ function bundlerUrl(chainId: number, apikey: string): string {
return `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${apikey}`;
}

type SponsorshipPolicy = { sponsorshipPolicyId: string };

type BundlerRpcSchema = [
{
Method: "pm_sponsorUserOperation";
Parameters: [UnsignedUserOperation, Address];
// TODO(bh2smith): Add possiblity to not supply policy:
// [UnsignedUserOperation, Address]
Parameters: [UnsignedUserOperation, Address, SponsorshipPolicy];
ReturnType: PaymasterData;
},
{
Expand Down Expand Up @@ -65,18 +69,19 @@ export class Erc4337Bundler {

async getPaymasterData(
rawUserOp: UnsignedUserOperation,
usePaymaster: boolean,
safeNotDeployed: boolean
safeNotDeployed: boolean,
sponsorshipPolicy?: string
): Promise<PaymasterData> {
// TODO: Keep this option out of the bundler
if (usePaymaster) {
if (sponsorshipPolicy) {
console.log("Requesting paymaster data...");
return handleRequest<PaymasterData>(() =>
this.client.request({
method: "pm_sponsorUserOperation",
params: [
{ ...rawUserOp, signature: PLACEHOLDER_SIG },
this.entryPointAddress,
{ sponsorshipPolicyId: sponsorshipPolicy },
],
})
);
Expand Down
16 changes: 8 additions & 8 deletions src/near-safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ export class NearSafe {
async buildTransaction(args: {
chainId: number;
transactions: MetaTransaction[];
usePaymaster: boolean;
sponsorshipPolicy?: string;
}): Promise<UserOperation> {
const { transactions, usePaymaster, chainId } = args;
const { transactions, sponsorshipPolicy, chainId } = args;
if (transactions.length === 0) {
throw new Error("Empty transaction set!");
}
Expand Down Expand Up @@ -189,8 +189,8 @@ export class NearSafe {

const paymasterData = await bundler.getPaymasterData(
rawUserOp,
usePaymaster,
!safeDeployed
!safeDeployed,
sponsorshipPolicy
);

const unsignedUserOp = { ...rawUserOp, ...paymasterData };
Expand Down Expand Up @@ -228,11 +228,11 @@ export class NearSafe {
*/
async encodeSignRequest(
signRequest: SignRequestData,
usePaymaster: boolean
sponsorshipPolicy?: string
): Promise<EncodedTxData> {
const { payload, evmMessage, hash } = await this.requestRouter(
signRequest,
usePaymaster
sponsorshipPolicy
);
return {
nearPayload: await this.nearAdapter.mpcContract.encodeSignatureRequestTx({
Expand Down Expand Up @@ -420,7 +420,7 @@ export class NearSafe {
*/
async requestRouter(
{ method, chainId, params }: SignRequestData,
usePaymaster: boolean
sponsorshipPolicy?: string
): Promise<{
evmMessage: string;
payload: number[];
Expand Down Expand Up @@ -460,7 +460,7 @@ export class NearSafe {
const userOp = await this.buildTransaction({
chainId,
transactions,
usePaymaster,
...(sponsorshipPolicy ? { sponsorshipPolicy } : {}),
});
const opHash = await this.opHash(chainId, userOp);
return {
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export interface PaymasterData {
*/
export interface UserOptions {
/** Whether to use a paymaster for gas fee coverage. */
usePaymaster: boolean;
sponsorshipPolicy?: string;
/** The unique nonce used to differentiate multiple Safe setups. */
safeSaltNonce: string;
/** The NEAR contract ID for the MPC contract. */
Expand Down

0 comments on commit f23d49b

Please sign in to comment.