Skip to content

Commit

Permalink
Merge pull request #24 from FastLane-Labs/refactor/update-api-interface
Browse files Browse the repository at this point in the history
Refactor/update api interface
  • Loading branch information
0x1NotMe authored Oct 9, 2024
2 parents f521c68 + e88f207 commit 5794f0a
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 39 deletions.
82 changes: 64 additions & 18 deletions src/backend/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export interface IBackend {
/**
* Submit a user operation to the backend
* @summary Submit a user operation to the backend
* @param {UserOperation} [userOp] The user operation
* @param {string[]} [hints] Hints for solvers
* @param {UserOperation} userOp The user operation
* @param {string[]} hints Hints for solvers
* @param {*} [extra] Extra parameters
* @returns {Promise<string>} The hash of the user operation
*/
Expand Down Expand Up @@ -50,7 +50,7 @@ export interface IBackend {
/**
* Submit user/solvers/dApp operations to the backend for bundling
* @summary Submit a bundle of user/solvers/dApp operations to the backend
* @param {Bundle} [bundle] The user/solvers/dApp operations to be bundled
* @param {Bundle} bundle The user/solvers/dApp operations to be bundled
* @param {*} [extra] Extra parameters
* @returns {Promise<string>} The result message
*/
Expand All @@ -77,12 +77,37 @@ export interface IBackend {
wait?: boolean,
extra?: any,
): Promise<string>;

/**
* Get the full bundle for a given user operation
* @summary Get the full bundle for a given user operation
* @param {UserOperation} userOp The user operation
* @param {boolean} [wait] Hold the request until having a response
* @param {*} [extra] Extra parameters
* @returns {Promise<Bundle>} The full bundle
*/
getBundleForUserOp(
userOp: UserOperation,
hints: string[],
wait?: boolean,
extra?: any,
): Promise<Bundle>;

_getBundleForUserOp(
userOp: UserOperation,
hints: string[],
wait?: boolean,
extra?: any,
): Promise<Bundle>;
}

export abstract class BaseBackend implements IBackend {
protected hooksControllers: IHooksController[] = [];
protected chainId: number;

constructor(protected params: { [k: string]: string } = {}) {}
constructor(protected params: { [k: string]: string } = {}) {
this.chainId = Number(params.chainId);
}

addHooksControllers(hooksControllers: IHooksController[]): void {
this.hooksControllers.push(...hooksControllers);
Expand Down Expand Up @@ -186,32 +211,53 @@ export abstract class BaseBackend implements IBackend {
return atlasTxHash;
}

async _submitUserOperation(
async getBundleForUserOp(
userOp: UserOperation,
hints: string[],
wait?: boolean,
extra?: any,
): Promise<string> {
throw new Error("Method not implemented.");
): Promise<Bundle> {
// Pre hooks
for (const hooksController of this.hooksControllers) {
userOp = await hooksController.preGetBundleForUserOp(userOp);
}

// Implemented by subclass
let bundle = await this._getBundleForUserOp(userOp, hints, wait, extra);

// Post hooks
for (const hooksController of this.hooksControllers) {
bundle = await hooksController.postGetBundleForUserOp(bundle);
}

return bundle;
}

async _getSolverOperations(
abstract _submitUserOperation(
userOp: UserOperation,
hints: string[],
extra?: any,
): Promise<string>;

abstract _getSolverOperations(
userOp: UserOperation,
userOpHash: string,
wait?: boolean,
extra?: any,
): Promise<SolverOperation[]> {
throw new Error("Method not implemented.");
}
): Promise<SolverOperation[]>;

async _submitBundle(bundle: Bundle, extra?: any): Promise<string> {
throw new Error("Method not implemented.");
}
abstract _submitBundle(bundle: Bundle, extra?: any): Promise<string>;

async _getBundleHash(
abstract _getBundleHash(
userOpHash: string,
wait?: boolean,
extra?: any,
): Promise<string> {
throw new Error("Method not implemented.");
}
): Promise<string>;

abstract _getBundleForUserOp(
userOp: UserOperation,
hints: string[],
wait?: boolean,
extra?: any,
): Promise<Bundle>;
}
108 changes: 108 additions & 0 deletions src/backend/fastlane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ const ROUTES: Map<string, Route> = new Map([
path: "/bundleHash",
},
],
[
"getBundleForUserOp",
{
method: "POST",
path: "/bundle",
},
],
]);

export class FastlaneBackend extends BaseBackend {
Expand Down Expand Up @@ -176,6 +183,46 @@ export class FastlaneBackend extends BaseBackend {
throw new Error(reponseBody.message);
}
}

/**
* Get the full bundle for a given user operation
* @summary Get the full bundle for a given user operation
* @param {UserOperation} userOp The user operation
* @param {boolean} [wait] Hold the request until having a response
* @param {*} [extra] Extra parameters
* @returns {Promise<Bundle>} The full bundle
*/
public async _getBundleForUserOp(
userOp: UserOperation,
hints: string[],
wait?: boolean,
extra?: any,
): Promise<Bundle> {
const localVarFetchArgs = FastlaneApiFetchParamCreator().getBundleForUserOp(
userOp,
hints,
wait,
extra,
);
const response = await fetch(
this.params["basePath"] + localVarFetchArgs.url,
localVarFetchArgs.options,
);
if (response.status >= 200 && response.status < 300) {
const bundleData = await response.json();
return OperationBuilder.newBundle(
this.chainId,
OperationBuilder.newUserOperation(bundleData.userOperation),
bundleData.solverOperations.map((op: any) =>
OperationBuilder.newSolverOperation(op),
),
OperationBuilder.newDAppOperation(bundleData.dAppOperation),
);
} else {
const responseBody = await response.json();
throw new Error(responseBody.message);
}
}
}

