# Tempo

Source: https://developers.bitgo.com/docs/tempo

## Overview

Tempo is an EVM-compatible blockchain purpose-built for global payments. It features native Account Abstraction (EIP-7702) at the protocol level, enabling advanced transaction capabilities such as batch transactions and custom fee tokens — all natively supported at the protocol layer. Tempo eliminates volatile gas tokens by using USD-denominated TIP-20 stablecoins for all transaction fees, with ultra-low costs under $0.001 per transfer. The network achieves sub-second finality (~1 second) and ~0.4 second block times using Simplex BFT consensus.

## Explorer
<a href="https://explore.mainnet.tempo.xyz" target="_blank" rel="noreferrer">https://explore.mainnet.tempo.xyz</a>


## Wallets Types

BitGo enables holding tempo in the following wallet types:

|                  | Multisig Cold | Multisig Hot | MPC Cold | MPC Hot |
| ---------------- | ------------- | ------------ | -------- | ------- |
| **Custody**      | ❌ | ❌ | ✅ | ❌ |
| **Self-Custody** | ❌ | ❌ | ✅ | ✅ |

## Ticker Symbols

| Mainnet | Testnet |
| ------- | ------- |
| TEMPO | TTEMPO |

## Faucet

You can use a faucet to obtain free testnet TIP-20 tokens for development and testing purposes.

**Faucet:** https://docs.tempo.xyz/quickstart/faucet

## Units

Tempo has no native coin. All balances and fees use TIP-20 USD stablecoins, which adhere to 6 decimal places:

- 1 TIP-20 token = <code>10<sup>6</sup></code> base units (micro-units)
- 1 base unit = <code>10<sup>-6</sup></code> TIP-20 token

For that reason, only string balance properties are available, which are `balanceString`, `confirmedBalanceString`, and
`spendableBalanceString`.

## Memos / memoId addressing

Tempo uses a **single-account model**: each wallet has one base address (a standard `0x`-prefixed EVM address). Append a `?memoId=<N>` query parameter to that base address to route incoming payments to a specific user or purpose — you never generate a new on-chain address:

```
0x2476602c78e9a5e0563320c78878faa3952b256f?memoId=123
```

- Set `<N>` to a **non-negative integer with no leading zeros**, such as `0`, `1`, or `123`. Tempo rejects `-1`, `1.5`, `01`, and `abc`.
- Reuse the single base address across all users; the `memoId` uniquely identifies each. This removes per-user address generation and eliminates manual consolidation.
- The `memoId` never changes ownership — every `?memoId=<N>` variant belongs to the same wallet.
- `?memoId=0` is equivalent to the bare base address. A deposit with no memo credits `memoId=0`, so treat `0x2476...?memoId=0` and `0x2476...` as the same destination.

> 🚧 **DX note:** `wallet.receiveAddress()` appends a default `?memoId=0` suffix, producing `0x2476...?memoId=0`. External EVM tools (wallets, explorers, exchanges) do not recognize this suffix, so **strip `?memoId=0`** before displaying the address or sharing it for deposits. Only append a suffix when you need a specific non-zero `memoId`.

### memoId works only through TIP-20 `transferWithMemo`

BitGo attributes deposits by reading the on-chain memo emitted by the TIP-20 `transferWithMemo` method. The `memoId` model therefore applies **only** to TIP-20 transfers. Any operation that must carry a `memoId` — including mint, burn, or other token actions — must use the corresponding `*WithMemo` variant; plain calls without a memo credit `memoId=0` by default.

### Reading the memoId from transfers

The List Transfers response does **not** return the Tempo memo ID in `entries[].memo`. Read it from the `?memoId=<N>` suffix on `entries[].address` instead.

## Fees

Tempo uses a fixed base fee model rather than EIP-1559 dynamic pricing, making transaction costs predictable and ultra-low — basic transfers cost less than $0.001 USD. Tempo pays all fees in USD-denominated TIP-20 stablecoins. If a user's preferred fee token differs from the validator's, an on-chain Fee AMM automatically converts it at settlement. The network calculates the total fee as:

