Turnkey

Upgrade existing Turnkey wallets to Smart Wallets to enable gasless transactions, batching, and more in under 10 minutes. Keep Turnkey for key management, no wallet migration needed. Add battle-tested transaction infrastructure using EIP-7702 to upgrade EOAs to Smart Wallets:

Don’t have a wallet set up yet? Start with the Smart Wallet Quickstart to create one in minutes.

Setup

Use Turnkey’s @turnkey/sdk-server package with Smart Wallets infrastructure in a server environment.

Installation

$npm install @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
># or
>yarn add @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
># or
>pnpm add @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem

Prerequisites: Get your keys

  • Alchemy API Key:
    • Go to the dashboard
    • Create or select an app and copy the API key
  • Gas sponsorship Policy ID:
    • Create a gas sponsorship policy in the dashboard and copy its Policy ID
  • Turnkey Credentials:
    • Go to the Turnkey Dashboard
    • Create or select an organization and copy the Organization ID
    • Create an API key pair and copy the API Public Key and Private Key
    • Create or import a private key and copy the Private Key ID (UUID)
    • Note the Ethereum address associated with that private key

Both the Smart Wallets configuration and the gas sponsorship policy must be linked to the application behind your API key for sponsorship to work.

Create Smart Wallet Client from Turnkey

Create a helper function that converts a Turnkey wallet into an Alchemy Smart Wallet client:

import { 
class WalletClientSigner

Represents a wallet client signer for smart accounts, providing methods to get the address, sign messages, sign typed data, and sign 7702 authorizations.

WalletClientSigner
} from "@aa-sdk/core";
import {
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
,
const baseSepolia: Chain
baseSepolia
} from "@account-kit/infra";
import {
function createSmartWalletClient<TAccount extends Address | undefined = undefined>(params: SmartWalletClientParams<TAccount>): SmartWalletClient<TAccount>

Creates a smart wallet client that can be used to interact with a smart account.

createSmartWalletClient
} from "@account-kit/wallet-client";
import {
class Turnkey
Turnkey
} from "@turnkey/sdk-server";
import {
function createAccount(input: { client: TurnkeyClient | TurnkeyBrowserClient | TurnkeyServerClient; organizationId: string; signWith: string; ethereumAddress?: string; }): Promise<LocalAccount>
createAccount
} from "@turnkey/viem";
import {
function createWalletClient<transport extends Transport, chain extends Chain | undefined = undefined, accountOrAddress extends Account | Address | undefined = undefined, rpcSchema extends RpcSchema | undefined = undefined>(parameters: WalletClientConfig<transport, chain, accountOrAddress, rpcSchema>): WalletClient<transport, chain, ParseAccount<accountOrAddress>, rpcSchema>

Creates a Wallet Client with a given Transport configured for a Chain.

Docs: https://viem.sh/docs/clients/wallet

A Wallet Client is an interface to interact with Ethereum Account(s) and provides the ability to retrieve accounts, execute transactions, sign messages, etc. through Wallet Actions.

The Wallet Client supports signing over: JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc). Local Accounts (e.g. private key/mnemonic wallets).

