How to use a Turnkey signer with permissionless.js
Turnkey is a key infrastructure provider with a great developer API and a powerful security policy engine.
By combining permissionless.js with Turnkey, you can create custodial AA wallets whose security is provided by Turnkey, with powerful functionalities such as sponsoring gas, batching transactions, etc.
Setup
To use Turnkey with permissionless.js, first create an application that integrates with Turnkey.
- Refer to the Turnkey documentation site for instructions on setting up an application with the Turnkey.
- For a quick start, Turnkey provides examples, available here.
Integration
Integrating permissionless.js with Turnkey is straightforward after setting up the project. Turnkey provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
Create the TurnkeyClient and a Turnkey viem account
After following the Turnkey documentation, you will have access to a TurnkeyClient
. An example is shown below that you can use to create a SmartAccountSigner
object:
import { TurnkeyClient } from "@turnkey/http"
import { createAccount } from "@turnkey/viem"
import { walletClientToSmartAccountSigner } from "permissionless"
import { createWalletClient, http } from "viem"
// Param options here will be specific to your project. See the Turnkey docs for more info.
const turnkeyClient = new TurnkeyClient({ baseUrl: "" }, stamper)
const turnkeyAccount = await createAccount({
client: turnkeyClient,
organizationId: subOrganizationId, // Your subOrganization id
signWith: signWith, // Your suborganization `signWith` param.
})
// Create a SmartAccountSigner from the turnkeyAccount
const walletClient = createWalletClient({
account: turnkeyAccount,
transport: http("https://rpc.ankr.com/eth_sepolia"),
})
const smartAccountSigner = walletClientToSmartAccountSigner(walletClient)
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,
},
})