```
fee = ceil(base_fee * gas_used / 10^12)
```

| Transaction Type | Gas Cost | Estimated Fee (USD) |
|:----------------|:---------|:--------------------|
| AA Transaction (P256) | 26,000 | ~$0.001 |
| Batch Transaction (3 calls) | ~50,000 | < $0.002 |

## Create Wallet

```shell
export BITGO_EXPRESS_HOST="<YOUR_LOCALHOST>"
export COIN="ttempo"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export LABEL="<DESIRED_WALLET_NAME>"
export PASSPHRASE="<YOUR_BITGO_LOGIN_PASSPHRASE>"
export ENTERPRISE_ID="<YOUR_ENTERPRISE_ID>"

curl -X POST \
 http://$BITGO_EXPRESS_HOST/api/v2/$COIN/wallet/generate \
 -H 'Content-Type: application/json' \
 -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "label": "'"$LABEL"'",
    "passphrase": "'"$PASSPHRASE"'",
    "enterprise": "'"$ENTERPRISE_ID"'",
    "walletVersion": 4
}'
```
```javascript
bitgo
  .coin('ttempo')
  .wallets()
  .generateWallet({
    label: 'My Test Wallet',
    passphrase: 'secretpassphrase1a5df8380e0e30',
    enterprise: '5612c2beeecf83610b621b90964448cd',
    walletVersion: 4,
  })
  .then(function (wallet) {
    // print the new wallet
    console.dir(wallet);
  });
```

## Create Address

