Skip to content

Commit

Permalink
attempt safe-kit
Browse files Browse the repository at this point in the history
  • Loading branch information
bh2smith committed Jun 28, 2024
1 parent ed63d8e commit cb99424
Show file tree
Hide file tree
Showing 4 changed files with 497 additions and 58 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"fmt": "prettier -w ."
},
"dependencies": {
"@safe-global/api-kit": "^2.4.2",
"@safe-global/relay-kit": "^3.0.2",
"@safe-global/safe-deployments": "^1.37.0",
"@safe-global/safe-modules-deployments": "^2.2.0",
"dotenv": "^16.4.5",
Expand Down
61 changes: 61 additions & 0 deletions src/alt-safe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import SafeApiKit, { SafeInfoResponse } from "@safe-global/api-kit";
import { Safe4337Pack } from "@safe-global/relay-kit";
import { ethers } from "ethers";

const SAFE_4337_MODULE = "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226";
const isRelevantSafe = (safe: SafeInfoResponse) =>
safe.threshold === 1 && safe.fallbackHandler === SAFE_4337_MODULE;
const sanitizeAddress = (dirty: string) => ethers.getAddress(dirty);

export async function existingSafe(
signer: string,
chainId: bigint,
): Promise<string | undefined> {
const apiKit = new SafeApiKit({
chainId,
});
const safes = (await apiKit.getSafesByOwner(signer)).safes;
const safeInfos = await Promise.all(
safes.map((safeAddress) => apiKit.getSafeInfo(safeAddress)),
);
const relevantSafes = safeInfos.filter((info) => isRelevantSafe(info));
console.log("Relevant Safes", relevantSafes)
if (relevantSafes.length > 0) {
if (relevantSafes.length > 1) {
console.warn(
`Found multiple relevant Safes for ${signer} - using the first`,
);
}
return relevantSafes[0].address;
}
}

export async function loadSafeKit(
rpcUrl: string,
bundlerUrl: string,
nearSigner: string,
): Promise<Safe4337Pack> {
const signer = sanitizeAddress(nearSigner);
const provider = new ethers.JsonRpcProvider(rpcUrl);
const safeAddress = await existingSafe(
signer,
(await provider.getNetwork()).chainId,
);
let options = safeAddress
? { safeAddress }
: {
owners: [signer],
threshold: 1,
};
const safe4337Pack = await Safe4337Pack.init({
provider: rpcUrl,
bundlerUrl,
options,
customContracts: {
// Why do I need to specify this?
safe4337ModuleAddress: SAFE_4337_MODULE
}
// ...
});
return safe4337Pack;
}
97 changes: 55 additions & 42 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ContractSuite } from "./safe";
import { getNearSignature } from "./near";
import { Erc4337Bundler } from "./bundler";
import { assertFunded, packSignature } from "./util";
import { loadSafeKit } from "./alt-safe";

dotenv.config();
const { SAFE_SALT_NONCE, ERC4337_BUNDLER_URL, ETH_RPC, RECOVERY_ADDRESS } =
Expand All @@ -26,58 +27,70 @@ async function main() {
`NearEth Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`,
);

const safePack = await ContractSuite.init(provider);
const bundler = new Erc4337Bundler(
ERC4337_BUNDLER_URL!,
await safePack.entryPoint.getAddress(),
);
// const safePack = await ContractSuite.init(provider);
// const bundler = new Erc4337Bundler(
// ERC4337_BUNDLER_URL!,
// await safePack.entryPoint.getAddress(),
// );

// // TODO(bh2smith): add the recovery as first tx (more deterministic)
// const owners = [
// nearAdapter.address,
// ...(RECOVERY_ADDRESS ? [RECOVERY_ADDRESS] : []),
// ];
// const setup = await safePack.getSetup(owners);
// const safeAddress = await safePack.addressForSetup(setup, SAFE_SALT_NONCE);
// console.log("Safe Address:", safeAddress);
// const safeNotDeployed = await assertFunded(
// provider,
// safeAddress,
// argv.usePaymaster,
// );

// TODO(bh2smith): add the recovery as first tx (more deterministic)
const owners = [
const safeKit = await loadSafeKit(
ETH_RPC!,
ERC4337_BUNDLER_URL!,
nearAdapter.address,
...(RECOVERY_ADDRESS ? [RECOVERY_ADDRESS] : []),
];
const setup = await safePack.getSetup(owners);
const safeAddress = await safePack.addressForSetup(setup, SAFE_SALT_NONCE);
console.log("Safe Address:", safeAddress);
const safeNotDeployed = await assertFunded(
provider,
safeAddress,
argv.usePaymaster,
);
const safeAddress = await safeKit.protocolKit.getAddress();
console.log("Safe Address:", safeAddress);

const userOp = await safeKit.createTransaction({
transactions: [{ to: nearAdapter.address, value: "1", data: "0x69" }], // Transaction Data:
});
console.log(userOp);
// TODO(bh2smith) Bundler Gas Feed: `pimlico_getUserOperationGasPrice`
const gasFees = await provider.getFeeData();
// const gasFees = await provider.getFeeData();

const rawUserOp = await safePack.buildUserOp(
{ to: nearAdapter.address, value: 1n, data: "0x69" }, // Transaction Data:
safeAddress,
gasFees,
setup,
safeNotDeployed,
SAFE_SALT_NONCE || "0",
);
const paymasterData = await bundler.getPaymasterData(
rawUserOp,
argv.usePaymaster,
safeNotDeployed,
);
// const rawUserOp = await safePack.buildUserOp(
// { to: nearAdapter.address, value: 1n, data: "0x69" }, // Transaction Data:
// safeAddress,
// gasFees,
// setup,
// safeNotDeployed,
// SAFE_SALT_NONCE || "0",
// );
// const paymasterData = await bundler.getPaymasterData(
// rawUserOp,
// argv.usePaymaster,
// safeNotDeployed,
// );

const unsignedUserOp = { ...rawUserOp, ...paymasterData };
console.log("Unsigned UserOp", unsignedUserOp);
const safeOpHash = await safePack.getOpHash(unsignedUserOp, paymasterData);
// const unsignedUserOp = { ...rawUserOp, ...paymasterData };
// console.log("Unsigned UserOp", unsignedUserOp);
// const safeOpHash = await safePack.getOpHash(unsignedUserOp, paymasterData);

console.log("Signing with Near...");
const signature = await getNearSignature(nearAdapter, safeOpHash);
// console.log("Signing with Near...");
// const signature = await getNearSignature(nearAdapter, safeOpHash);

const userOpHash = await bundler.sendUserOperation({
...unsignedUserOp,
signature: packSignature(signature),
});
console.log("UserOp Hash", userOpHash);
// const userOpHash = await bundler.sendUserOperation({
// ...unsignedUserOp,
// signature: packSignature(signature),
// });
// console.log("UserOp Hash", userOpHash);

const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
console.log("userOp Receipt", userOpReceipt);
// const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
// console.log("userOp Receipt", userOpReceipt);
}

main().catch((err) => {
Expand Down
Loading

0 comments on commit cb99424

Please sign in to comment.