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

# Симуляция пакета Mint, Approve и TransferFrom для токена

> Симулируйте пакет из 3 шагов, который минтит DAI, одобряет spender и передаёт токены, с общим состоянием между шагами для тестирования многотранзакционных потоков.

Это руководство создаёт пакет симуляции из 3 шагов, который отрабатывает классический процесс одобрения ERC-20: минтинг токенов держателю, одобрение spender'у, а затем вызов `transferFrom` этим spender'ом. Каждый шаг зависит от состояния, создаваемого предыдущим шагом, поэтому именно пакет — правильный инструмент.

## Зачем нужен пакет?

Симуляция каждой транзакции по отдельности не работает:

* **Только Mint**: вы можете проверить логику mint, но у адреса-держателя нет токенов в mainnet.
* **Только Approve**: симуляции нужно, чтобы у держателя действительно был баланс для одобрения.
* **Только TransferFrom**: требуется, чтобы был и баланс, и одобрение.

Sim Bundle выполняет все три последовательно, разделяя итоговое состояние. Шаг 2 видит сминтенный баланс из шага 1. Шаг 3 видит одобрение из шага 2.

## Три транзакции

Все три транзакции направлены на контракт DAI в Ethereum mainnet (`0x6b175474e89094c44da98b954eedeac495271d0f`).

### Шаг 1. Mint 2 DAI

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
From:  0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2  (fake ward)
To:    0x6b175474e89094c44da98b954eedeac495271d0f  (DAI)
Input: 0x40c10f19
       000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1  (recipient)
       0000000000000000000000000000000000000000000000001bc16d674ec80000  (2 DAI)
```

Функция `mint` в DAI защищена маппингом `wards`: вызвать её могут только адреса, зарегистрированные как wards. Fake ward (`0xe2e2...e2`) не является ward в mainnet. Чтобы этот шаг прошёл успешно, добавьте **переопределение состояния**, которое устанавливает слот хранилища ward для этого адреса в `1`:

| Поле        | Значение                                                             |
| ----------- | -------------------------------------------------------------------- |
| Contract    | `0x6b175474e89094c44da98b954eedeac495271d0f`                         |
| Storage key | `0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03` |
| Value       | `0x0000000000000000000000000000000000000000000000000000000000000001` |

Ключ хранилища — это `keccak256(abi.encode(wardAddress, 0))`, стандартный слот маппинга Solidity для `wards[fakeWardAddress]` при позиции хранилища `0`.

### Шаг 2. Approve 1 DAI

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
From:  0xe58b9ee93700a616b50509c8292977fa7a0f8ce1  (token holder from step 1)
To:    0x6b175474e89094c44da98b954eedeac495271d0f  (DAI)
Input: 0x095ea7b3
       000000000000000000000000f7ddedc66b1d482e5c38e4730b3357d32411e5dd  (spender)
       0000000000000000000000000000000000000000000000000de0b6b3a7640000  (1 DAI)
```

Держатель одобряет spender'у списание до 1 DAI. Этот шаг работает, потому что шаг 1 уже зачислил 2 DAI на баланс держателя в общем потоке состояния.

### Шаг 3. TransferFrom 0.03 DAI

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
From:  0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd  (approved spender)
To:    0x6b175474e89094c44da98b954eedeac495271d0f  (DAI)
Input: 0x23b872dd
       000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1  (sender / token holder)
       000000000000000000000000bd8daa414fda8a8a129f7035e7496759c5af8570  (recipient)
       000000000000000000000000000000000000000000000000006a94d74f430000  (0.03 DAI)
