> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Tempo

> Add the Tempo extensions, read Tempo state with a viem PublicClient, and send TIP-20 transfers and Tempo transactions from embedded wallets

[Tempo](https://tempo.xyz) is EVM compatible but has no native token: every transfer moves a TIP-20 stablecoin, and fees are paid in TIP-20 tokens too. Tempo therefore ships as its own package, `@dynamic-labs-sdk/tempo`, with its own network registry, separate from the EVM extensions on [Adding EVM Extensions](/docs/javascript/reference/evm/adding-evm-extensions).

<Info>
  Earlier releases exposed Tempo from `@dynamic-labs-sdk/evm/tempo`. That entry point no longer exists. Install `@dynamic-labs-sdk/tempo` and import the same helpers from it.
</Info>

## Installation

```bash theme={"system"}
npm install @dynamic-labs-sdk/tempo viem
```

The package lists `viem` as a peer dependency, so your app installs it alongside the SDK.

## Add the extension

`addTempoExtension` registers Tempo's network provider builder. Register it at module level next to your client, and enable Tempo in the [Dynamic dashboard](https://app.dynamic.xyz/dashboard/chains-and-networks). The extension covers chain IDs `4217`, `42429`, and `42431`. Calling it more than once has no effect after the first call.

```typescript theme={"system"}
import { createDynamicClient } from '@dynamic-labs-sdk/client';
import { addTempoExtension } from '@dynamic-labs-sdk/tempo';

const dynamicClient = createDynamicClient({
  ...
});

addTempoExtension(dynamicClient);
```

Every function that takes a Dynamic client accepts it as an optional last argument. You only need to pass it when your app runs [multiple Dynamic clients](/docs/javascript/reference/client/create-dynamic-client).

## Add embedded wallet support

Tempo embedded wallets come from the `/waas` entry point. `addWaasTempoExtension` registers the Tempo network provider and the embedded wallet provider, so it replaces `addTempoExtension` for apps that use embedded wallets.

```typescript theme={"system"}
import { addWaasTempoExtension } from '@dynamic-labs-sdk/tempo/waas';

addWaasTempoExtension(dynamicClient);
```

Embedded Tempo wallets can sign messages, transfer TIP-20 tokens, and sign and send Tempo transactions. They do not support typed data signing, and a wallet that belongs to a business account cannot send transactions.

## Recognize Tempo accounts

Tempo wallet accounts have `chain: 'TEMPO'`, so use `isTempoWalletAccount` rather than the EVM guard to pick them out of a user's accounts.

```typescript theme={"system"}
import { getWalletAccounts } from '@dynamic-labs-sdk/client';
import { isTempoWalletAccount } from '@dynamic-labs-sdk/tempo';

const getTempoWalletAccounts = () => getWalletAccounts().filter(isTempoWalletAccount);
```

`isTempoNetworkProvider` narrows a network provider to a `TempoNetworkProvider`, and `isTempoWalletProvider` does the same for wallet providers.

## Read chain state

`getTempoPublicClient` resolves the active Tempo network from the Tempo registry and returns a [viem PublicClient](https://viem.sh/docs/clients/public) pointed at it.

```typescript theme={"system"}
import { getTempoPublicClient } from '@dynamic-labs-sdk/tempo';
import type { TempoWalletAccount } from '@dynamic-labs-sdk/tempo';

const getTempoBlockNumber = async (walletAccount: TempoWalletAccount) => {
  const publicClient = await getTempoPublicClient({ walletAccount });

  return publicClient.getBlockNumber();
};
```

## Create a wallet client

An embedded Tempo wallet provider creates a [viem WalletClient](https://viem.sh/docs/clients/wallet) whose account signs messages and Tempo transactions. Look up the provider for the account, confirm it is a Tempo provider, and call `createViemWalletClient`. `getWalletProviderFromWalletAccount` comes from the core entry point and takes the Dynamic client as its second argument, so the example passes the `dynamicClient` you created earlier.

```typescript theme={"system"}
import { getWalletProviderFromWalletAccount } from '@dynamic-labs-sdk/client/core';
import { isTempoWalletProvider } from '@dynamic-labs-sdk/tempo';
import type { TempoWalletAccount } from '@dynamic-labs-sdk/tempo';

const createTempoWalletClient = async (walletAccount: TempoWalletAccount) => {
  const walletProvider = getWalletProviderFromWalletAccount({ walletAccount }, dynamicClient);

  if (!isTempoWalletProvider(walletProvider) || !walletProvider.createViemWalletClient) {
    throw new Error('This wallet cannot create a Tempo wallet client');
  }

  return walletProvider.createViemWalletClient({ walletAccount });
};
```

`createViemWalletClient` also accepts optional `httpTransportConfig` and `viemWalletClientConfig` values, which configure the viem transport and wallet client.

<Warning>
  The EVM helper `createWalletClientForWalletAccount` does not accept Tempo wallet accounts. Create the wallet client from the Tempo wallet provider as shown above.
</Warning>

## Send a TIP-20 transfer

`sendTip20Transaction` transfers a TIP-20 token with a viem WalletClient created from the Tempo wallet account. `tokenAddress` is required, and `decimals` defaults to `18`, so pass the token's own decimals for tokens such as 6 decimal stablecoins. `amount` is the human-readable amount, not the smallest unit.

<Tabs>
  <Tab title="JavaScript">
    ```typescript theme={"system"}
    import { sendTip20Transaction } from '@dynamic-labs-sdk/tempo';
    import type { TempoWalletAccount } from '@dynamic-labs-sdk/tempo';

    import { createTempoWalletClient } from './createTempoWalletClient';

    const sendStablecoin = async (walletAccount: TempoWalletAccount) => {
      const walletClient = await createTempoWalletClient(walletAccount);

      return sendTip20Transaction({
        amount: '1.5',
        decimals: 6,
        to: '0xRecipientAddress...',
        tokenAddress: '0xTip20TokenAddress...',
        walletClient,
      });
    };
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { sendTip20Transaction } from '@dynamic-labs-sdk/tempo';
    import { isTempoWalletAccount } from '@dynamic-labs-sdk/tempo';
    import { useGetWalletAccounts } from '@dynamic-labs-sdk/react-hooks';

    import { createTempoWalletClient } from './createTempoWalletClient';

    function SendStablecoinButton() {
      const { data: walletAccounts = [] } = useGetWalletAccounts();
      const walletAccount = walletAccounts.find(isTempoWalletAccount);

      const handleSend = async () => {
        if (!walletAccount) return;

        const walletClient = await createTempoWalletClient(walletAccount);

        const hash = await sendTip20Transaction({
          amount: '1.5',
          decimals: 6,
          to: '0xRecipientAddress...',
          tokenAddress: '0xTip20TokenAddress...',
          walletClient,
        });

        console.log('Transfer sent:', hash);
      };

      return (
        <button disabled={!walletAccount} onClick={handleSend}>
          Send 1.5 TIP-20
        </button>
      );
    }
    ```
  </Tab>
</Tabs>

Both examples import `createTempoWalletClient`, the function defined in [Create a wallet client](#create-a-wallet-client), from your own module.

<Warning>
  There is no native token on Tempo, so a value transfer without `tokenAddress` cannot succeed. `sendTip20Transaction` throws `TempoNativeTokenError` when `tokenAddress` is empty.
</Warning>

### Transfer with `transferAmount`

The client function `transferAmount` works with embedded Tempo wallets as well. It takes the token as an object with `address` and `decimals`, and it requires the token because Tempo has no native currency to fall back on.

```typescript theme={"system"}
import { transferAmount } from '@dynamic-labs-sdk/client';
import type { TempoWalletAccount } from '@dynamic-labs-sdk/tempo';

const transferStablecoin = async (walletAccount: TempoWalletAccount) => {
  const { transactionHash } = await transferAmount({
    amount: '1.5',
    recipient: '0xRecipientAddress...',
    token: { address: '0xTip20TokenAddress...', decimals: 6 },
    walletAccount,
  });

  return transactionHash;
};
```

## Send a Tempo transaction

`sendTempoTransaction` signs and sends a transaction that uses Tempo-only fields: batched calls, a fee token, a nonce key, or a validity window. Use it when a plain viem `sendTransaction` cannot express the transaction. It is available from the `/waas` entry point and works with embedded Tempo wallets.

```typescript theme={"system"}
import { sendTempoTransaction } from '@dynamic-labs-sdk/tempo/waas';
import type { TempoWalletAccount } from '@dynamic-labs-sdk/tempo';
import { encodeFunctionData, erc20Abi, parseUnits } from 'viem';

const payTwoRecipients = async (walletAccount: TempoWalletAccount) => {
  const tokenAddress = '0xTip20TokenAddress...';

  const transferTo = (recipient: `0x${string}`) =>
    encodeFunctionData({
      abi: erc20Abi,
      args: [recipient, parseUnits('1.5', 6)],
      functionName: 'transfer',
    });

  const { transactionHash } = await sendTempoTransaction({
    calls: [
      { data: transferTo('0xFirstRecipient...'), to: tokenAddress },
      { data: transferTo('0xSecondRecipient...'), to: tokenAddress },
    ],
    feeToken: tokenAddress,
    walletAccount,
  });

  return transactionHash;
};
```

The guides below cover each Tempo-only field in detail:

* [Send batched calls](/docs/javascript/reference/tempo/send-batched-calls) for `calls` and how gas is estimated for a batch.
* [Pay fees with a TIP-20 token](/docs/javascript/reference/tempo/pay-fees-with-a-token) for `feeToken`.
* [Use parallel nonce lanes](/docs/javascript/reference/tempo/use-parallel-nonce-lanes) for `nonceKey`.
* [Set a validity window](/docs/javascript/reference/tempo/set-a-validity-window) for `validAfter` and `validBefore`.
* [Sign for a fee sponsor](/docs/javascript/reference/tempo/sign-for-a-fee-sponsor) for `signTempoTransaction` and `awaitingFeePayer`.

### Parameters

| Parameter | Type | Description |
| - | - | - |
| `walletAccount` | `WalletAccount` | The embedded Tempo wallet that sends the transaction. |
| `calls` | `TempoTransactionCall[]` | The calls the transaction executes atomically. Each call takes `to`, `data`, and `value`. |
| `feeToken` | `Hex` (optional) | The TIP-20 token address that pays the fee. |
| `gas` | `bigint` (optional) | The gas limit. Defaults to a buffered estimate. If estimating a single call fails, the error is thrown unless you pass `gas`. |
| `nonceKey` | `bigint` (optional) | Selects a parallel nonce lane. The transaction uses the next nonce on that lane. |
| `validAfter` | `number` (optional) | A Unix timestamp in seconds. The transaction is not valid before this time. |
| `validBefore` | `number` (optional) | A Unix timestamp in seconds. The transaction expires after this time. It must be later than `validAfter`. |

### Returns

`Promise<{ transactionHash: string }>`. The hash is returned once the signed transaction is submitted, not once it is included in a block. To wait for inclusion, pass the hash to `waitForTransactionReceipt` on the client from `getTempoPublicClient`.

### Errors

| Error | When it is thrown |
| - | - |
| `InvalidParamError` | The wallet account is not an embedded Tempo wallet account. |
| `TempoTransactionValidationError` | A field is malformed or inconsistent, for example `calls` combined with a top-level `to`, `data`, or `value`. |
| `TempoNativeTokenError` | A token transfer is requested without a token address. |
| `InvalidTempoNetworkError` | The wallet provider is asked to switch to a network that is not a Tempo network. |
| `TempoTypedDataNotSupportedError` | The wallet is asked to sign typed data, which embedded Tempo wallets do not support yet. |

`TempoTransactionValidationError`, `InvalidTempoNetworkError`, `TempoTypedDataNotSupportedError`, and the `TempoTransactionRequest` and `TempoTransactionCall` types are exported from `@dynamic-labs-sdk/tempo/waas`. `TempoNativeTokenError` is exported from `@dynamic-labs-sdk/tempo`.

## Recognize Tempo networks

`isTempoChainId` tells you whether a chain ID belongs to Tempo, which is useful when your app routes a network ID to chain-specific behavior. `TEMPO_CHAIN_IDS` holds the same list if you need to enumerate it.

```typescript theme={"system"}
import { TEMPO_CHAIN_IDS, isTempoChainId } from '@dynamic-labs-sdk/tempo';

const supportsNativeValueTransfers = (networkId: number) => !isTempoChainId(networkId);

console.log('Tempo networks:', TEMPO_CHAIN_IDS);
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.