Skip to content

Commit

Permalink
wip get sigs working on v7 passkey safe
Browse files Browse the repository at this point in the history
  • Loading branch information
peterferguson committed Mar 17, 2024
1 parent c3ea237 commit 5b6b338
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 46 deletions.
118 changes: 81 additions & 37 deletions apps/dapp/src/components/create-payment-button.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { Button } from "@/components/ui/button";
import { formatMintCouponToModuleExecutionCalldata } from "@/lib/example-nft";
import {
getUserOperationHash,
bundlerClient,
paymasterClient,
publicClient,
} from "@/lib/permissionless";
import { getPayeeOrigin, getPaymentOrigin } from "@/lib/utils";
import {
ENTRYPOINT_ADDRESS_V07,
getAccountNonce,
getUserOperationHash,
type UserOperation,
} from "permissionless";
import { bundlerClient, publicClient } from "@/lib/permissionless";
import { getPayeeOrigin, getPaymentOrigin } from "@/lib/utils";
import {
fallbackToIframeCredentialCreation,
getAvailableCredentials,
getPaymentDetails,
payWithSPC,
getAvailableCredentials,
fallbackToIframeCredentialCreation,
} from "spc-lib";
import type { Address } from "viem";
import { baseSepolia } from "viem/chains";
Expand All @@ -33,39 +37,12 @@ export function CreatePaymentButton() {
return fallbackToIframeCredentialCreation();
}

const nonce = await getAccountNonce(publicClient, {
sender: address,
entryPoint: ENTRYPOINT_ADDRESS_V07,
});

console.log("nonce ", nonce);

const userOperation = {
sender: address,
nonce,
callData: formatMintCouponToModuleExecutionCalldata(),
// accountGasLimits: 8000000n,
callGasLimit: 500305n,
verificationGasLimit: 8005650n,
preVerificationGas: 506135n,
maxFeePerGas: 113000000n,
maxPriorityFeePerGas: 113000100n,
signature: "0x" as const,
} satisfies UserOperation<"v0.7">;

const gasEstimate = await bundlerClient.estimateUserOperationGas({
userOperation,
});

console.log("gasEstimate ", gasEstimate);

const userOperationHash = getUserOperationHash({
userOperation,
chainId: baseSepolia.id,
entryPoint: ENTRYPOINT_ADDRESS_V07,
});
const { userOperation, userOperationHash } =
await getMintNftUserOperation(address);

await payWithSPC(
const {
response: { signature },
} = await payWithSPC(
{
rpId: getPaymentOrigin().replace("https://", ""),
allowedCredentials: [credential.credentialId],
Expand All @@ -76,9 +53,76 @@ export function CreatePaymentButton() {
getPaymentDetails("0.0000001"),
address,
);

await bundlerClient.sendUserOperation({
userOperation: {
...userOperation,
signature: `0x${signature}`,
},
});
}}
>
Create Payment
</Button>
);
}

const getMintNftUserOperation = async (address: Address) => {
const nonce = await getAccountNonce(publicClient, {
sender: address,
entryPoint: ENTRYPOINT_ADDRESS_V07,
});

console.log("nonce ", nonce);

let userOperation: UserOperation<"v0.7"> = {
sender: address,
nonce,
callData: formatMintCouponToModuleExecutionCalldata(),
// accountGasLimits: 8000000n,
callGasLimit: 900080n,
verificationGasLimit: 8005650n,
preVerificationGas: 801330n,

maxFeePerGas: 113000000n,
maxPriorityFeePerGas: 113000100n,
signature: "0x",
};

// const gasEstimate = await bundlerClient.estimateUserOperationGas({
// userOperation,
// });

// console.log("gasEstimate ", gasEstimate);

// const paymasterData = await paymasterClient.sponsorUserOperation({
// userOperation,
// });

// console.log("paymasterData", paymasterData);

// userOperation = { ...userOperation, ...paymasterData };

// const userOperation = {
// callData:
// "0x7bb374280000000000000000000000004a56fd1d63d99978fdb3ac5c152ea122514b6792000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000443efc46400000000000000000000000000000000000000000000000000000000",
// initCode: "0x",
// callGasLimit: "117688",
// verificationGasLimit: "61421",
// preVerificationGas: "285339",
// maxFeePerGas: "75550508",
// maxPriorityFeePerGas: "110000",
// nonce: "0",
// paymasterAndData: "0x",
// sender: "0x2fe7892721d8279cec0f8687c4b2e922ca7b76b0",
// signature: "0x",
// };

const userOperationHash = getUserOperationHash({
userOperation,
chainId: baseSepolia.id,
entryPoint: ENTRYPOINT_ADDRESS_V07,
});

return { userOperation, userOperationHash };
};
7 changes: 2 additions & 5 deletions apps/dapp/src/lib/permissionless.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBundlerClient } from "permissionless";
import { ENTRYPOINT_ADDRESS_V07, createBundlerClient } from "permissionless";
import { pimlicoPaymasterActions } from "permissionless/actions/pimlico";
import { createClient, createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";
Expand All @@ -11,16 +11,13 @@ const pimlicoTransport = (version: "v1" | "v2") =>
`https://api.pimlico.io/${version}/${baseSepolia.id}/rpc?apikey=${pimlicoApiKey}`,
);