```

Spender перемещает 0.03 DAI от держателя получателю. Это работает, потому что шаг 2 предоставил разрешение (allowance), которое видно только внутри потока состояния пакета, а не в сети.

## Запуск через UI Simulator

<Steps>
  <Step title="Откройте Sim Bundle Builder">
    Перейдите в **Simulator** в левой навигации, затем нажмите **New Simulation**. Откроется конструктор пакета с одним пустым шагом.
  </Step>

  <Step title="Настройте панель сессии">
    Панель сессии расположена сверху. Установите **Network** в **Mainnet** и оставьте **Block** как **Pending**.
  </Step>

  <Step title="Заполните шаг 1: Mint">
    Переключите шаг в режим **Raw**. Установите:

    * **From**: `0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2`
    * **To**: `0x6b175474e89094c44da98b954eedeac495271d0f`
    * **Calldata**: `0x40c10f19000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce10000000000000000000000000000000000000000000000001bc16d674ec80000`

    Затем раскройте **State overrides**, добавьте контракт DAI и введите слот хранилища ward и значение из таблицы выше. Нажмите **Add storage override**, чтобы зафиксировать слот.

    <Frame caption="Step 1 with the mint calldata filled and the ward state override configured.">
      <img src="https://mintcdn.com/tenderly/XsEZlaGXYskrtN68/images/simulator-ui/sim-bundle-step1-mint-result.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=7bc42b094c26b08690b3d36992cc2f58" alt="Step 1 mint transaction configured with state override" width="1600" height="1000" data-path="images/simulator-ui/sim-bundle-step1-mint-result.webp" />
    </Frame>
  </Step>

  <Step title="Добавьте шаг 2: Approve">
    Нажмите **+ Add function call** на панели пакета. Переключите новый шаг в режим **Raw**. Установите:

    * **From**: `0xe58b9ee93700a616b50509c8292977fa7a0f8ce1`
    * **To**: `0x6b175474e89094c44da98b954eedeac495271d0f`
    * **Calldata**: `0x095ea7b3000000000000000000000000f7ddedc66b1d482e5c38e4730b3357d32411e5dd0000000000000000000000000000000000000000000000000de0b6b3a7640000`
  </Step>

  <Step title="Добавьте шаг 3: TransferFrom">
    Добавьте ещё один шаг. Переключитесь в режим **Raw**. Установите:

    * **From**: `0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd`
    * **To**: `0x6b175474e89094c44da98b954eedeac495271d0f`
    * **Calldata**: `0x23b872dd000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1000000000000000000000000bd8daa414fda8a8a129f7035e7496759c5af8570000000000000000000000000000000000000000000000000006a94d74f430000`
  </Step>

  <Step title="Запустите пакет">
    Нажмите **Simulate** (или клавиши `⌘↵`). Все три шага выполняются последовательно. Нажмите каждый шаг на панели, чтобы просмотреть его декодированный вывод, использованный газ, события и трансферы токенов.

    <Frame caption="The bundle after running: step 2 (approve) is focused, showing the Approval event and decoded output.">
      <img src="https://mintcdn.com/tenderly/XsEZlaGXYskrtN68/images/simulator-ui/sim-bundle-step2-approve-result.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=f5e7d7ea168a4e57c85d8f900b632e53" alt="Step 2 approve result showing the Approval event" width="1600" height="1000" data-path="images/simulator-ui/sim-bundle-step2-approve-result.webp" />
    </Frame>

    <Frame caption="Step 3 (transferFrom) succeeded. The 0.03 DAI transfer is visible in the ERC-20 transfers tab.">
      <img src="https://mintcdn.com/tenderly/XsEZlaGXYskrtN68/images/simulator-ui/sim-bundle-step3-transfer-result.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=3880b61afb94b616875f6c3be3d71467" alt="Step 3 transferFrom result showing the DAI transfer" width="1600" height="1000" data-path="images/simulator-ui/sim-bundle-step3-transfer-result.webp" />
    </Frame>
  </Step>
</Steps>

## Запуск через Simulation API

Используйте эндпоинт `simulate-bundle`, чтобы запустить ту же последовательность программно. Замените `{accountSlug}` и `{projectSlug}` на ваши значения.

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
curl --request POST \
  --url https://api.tenderly.co/api/v1/account/{accountSlug}/project/{projectSlug}/simulate-bundle \
  --header 'Content-Type: application/json' \
  --header 'X-Access-Key: <api-key>' \
  --data '{
  "simulations": [
    {
      "network_id": "1",
      "save": true,
      "save_if_fails": true,
      "simulation_type": "full",
      "from": "0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2",
      "to": "0x6b175474e89094c44da98b954eedeac495271d0f",
      "input": "0x40c10f19000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce10000000000000000000000000000000000000000000000001bc16d674ec80000",
      "state_objects": {
        "0x6b175474e89094c44da98b954eedeac495271d0f": {
          "storage": {
            "0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        }
      }
    },
    {
      "network_id": "1",
      "save": true,
      "save_if_fails": true,
      "simulation_type": "full",
      "from": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
      "to": "0x6b175474e89094c44da98b954eedeac495271d0f",
      "input": "0x095ea7b3000000000000000000000000f7ddedc66b1d482e5c38e4730b3357d32411e5dd0000000000000000000000000000000000000000000000000de0b6b3a7640000"
    },
    {
      "network_id": "1",
      "save": true,
      "save_if_fails": true,
      "simulation_type": "full",
      "from": "0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd",
      "to": "0x6b175474e89094c44da98b954eedeac495271d0f",
      "input": "0x23b872dd000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1000000000000000000000000bd8daa414fda8a8a129f7035e7496759c5af8570000000000000000000000000000000000000000000000000006a94d74f430000"
    }
  ]
}'
```

Ответ — массив из трёх результатов симуляции, по одному на шаг. Каждая запись содержит `status`, `gasUsed`, декодированные `logs`, `trace`, `assetChanges` и `balanceChanges`.

Успешное выполнение показывает `"status": true` для всех трёх шагов. Если шаг откатывается (например, потому что в шаге 1 отсутствует переопределение состояния), API возвращает все результаты вплоть до сбойного шага включительно, а последующие шаги не выполняются.

## На что обращать внимание в результатах

| Шаг          | Ключевые сигналы                                                                                                                                                                                                           |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Mint         | Запись `assetChanges` с `"type": "Mint"` и зачислением 2 DAI держателю. Событие `Transfer` с нулевого адреса.                                                                                                              |
| Approve      | Событие `Approval` в `logs`. Изменений активов нет (approve не перемещает токены).                                                                                                                                         |
| TransferFrom | Запись `assetChanges` с `"type": "Transfer"` и перемещением 0.03 DAI от держателя к получателю. Событие `Transfer` в `logs`. `balanceChanges` показывает отрицательное изменение у держателя и положительное у получателя. |

## Следующие шаги

* [Пакетные симуляции (справочник API)](/simulations/bundled-simulations): полная документация по эндпоинту и схеме ответа.
* [Переопределения состояния](/simulations/state-overrides): как переопределять хранилище, баланс и байткод в симуляциях.
* [UI Simulator](/simulator-ui/overview): справочник по компоновке и горячие клавиши для конструктора пакетов.