Tempo follows a single-account model (see [Memos / memoId addressing](#memos--memoid-addressing)). The returned address may include a `?memoId=<N>` suffix; the base `0x` address is reusable across many `memoId` values rather than requiring a new address per deposit.

```shell
export WALLET="585c51a5df8380e0e3082e46"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"

curl -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
https://app.bitgo-test.com/api/v2/ttempo/wallet/$WALLET/address
```
```javascript
bitgo
  .coin('ttempo')
  .wallets()
  .getWallet({ id: '585c51a5df8380e0e3082e46' })
  .then(function (wallet) {
    return wallet.createAddress();
  })
  .then(function (newAddress) {
    // print new address details
    console.dir(newAddress);
  });
```

## Estimate Fee

```shell
export COIN="ttempo"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"

curl -X GET \
 https://app.bitgo-test.com/api/v2/$COIN/tx/fee \
 -H 'Content-Type: application/json' \
 -H "Authorization: Bearer $ACCESS_TOKEN"
```
```javascript
const BitGoJS = require('../../../src/index.js');
const bitgo = new BitGoJS.BitGo({ env: 'test' });
const accessToken = '<YOUR_ACCESS_TOKEN>';
const coin = 'ttempo';

async function getFeeEstimate() {
  try {
    await bitgo.authenticateWithAccessToken({ accessToken });
    const res = await bitgo.coin(coin).feeEstimate({ numBlocks: 2 });
    console.dir(res);
  } catch (err) {
    console.error('Error fetching fee estimate:', err);
  }
}

getFeeEstimate();
```

## Transact

Tempo has no native coin — **every transfer moves a TIP-20 token**, so a network-prefixed `tokenName` is **required** on each transfer: use `tempo:<token>` on mainnet or `ttempo:<token>` on testnet, such as `tempo:pathusd` or `ttempo:pathusd`. All amounts use **6-decimal base units** (1 TIP-20 token = `10^6` base units, so `1000000` = 1 token).

```shell
export BITGO_EXPRESS_HOST="<YOUR_LOCALHOST>"
export COIN="ttempo"
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export ADDRESS="<DESTINATION_ADDRESS>"
export TOKEN_NAME="ttempo:pathusd"
export AMOUNT="1000000"  # 1 token = 10^6 base units
export WALLET_PASSPHRASE="<YOUR_WALLET_PASSPHRASE>"

curl -X POST \
 http://$BITGO_EXPRESS_HOST/api/v2/$COIN/wallet/$WALLET_ID/sendcoins \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "address": "'"$ADDRESS"'",
    "amount": "'"$AMOUNT"'",
    "tokenName": "'"$TOKEN_NAME"'",
    "walletPassphrase": "'"$WALLET_PASSPHRASE"'"
}'
```
```javascript
const tx = await fundedWallet.send({
    address: '<DESTINATION_ADDRESS>',
    amount: '1000000', // 1 token = 10^6 base units
    tokenName: 'ttempo:pathusd',
    walletPassphrase: process.env.PASSWORD,
  });
```

### Send a TIP-20 transfer with a memoId

To direct a transfer to a specific `memoId` (see [Memos / memoId addressing](#memos--memoid-addressing)), append the `?memoId=<N>` suffix to the recipient `address`. The same `tokenName` and base-unit `amount` rules apply.

```shell
export BITGO_EXPRESS_HOST="<YOUR_LOCALHOST>"
export COIN="ttempo"
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export ADDRESS="0x2476602c78e9a5e0563320c78878faa3952b256f?memoId=123"
export TOKEN_NAME="ttempo:pathusd"
export AMOUNT="1000000"  # 1 token = 10^6 base units
export WALLET_PASSPHRASE="<YOUR_WALLET_PASSPHRASE>"

curl -X POST \
 http://$BITGO_EXPRESS_HOST/api/v2/$COIN/wallet/$WALLET_ID/sendcoins \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "address": "'"$ADDRESS"'",
    "amount": "'"$AMOUNT"'",
    "tokenName": "'"$TOKEN_NAME"'",
    "walletPassphrase": "'"$WALLET_PASSPHRASE"'"
}'
```
```javascript
const tx = await fundedWallet.send({
    address: '0x2476602c78e9a5e0563320c78878faa3952b256f?memoId=123',
    amount: '1000000', // 1 token = 10^6 base units
    tokenName: 'ttempo:pathusd',
    walletPassphrase: process.env.PASSWORD,
  });
```

The call returns the built transfer. The recipient `memoId` stays on `transfer.entries[].address`:

```json
{
  "txid": "0x9f2c1d4e...c7a8",
  "status": "signed",
  "transfer": {
    "id": "665f0a1b2c3d4e5f60718293",
    "coin": "ttempo:pathusd",
    "wallet": "585c51a5df8380e0e3082e46",
    "txid": "0x9f2c1d4e...c7a8",
    "type": "send",
    "state": "signed",
    "valueString": "-1000000",
    "feeString": "1000",
    "entries": [
      {
        "address": "0x2476602c78e9a5e0563320c78878faa3952b256f?memoId=123",
        "valueString": "1000000"
      }
    ]
  },
  "txRequest": {
    "txRequestId": "b1c2d3e4-5678-90ab-cdef-1234567890ab",
    "state": "delivered"
  }
}
```

### Send to Many

```shell
export BITGO_EXPRESS_HOST="<YOUR_LOCALHOST>"
export COIN="ttempo"
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export ADDRESS_1="<DESTINATION_ADDRESS_1>"
export AMOUNT_1="1000000"  # 1 token = 10^6 base units
export TOKEN_NAME="ttempo:pathusd"
export WALLET_PASSPHRASE="<YOUR_WALLET_PASSPHRASE>"

curl -X POST \
  http://$BITGO_EXPRESS_HOST/api/v2/$COIN/wallet/$WALLET_ID/sendmany \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "recipients": [
    {
      "address": "'"$ADDRESS_1"'",
      "amount": "'"$AMOUNT_1"'",
      "tokenName": "'"$TOKEN_NAME"'"
    }
  ],
    "walletPassphrase": "'"$WALLET_PASSPHRASE"'"
}'
```
```javascript
let params = {
  recipients: [
    {
      amount: "1000000", // 1 token = 10^6 base units
      address: "<DESTINATION_ADDRESS_1>", // optionally append ?memoId=<N>
      tokenName: "ttempo:pathusd", // required on every recipient
    }
  ],
  walletPassphrase: "<YOUR_WALLET_PASSPHRASE>",
};
wallet.sendMany(params).then(function (transaction) {
  // Print transaction details
  console.dir(transaction);
});
```

### Paying fees with a custom token

Tempo has no native gas coin — **transaction fees are paid in a TIP-20 stablecoin**. By default, BitGo pays fees in **pathUSD**. To pay fees in a different TIP-20 token, pass the token's **contract address** as the top-level `feeToken` parameter on your send request; it applies to the whole transaction (one fee token per transaction).

The fee token is **independent of the token being transferred** — you can send `ttempo:pathusd` while paying the fee in `ttempo:usd1`. If the fee token differs from the validator's preferred token, Tempo's on-chain Fee AMM converts it automatically at settlement.

- **Value:** the fee token's on-chain **contract address** (not the `tokenName`).
- **Default:** omit `feeToken` to pay fees in **pathUSD**.
- **Applies to** both `sendcoins` (single) and `sendmany` requests.

Common testnet (`ttempo`) fee-token contract addresses:

| Token | `tokenName` | Contract address |
|-------|-------------|------------------|
| pathUSD (default) | `ttempo:pathusd` | `0x20c0000000000000000000000000000000000000` |
| alphaUSD | `ttempo:alphausd` | `0x20c0000000000000000000000000000000000001` |

On mainnet (`tempo`), pathUSD uses the same `0x20c0…0000` address; look up other tokens in [Statics](https://github.com/BitGo/BitGoJS/blob/master/modules/statics/src/allCoinsAndTokens.ts).

```shell
export BITGO_EXPRESS_HOST="<YOUR_LOCALHOST>"
export COIN="ttempo"
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export ADDRESS="<DESTINATION_ADDRESS>"
export TOKEN_NAME="ttempo:pathusd"
export AMOUNT="1000000"  # 1 token = 10^6 base units
export FEE_TOKEN="<FEE_TOKEN_CONTRACT_ADDRESS>"
export WALLET_PASSPHRASE="<YOUR_WALLET_PASSPHRASE>"

curl -X POST \
 http://$BITGO_EXPRESS_HOST/api/v2/$COIN/wallet/$WALLET_ID/sendcoins \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "address": "'"$ADDRESS"'",
    "amount": "'"$AMOUNT"'",
    "tokenName": "'"$TOKEN_NAME"'",
    "feeToken": "'"$FEE_TOKEN"'",
    "walletPassphrase": "'"$WALLET_PASSPHRASE"'"
}'
```
```javascript
const tx = await fundedWallet.send({
    address: '<DESTINATION_ADDRESS>',
    amount: '1000000', // 1 token = 10^6 base units
    tokenName: 'ttempo:pathusd',
    feeToken: '<FEE_TOKEN_CONTRACT_ADDRESS>',
    walletPassphrase: process.env.PASSWORD,
  });
```

The chosen fee token is echoed back on the response under `coinSpecific`:

```json
{
  "coinSpecific": {
    "feeToken": "<FEE_TOKEN_CONTRACT_ADDRESS>",
    "feeTokenName": "<FEE_TOKEN_NAME>"
  }
}
```

## Stake

Tempo staking is out of scope for this integration.

## See Also

- [Reference: Address Types](/docs/utxo-address-types)
- [Create MPC Wallets](/docs/wallets-create-mpc-keys)
- [Trading Overview](/docs/trade-overview)
- [Withdraw from Wallet - Custody MPC](/docs/withdraw-wallet-type-custody-mpc)
- [Withdraw from Wallet - Self-Custody MPC (Manual)](/docs/withdraw-wallet-type-self-custody-mpc-manual)
- [Withdraw from Wallet - Self-Custody MPC (Simple)](/docs/withdraw-wallet-type-self-custody-mpc-simple)