export const ENTRYPOINT_ADDRESS_V07 =
"0x0000000071727De22E5E9d8BAf0edAc6f37da032" as const;

// create clients --------------------------------------------

export const publicClient = createPublicClient({
transport: http(baseSepolia.rpcUrls.default.http[0]),
});

export const pimlicoPaymasterClient = createClient({
export const paymasterClient = createClient({
chain: baseSepolia,
transport: pimlicoTransport("v2"),
}).extend(pimlicoPaymasterActions(ENTRYPOINT_ADDRESS_V07));
Expand Down
48 changes: 46 additions & 2 deletions apps/wallet/src/pages/relay/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,25 @@ const relaySafeDeploy = async ({
account: privateKeyToAccount(import.meta.env.PRIVATE_KEY),
});

// const { request, result } = await publicClient.simulateContract({
// account: walletClient.account,
// address: V6_BUNDLER_ONIT_FACTORY_ADDRESS,
// abi: createSafe4337Abi,
// functionName: "createSafe4337",
// args: [publicKey, 0n],
// });

// const { request, result } = await publicClient.simulateContract({
// account: walletClient.account,
// address: V6_BUNDLER_ONIT_FACTORY_ADDRESS,
// abi: createBasic4337Abi,
// functionName: "createBasic4337",
// args: [publicKey, 0n],
// });

const { request, result } = await publicClient.simulateContract({
account: walletClient.account,
address: ONIT_FACTORY_ADDRESS,
address: V7_BUNDLER_SAFE_FACTORY_ADDRESS,
abi: createSafe4337Abi,
functionName: "createSafe4337",
args: [publicKey, 0n],
Expand All @@ -91,9 +107,18 @@ const relaySafeDeploy = async ({

// abis --------------------------------------------

const ONIT_FACTORY_ADDRESS =
const V7_BUNDLER_SAFE_ONIT_FACTORY_ADDRESS =
"0x42ab880ea77fc7a09eb6ba0fe82fbc9901c114b6" as const;

const V6_BUNDLER_SAFE_ONIT_FACTORY_ADDRESS =
"0xa4025cc96a042a4522F9115478D4d527F954a40E" as const;

const V6_BUNDLER_ONIT_FACTORY_ADDRESS =
"0x70b3b72f7737c017888d64e60c5bfee6ca226b85" as const;

const V7_BUNDLER_SAFE_FACTORY_ADDRESS =
"0x758f1ce181e74b4eb3d38441a0b2b117991c5cc8" as const;

const createSafe4337Abi = [
{
type: "function",
Expand All @@ -112,3 +137,22 @@ const createSafe4337Abi = [
stateMutability: "nonpayable",
},
] as const;

const createBasic4337Abi = [
{
type: "function",
name: "createBasic4337",
inputs: [
{
name: "passkeyPublicKey",
type: "uint256[2]",
internalType: "uint256[2]",
},
{ name: "nonce", type: "uint256", internalType: "uint256" },
],
outputs: [
{ name: "onitAccountAddress", type: "address", internalType: "address" },
],
stateMutability: "nonpayable",
},
] as const;
5 changes: 3 additions & 2 deletions packages/spc-lib/src/pay-with-spc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const payWithSPC = async (
try {
response = await request.show();

console.log("spc response", response);
console.log("spc response", response, response.details);

// response.details is a PublicKeyCredential, with a clientDataJSON that
// contains the transaction data for verification by the issuing bank.
Expand All @@ -86,14 +86,15 @@ export const payWithSPC = async (
response: {
clientDataJSON: fromBuffer(cred.response.clientDataJSON),
authenticatorData: fromBuffer(cred.response.authenticatorData),
signature: fromBuffer(cred.response.signature),
signature: fromBuffer(cred.response.signature, "hex"),
userHandle: fromBuffer(cred.response.userHandle),
},
};

await response.complete("success");

/* send response.details to the issuing bank for verification */
return serialisableCredential;
} catch (err) {
await response.complete("fail");
/* SPC cannot be used; merchant should fallback to traditional flows */
Expand Down

0 comments on commit 5b6b338

Please sign in to comment.