const FastlaneApiFetchParamCreator = function () {
Expand Down Expand Up @@ -393,6 +440,67 @@ const FastlaneApiFetchParamCreator = function () {
options.headers,
);

return {
url: url.format(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Get the full bundle for a given user operation
* @summary Get the full bundle for a given user operation
* @param {UserOperation} userOp The user operation
* @param {string[]} hints Hints for solvers
* @param {boolean} [wait] Hold the request until having a response
* @param {*} [options] Override http request option.
*/
getBundleForUserOp(
userOp: UserOperation,
hints: string[],
wait?: boolean,
options: any = {},
): FetchArgs {
if (userOp === null || userOp === undefined) {
throw "Required parameter userOp was null or undefined when calling getBundle.";
}
const localVarUrlObj = url.parse(
ROUTES.get("getBundleForUserOp")?.path as string,
true,
);
const localVarRequestOptions = Object.assign(
{ method: ROUTES.get("getBundleForUserOp")?.method as string },
options,
);
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;

localVarHeaderParameter["Content-Type"] = "application/json";

if (wait !== undefined) {
localVarQueryParameter["wait"] = wait;
}

localVarUrlObj.query = Object.assign(
{},
localVarUrlObj.query,
localVarQueryParameter,
options.query,
);
localVarUrlObj.search = null;
localVarRequestOptions.headers = Object.assign(
{},
localVarHeaderParameter,
options.headers,
);

const requestBody = {
userOperation: userOp.toStruct(),
hints: hints,
};

localVarRequestOptions.body = JSON.stringify(requestBody, (_, v) =>
typeof v === "bigint" ? toQuantity(v) : v,
);

return {
url: url.format(localVarUrlObj),
options: localVarRequestOptions,
Expand Down
12 changes: 12 additions & 0 deletions src/backend/hooks/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export interface IHooksController {
preGetBundleHash(userOphash: string): Promise<string>;

postGetBundleHash(atlasTxHash: string): Promise<string>;

preGetBundleForUserOp(userOp: UserOperation): Promise<UserOperation>;

postGetBundleForUserOp(bundle: Bundle): Promise<Bundle>;
}

export interface IHooksControllerConstructable {
Expand Down Expand Up @@ -84,4 +88,12 @@ export abstract class BaseHooksController implements IHooksController {
async postGetBundleHash(atlasTxHash: string): Promise<string> {
return atlasTxHash;
}

async preGetBundleForUserOp(userOp: UserOperation): Promise<UserOperation> {
return userOp;
}

async postGetBundleForUserOp(bundle: Bundle): Promise<Bundle> {
return bundle;
}
}
51 changes: 38 additions & 13 deletions src/backend/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import { UserOperation, SolverOperation, Bundle } from "../operation";
import { flagTrustedOpHash } from "../utils";
import { chainConfig } from "../config";

const chainId = 11155111;

export class MockBackend extends BaseBackend {
private submittedBundles: { [key: string]: Bundle } = {};

constructor() {
super();
constructor(params: { [k: string]: string } = {}) {
super(params);
}

private generateUserOpHash(userOp: UserOperation): string {
return userOp.hash(
chainConfig[this.chainId].eip712Domain,
flagTrustedOpHash(userOp.callConfig()),
);
}

/**
Expand All @@ -29,10 +34,8 @@ export class MockBackend extends BaseBackend {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
extra?: any,
): Promise<string> {
return userOp.hash(
chainConfig[chainId].eip712Domain,
flagTrustedOpHash(userOp.callConfig()),
);
const userOpHash = this.generateUserOpHash(userOp);
return userOpHash;
}

/**
Expand Down Expand Up @@ -88,10 +91,7 @@ export class MockBackend extends BaseBackend {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
extra?: any,
): Promise<string> {
const userOpHash = bundle.userOperation.hash(
chainConfig[chainId].eip712Domain,
flagTrustedOpHash(bundle.userOperation.callConfig()),
);
const userOpHash = this.generateUserOpHash(bundle.userOperation);
this.submittedBundles[userOpHash] = bundle;
return userOpHash;
}
Expand All @@ -113,10 +113,35 @@ export class MockBackend extends BaseBackend {
): Promise<string> {
const bundle = this.submittedBundles[userOpHash];
if (bundle === undefined) {
throw "Bundle not found";
throw new Error(`Bundle not found for userOpHash: ${userOpHash}`);
}

// Simulate a random transaction hash
return keccak256(bundle.dAppOperation.abiEncode());
}

/**
* Get the full bundle for a given user operation
* @summary Get the full bundle for a given user operation
* @param {UserOperation} userOp The user operation
* @param {boolean} [wait] Hold the request until having a response
* @param {*} [extra] Extra parameters
* @returns {Promise<Bundle>} The full bundle
*/
public async _getBundleForUserOp(
userOp: UserOperation,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
hints: string[],
// eslint-disable-next-line @typescript-eslint/no-unused-vars
wait?: boolean,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
extra?: any,
): Promise<Bundle> {
const userOpHash = this.generateUserOpHash(userOp);
const bundle = this.submittedBundles[userOpHash];
if (bundle === undefined) {
throw new Error(`Bundle not found for userOp: ${userOpHash}`);
}
return bundle;
}
}
3 changes: 2 additions & 1 deletion src/operation/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,11 @@ export abstract class OperationBuilder {
}

public static newBundle(
chainId: number,
userOp: UserOperation,
solverOps: SolverOperation[],
dAppOp: DAppOperation,
): Bundle {
return new Bundle(userOp, solverOps, dAppOp);
return new Bundle(chainId, userOp, solverOps, dAppOp);
}
}
Loading

0 comments on commit 5794f0a

Please sign in to comment.