Skip to content

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

SimpleAccount
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,
	},
})