How to use a Passport Protocol signer with permissionless.js
Passport is an MPC-based programmable, distributed, and non-custodial key management system, that allows users to generate wallets, scoped to their application, either via user Passkeys or any developer defined authentication method. Our signer allows you to sign messages and transactions with a Passport Network account.
Setup
To use Passport with permissionless.js, you'll first need to make sure you have configured a scope for your application. For this you can follow the guides below:
- Refer to the Passport documentation for instructions on setting up your application with Passport.
- For a primer on setting up your scope you can check here.
Integration
Integrating permissionless.js with Passport is straightforward after setting up the project. Passport provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
Create the Passport signer
After following the Passport documentation, you will have access to a Passport WalletClient
object that you can use to create a SmartAccountSigner
object, both examples show how to register a user
but if you already have a registered user you can skip the registration step. For Developer Owned Auth, the authentication is handled on the developer's side. For more detail on both you can check the authentication guide.
import { Passport, TESTNET_RSA_PUBLIC_KEY } from "@0xpass/passport";
import { WebauthnSigner } from "@0xpass/webauthn-signer";
import { createPassportClient } from "@0xpass/passport-viem";=
import { walletClientToSmartAccountSigner } from "permissionless";
import { http } from "viem";
import { sepolia } from "viem/chains";
const passport = new Passport({
scope_id: "scope_id",
signer: new WebauthnSigner({
rpId: "rpId",
rpName: "rpName",
}),
enclave_public_key: TESTNET_RSA_PUBLIC_KEY,
});
const fallbackProvider = http("https://rpc.ankr.com/eth_sepolia");
await passport.setupEncryption();
await passport.register({
username: "test",
userDisplayName: "test",
});
const [authenticatedHeader] = await passport.authenticate({
username: "test",
userDisplayName: "test",
});
const client = await createPassportClient(
authenticatedHeader,
fallbackProvider,
sepolia,
"https://tiramisu.0xpass.io"
);
const smartAccountSigner = walletClientToSmartAccountSigner(client);
Use with permissionless.js
import { signerToSimpleSmartAccount } from "permissionless/accounts"
import { createPimlicoBundlerClient, createPimlicoPaymasterClient } from "permissionless/clients/pimlico"
import { createPublicClient, http } from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { sepolia } from "viem/chains"
const pimlicoRpcUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=<api-key>`
const pimlicoPaymaster = createPimlicoPaymasterClient({
transport: http(pimlicoRpcUrl),
entryPoint: ENTRYPOINT_ADDRESS_V06
})
const bundlerClient = createPimlicoBundlerClient({
transport: http(pimlicoRpcUrl),
entryPoint: ENTRYPOINT_ADDRESS_V06,
})
const publicClient = createPublicClient({
transport: http("https://rpc.ankr.com/eth_sepolia"),
chain: sepolia,
})
const smartAccount = await signerToSimpleSmartAccount(publicClient, {
signer: smartAccountSigner,
entryPoint: ENTRYPOINT_ADDRESS_V06,
})
const smartAccountClient = createSmartAccountClient({
account: smartAccount,
entryPoint: ENTRYPOINT_ADDRESS_V06,
chain: sepolia, // or whatever chain you are using
bundlerTransport: http(pimlicoRpcUrl, {
timeout: 30_000 // optional
}),
middleware: {
gasPrice: async () => (await bundlerClient.getUserOperationGasPrice()).fast,
sponsorUserOperation: pimlicoPaymaster.sponsorUserOperation,
},
})