createWalletClient
} from "viem";
const
const ALCHEMY_API_KEY: "your-alchemy-api-key"
ALCHEMY_API_KEY
= "your-alchemy-api-key";
const
const ALCHEMY_GAS_POLICY_ID: "your-gas-policy-id"
ALCHEMY_GAS_POLICY_ID
= "your-gas-policy-id";
const
const TURNKEY_ORGANIZATION_ID: "your-turnkey-org-id"
TURNKEY_ORGANIZATION_ID
= "your-turnkey-org-id";
const
const TURNKEY_API_PRIVATE_KEY: "your-turnkey-api-private-key"
TURNKEY_API_PRIVATE_KEY
= "your-turnkey-api-private-key";
const
const TURNKEY_API_PUBLIC_KEY: "your-turnkey-api-public-key"
TURNKEY_API_PUBLIC_KEY
= "your-turnkey-api-public-key";
async function
function createTurnkeySmartWalletClient({ privateKeyId, walletAddress, }: { privateKeyId: any; walletAddress: any; }): Promise<SmartWalletClient<undefined>>
createTurnkeySmartWalletClient
({
privateKeyId: any
privateKeyId
,
walletAddress: any
walletAddress
,
}) { // Initialize Turnkey SDK const
const turnkey: Turnkey
turnkey
= new
new Turnkey(config: TurnkeySDKServerConfig): Turnkey
Turnkey
({
TurnkeySDKServerConfig.defaultOrganizationId: string
defaultOrganizationId
:
const TURNKEY_ORGANIZATION_ID: "your-turnkey-org-id"
TURNKEY_ORGANIZATION_ID
,
TurnkeySDKServerConfig.apiBaseUrl: string
apiBaseUrl
: "https://api.turnkey.com",
TurnkeySDKServerConfig.apiPrivateKey: string
apiPrivateKey
:
const TURNKEY_API_PRIVATE_KEY: "your-turnkey-api-private-key"
TURNKEY_API_PRIVATE_KEY
,
TurnkeySDKServerConfig.apiPublicKey: string
apiPublicKey
:
const TURNKEY_API_PUBLIC_KEY: "your-turnkey-api-public-key"
TURNKEY_API_PUBLIC_KEY
,
}); // Create a viem account using Turnkey's signing capabilities const
const baseTurnkeyAccount: { address: Address; nonceManager?: NonceManager | undefined; sign?: ((parameters: { hash: Hash; }) => Promise<Hex>) | undefined | undefined; signAuthorization?: ((parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>) | undefined | undefined; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; ... 4 more ...; type: "local"; }
baseTurnkeyAccount
= await
function createAccount(input: { client: TurnkeyClient | TurnkeyBrowserClient | TurnkeyServerClient; organizationId: string; signWith: string; ethereumAddress?: string; }): Promise<LocalAccount>
createAccount
({
client: TurnkeyClient | TurnkeyBrowserClient | TurnkeyServerClient
client
:
const turnkey: Turnkey
turnkey
.
TurnkeyServerSDK.apiClient: (apiCredentials?: ApiCredentials) => TurnkeyApiClient
apiClient
(),
organizationId: string
organizationId
:
const TURNKEY_ORGANIZATION_ID: "your-turnkey-org-id"
TURNKEY_ORGANIZATION_ID
,
signWith: string
signWith
:
privateKeyId: any
privateKeyId
,
ethereumAddress?: string | undefined
ethereumAddress
:
walletAddress: any
walletAddress
,
}); // Wrap the account to fix the double 0x prefix bug in @turnkey/viem 0.14.8 const
const turnkeyAccount: { signAuthorization: (authorization: any) => Promise<{ r: string; s: string; address: Address; chainId: number; nonce: number; v: bigint; yParity?: undefined; } | { r: string; s: string; address: Address; chainId: number; nonce: number; v: bigint; yParity?: number | undefined; } | { r: string; s: string; address: Address; chainId: number; nonce: number; v?: bigint | undefined; yParity: number; }>; address: Address; nonceManager?: NonceManager | undefined; sign?: ((parameters: { hash: Hash; }) => Promise<Hex>) | undefined | undefined; ... 5 more ...; type: "local"; }
turnkeyAccount
= {
...
const baseTurnkeyAccount: { address: Address; nonceManager?: NonceManager | undefined; sign?: ((parameters: { hash: Hash; }) => Promise<Hex>) | undefined | undefined; signAuthorization?: ((parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>) | undefined | undefined; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; ... 4 more ...; type: "local"; }
baseTurnkeyAccount
,
signAuthorization: (authorization: any) => Promise<{ r: string; s: string; address: Address; chainId: number; nonce: number; v: bigint; yParity?: undefined; } | { r: string; s: string; address: Address; chainId: number; nonce: number; v: bigint; yParity?: number | undefined; } | { r: string; s: string; address: Address; chainId: number; nonce: number; v?: bigint | undefined; yParity: number; }>
signAuthorization
: async (
authorization: any
authorization
) => {
if (!
const baseTurnkeyAccount: { address: Address; nonceManager?: NonceManager | undefined; sign?: ((parameters: { hash: Hash; }) => Promise<Hex>) | undefined | undefined; signAuthorization?: ((parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>) | undefined | undefined; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; ... 4 more ...; type: "local"; }
baseTurnkeyAccount
.
signAuthorization?: ((parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>) | undefined
signAuthorization
) {
throw new
var Error: ErrorConstructor new (message?: string) => Error
Error
("signAuthorization not available on Turnkey account");
} const
const result: SignAuthorizationReturnType
result
= await
const baseTurnkeyAccount: { address: Address; nonceManager?: NonceManager | undefined; sign?: ((parameters: { hash: Hash; }) => Promise<Hex>) | undefined | undefined; signAuthorization?: ((parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>) | undefined | undefined; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; ... 4 more ...; type: "local"; }
baseTurnkeyAccount
.
signAuthorization?: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>
signAuthorization
(
authorization: any
authorization
);
// Strip the extra 0x prefix if present return { ...
const result: SignAuthorizationReturnType
result
,
r: string
r
:
const result: SignAuthorizationReturnType
result
.
r: `0x${string}`
r
.
String.replace(searchValue: { [Symbol.replace](string: string, replaceValue: string): string; }, replaceValue: string): string (+3 overloads)

Passes a string and replaceValue to the [Symbol.replace] method on searchValue. This method is expected to implement its own replacement algorithm.

replace
(/^0x0x/, "0x"),
s: string
s
:
const result: SignAuthorizationReturnType
result
.
s: `0x${string}`
s
.
String.replace(searchValue: { [Symbol.replace](string: string, replaceValue: string): string; }, replaceValue: string): string (+3 overloads)

Passes a string and replaceValue to the [Symbol.replace] method on searchValue. This method is expected to implement its own replacement algorithm.

replace
(/^0x0x/, "0x"),
}; }, }; // Create wallet client with Turnkey account const
const walletClient: { account: Account | { address: `0x${string}`; type: "json-rpc"; } | undefined; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; cacheTime: number; ccipRead?: false | { request?: (parameters: CcipRequestParameters) => Promise<CcipRequestReturnType>; } | undefined; chain: Chain; experimental_blockTag?: BlockTag | undefined; ... 34 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
walletClient
=
createWalletClient<AlchemyTransport, Chain, `0x${string}` | Account | undefined, undefined>(parameters: { account?: `0x${string}` | Account | undefined; cacheTime?: number | undefined | undefined; ccipRead?: { request?: (parameters: CcipRequestParameters) => Promise<CcipRequestReturnType>; } | false | undefined | undefined; ... 5 more ...; rpcSchema?: undefined; }): { ...; }

Creates a Wallet Client with a given Transport configured for a Chain.

Docs: https://viem.sh/docs/clients/wallet

A Wallet Client is an interface to interact with Ethereum Account(s) and provides the ability to retrieve accounts, execute transactions, sign messages, etc. through Wallet Actions.

The Wallet Client supports signing over: JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc). Local Accounts (e.g. private key/mnemonic wallets).

createWalletClient
({
account?: `0x${string}` | Account | undefined

The Account to use for the Client. This will be used for Actions that require an account as an argument.

account
:
const turnkeyAccount: { signAuthorization: (authorization: any) => Promise<{ r: string; s: string; address: Address; chainId: number; nonce: number; v: bigint; yParity?: undefined; } | { r: string; s: string; address: Address; chainId: number; nonce: number; v: bigint; yParity?: number | undefined; } | { r: string; s: string; address: Address; chainId: number; nonce: number; v?: bigint | undefined; yParity: number; }>; address: Address; nonceManager?: NonceManager | undefined; sign?: ((parameters: { hash: Hash; }) => Promise<Hex>) | undefined | undefined; ... 5 more ...; type: "local"; }
turnkeyAccount
,
chain?: Chain | undefined

Chain for the client.

chain
:
const baseSepolia: Chain
baseSepolia
,
transport: AlchemyTransport

The RPC transport

transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
:
const ALCHEMY_API_KEY: "your-alchemy-api-key"
ALCHEMY_API_KEY
}),
}); // Create signer from wallet client const
const signer: WalletClientSigner
signer
= new
new WalletClientSigner(client: WalletClient, signerType: string): WalletClientSigner

Initializes a signer with a given wallet client and signer type.

WalletClientSigner
(
const walletClient: { account: Account | { address: `0x${string}`; type: "json-rpc"; } | undefined; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; cacheTime: number; ccipRead?: false | { request?: (parameters: CcipRequestParameters) => Promise<CcipRequestReturnType>; } | undefined; chain: Chain; experimental_blockTag?: BlockTag | undefined; ... 34 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
walletClient
, "turnkey-custom");
// Create and return the Smart Wallet client return
createSmartWalletClient<undefined>(params: SmartWalletClientParams<undefined>): SmartWalletClient<undefined>

Creates a smart wallet client that can be used to interact with a smart account.

createSmartWalletClient
({
chain: Chain
chain
:
const baseSepolia: Chain
baseSepolia
,
transport: AlchemyTransport
transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
:
const ALCHEMY_API_KEY: "your-alchemy-api-key"
ALCHEMY_API_KEY
}),
signer: SmartAccountSigner<any>
signer
,
policyId?: string | undefined
policyId
:
const ALCHEMY_GAS_POLICY_ID: "your-gas-policy-id"
ALCHEMY_GAS_POLICY_ID
,
}); }

Send Transactions with EIP-7702

Send gasless transactions using EIP-7702 to upgrade the Turnkey wallet to a smart wallet:

const 
const TURNKEY_PRIVATE_KEY_ID: "your-private-key-uuid"
TURNKEY_PRIVATE_KEY_ID
= "your-private-key-uuid";
const
const TURNKEY_WALLET_ADDRESS: "0x..."
TURNKEY_WALLET_ADDRESS
= "0x...";
async function
function sendTransaction(): Promise<void>
sendTransaction
() {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout

See util.format() for more information.

log
("Wallet address:",
const TURNKEY_WALLET_ADDRESS: "0x..."
TURNKEY_WALLET_ADDRESS
);
// Create Smart Wallet client from Turnkey wallet const
const client: any
client
= await
any
createTurnkeySmartWalletClient
({
privateKeyId: string
privateKeyId
:
const TURNKEY_PRIVATE_KEY_ID: "your-private-key-uuid"
TURNKEY_PRIVATE_KEY_ID
,
walletAddress: string
walletAddress
:
const TURNKEY_WALLET_ADDRESS: "0x..."
TURNKEY_WALLET_ADDRESS
,
}); // Send a gasless transaction with EIP-7702 const
const result: any
result
= await
const client: any
client
.
any
sendCalls
({
from: string
from
:
const TURNKEY_WALLET_ADDRESS: "0x..."
TURNKEY_WALLET_ADDRESS
,
calls: { to: string; value: string; data: string; }[]
calls
: [
{
to: string
to
: "0x0000000000000000000000000000000000000000",
value: string
value
: "0x0",
data: string
data
: "0x",
}, ],
capabilities: { eip7702Auth: boolean; paymasterService: { policyId: any; }; }
capabilities
: {
eip7702Auth: boolean
eip7702Auth
: true,
paymasterService: { policyId: any; }
paymasterService
: {
policyId: any
policyId
:
any
ALCHEMY_GAS_POLICY_ID
,
}, }, });
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout

See util.format() for more information.

log
("Transaction sent:",
const result: any
result
.
any
preparedCallIds
[0]);
}

Wait for Transaction Confirmation

Wait for the transaction to be confirmed onchain:

async function 
function sendAndWaitForTransaction(): Promise<void>
sendAndWaitForTransaction
() {
const
const client: any
client
= await
any
createTurnkeySmartWalletClient
({
privateKeyId: any
privateKeyId
:
any
TURNKEY_PRIVATE_KEY_ID
,
walletAddress: any
walletAddress
:
any
TURNKEY_WALLET_ADDRESS
,
}); const
const result: any
result
= await
const client: any
client
.
any
sendCalls
({
from: any
from
:
any
TURNKEY_WALLET_ADDRESS
,
calls: { to: string; value: string; data: string; }[]
calls
: [
{
to: string
to
: "0x0000000000000000000000000000000000000000",
value: string
value
: "0x0",
data: string
data
: "0x",
}, ],
capabilities: { eip7702Auth: boolean; paymasterService: { policyId: any; }; }
capabilities
: {
eip7702Auth: boolean
eip7702Auth
: true,
paymasterService: { policyId: any; }
paymasterService
: {
policyId: any
policyId
:
any
ALCHEMY_GAS_POLICY_ID
,
}, }, });
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout

See util.format() for more information.

log
("Waiting for confirmation...");
// Wait for the transaction to be confirmed const
const txStatus: any
txStatus
= await
const client: any
client
.
any
waitForCallsStatus
({
id: any
id
:
const result: any
result
.
any
preparedCallIds
[0],
timeout: number
timeout
: 60_000, // 60 seconds
}); const
const txnHash: any
txnHash
=
const txStatus: any
txStatus
.
any
receipts
?.[0]?.
any
transactionHash
;
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout

See util.format() for more information.

log
("Transaction confirmed!");
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout

See util.format() for more information.

log
("Transaction hash:",
const txnHash: any
txnHash
);
}

Batch Multiple Transactions

Send multiple transactions in a single batch:

const 
const result: any
result
= await
any
client
.
any
sendCalls
({
from: any
from
:
any
TURNKEY_WALLET_ADDRESS
,
calls: { to: string; data: string; value: string; }[]
calls
: [
{
to: string
to
: "0x...",
data: string
data
: "0x...",
value: string
value
: "0x0" },
{
to: string
to
: "0x...",
data: string
data
: "0x...",
value: string
value
: "0x0" },
{
to: string
to
: "0x...",
data: string
data
: "0x...",
value: string
value
: "0x0" },
],
capabilities: { eip7702Auth: boolean; paymasterService: { policyId: any; }; }
capabilities
: {
eip7702Auth: boolean
eip7702Auth
: true,
paymasterService: { policyId: any; }
paymasterService
: {
policyId: any
policyId
:
any
ALCHEMY_GAS_POLICY_ID
,
}, }, });

Notes

  • EIP-7702: Upgrades the Turnkey wallet to a smart wallet at transaction time without migration
  • Gas Sponsorship: Use paymasterService with your policy ID for gasless transactions
  • Server-Side: This example uses Turnkey’s @turnkey/sdk-server package which is designed for server environments. However, the @account-kit packages (@account-kit/wallet-client, @account-kit/infra, etc.) can be used in any JavaScript environment including browsers, React Native, and Node.js