Draft-ссылка открывает Tenderly Simulator с предзаполненной формой. Ничего не запускается, пока получатель не нажмёт Simulate, поэтому ссылкой безопасно делиться в тест-отчётах, pull request’ах или оповещениях.
Draft предзаполняет одиночную симуляцию, а не бандл. Он заполняет одну форму транзакции (один row); он не восстанавливает многошаговый бандл.
Формат URL
Ссылка — это маршрут новой симуляции Simulator с query-параметром draft:
https://dashboard.tenderly.co/<org>/<project>/simulator/new?draft=<value>
<value> — это JSON-полезная нагрузка draft’а, закодированная в UTF-8, а затем в base64url: стандартный base64 с +, заменённым на -, /, заменённым на _, и удалёнными символами заполнения =.
Для разового обмена вам не нужен скрипт. Заполните форму в Simulator и нажмите Copy draft link в заголовке страницы (отображается, когда в бандле одна симуляция). Остальная часть этой страницы предназначена для программной генерации ссылок из CI, тест-отчётов или других инструментов.
Быстрый старт
Минимального draft’а с сырым calldata достаточно для большинства CI-скриптов. Адрес to идёт в contractAddress, а calldata — в rawFunctionInput:
{
"v": 1,
"network": { "id": "1" },
"row": {
"contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"from": "0xab5801a7d398351b8be11c439e05c5b3259aec9b",
"inputDataType": "raw",
"rawFunctionInput": "0xa9059cbb…"
}
}
Закодируйте эту полезную нагрузку и добавьте её к URL:
import base64, json
payload = {
"v": 1,
"network": {"id": "1"},
"row": {
"contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"from": "0xab5801a7d398351b8be11c439e05c5b3259aec9b",
"inputDataType": "raw",
"rawFunctionInput": "0xa9059cbb…",
},
}
draft = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")
url = f"https://dashboard.tenderly.co/<org>/<project>/simulator/new?draft={draft}"
// Node.js
const payload = {
v: 1,
network: { id: "1" },
row: {
contractAddress: "0xdac17f958d2ee523a2206206994597c13d831ec7",
from: "0xab5801a7d398351b8be11c439e05c5b3259aec9b",
inputDataType: "raw",
rawFunctionInput: "0xa9059cbb…",
},
};
const draft = Buffer.from(JSON.stringify(payload)).toString("base64url");
const url = `https://dashboard.tenderly.co/<org>/<project>/simulator/new?draft=${draft}`;
# Shell, читая полезную нагрузку из payload.json (GNU coreutils)
DRAFT=$(jq -c . payload.json | basenc --base64url -w0 | tr -d '=')
# Резервный вариант для macOS:
DRAFT=$(jq -c . payload.json | base64 | tr '+/' '-_' | tr -d '=\n')
Полезная нагрузка верхнего уровня
| Поле | Тип | Обязательно | Описание |
|---|
v | number | да | Версия схемы. Должна быть 1. |
network | { "id": string } или null | да | Chain id как строка ("1" — Mainnet, "56" — BNB Chain и так далее). Должна быть сетью, включённой в проекте получателя, иначе он получит ошибку «network not available» и пустую форму. Всегда указывайте её. null принимается, но пропускает поиск контракта и оставляет форму в основном непригодной для использования. Chain id см. в Supported Networks. |
row | object | да | Поля формы. См. row fields ниже. |
Поля row
Только contractAddress обязательно. Опускайте всё, что вам не нужно. Опущенные поля сохраняют значения по умолчанию формы, а неизвестные дополнительные поля игнорируются.
Контракт и функция
| Поле | Тип | Описание |
|---|
contractAddress | string | Обязательно. Адрес «to». Контракт и его ABI загружаются на стороне получателя при открытии ссылки. |
inputDataType | "decoded" или "raw" | Выбирает режим ввода функции. Используйте "raw" с rawFunctionInput или "decoded" с contractFunction и functionInputs. |
rawFunctionInput | string | Hex calldata (0x…) для raw-режима. ABI не требуется, что делает это самым надёжным вариантом для скриптов. |
contractFunction | object или null | Ссылка на функцию в decoded-режиме: { "name": string, "selector"?: string, "signature"?: string }. name обязательно внутри объекта. selector (4-байтовый hex, например "0xa9059cbb") необязателен, но предпочтителен: он сопоставляется первым и точен для перегруженных функций. Без него сопоставление возвращается к name и может выбрать неверную перегрузку. signature служит только для информации. |
functionInputs | array или object | Значения аргументов в decoded-режиме. Предпочтительно: позиционный массив в порядке ABI, например ["0xabc…", "1000000", ["0xa…", "0xb…"]]. Значения — строки, числа или булевы. Аргументы-массивы и кортежи можно передавать нативно как вложенные массивы или объекты без предварительного преобразования в строки. Также принимается ключевая форма { "input_0": … } (это то, что выдаёт кнопка Copy в UI). |
contractAbiImport | string | Предзаполняет поле «Edit ABI» в приложении. Сопоставление функций при открытии использует только загруженный ABI, поэтому это поле не заставит decoded-режим работать для неверифицированных контрактов. Используйте для них сырое calldata. |
Параметры транзакции
| Поле | Тип | Описание |
|---|
from | string | Адрес отправителя. |
gas | string или number | Лимит газа, например "8000000". |
gasPrice | string или number | Цена газа в wei. |
value | string или number | Значение нативного токена в wei. |
Выбор блока
| Поле | Тип | Описание |
|---|
block | string или number | Номер блока для симуляции. Опустите для головы цепочки. |
blockIndex | string, number или null | Позиция транзакции внутри блока. null или отсутствие означает начало блока. |
endOfBlock | boolean | true запускает симулированную транзакцию после каждой транзакции в блоке (переопределяет blockIndex). |
usePendingBlock | boolean | true симулирует на pending-блоке вместо фиксированного номера. |
О том, как выбор блока и индекс транзакции работают в UI, см. в Simulation Parameters.
Параметры L2
Эти поля применяются только к сетям OP-stack и Boba.
| Поле | Тип | Описание |
|---|
depositTx | boolean | Отметить как L2 deposit-транзакцию. |
systemTx | boolean | Отметить как L2 system-транзакцию. |
mint | string | Сумма deposit mint. |
l1BlockNumber | string | Номер исходного блока L1. |
l1Timestamp | string | Метка времени исходного L1. |
l1MessageSender | string | Адрес отправителя сообщения L1. |
l1Turing | string | Данные Boba Turing. |
Переопределения
| Поле | Тип | Описание |
|---|
blockHeaderOverrides | object | { "number"?: …, "timestamp"?: … }, каждое — строка, число или null. Переопределяет заголовок симулируемого блока. |
stateOverrides | array | Переопределения состояния по контрактам: { "id"?: string, "contractAddress": string, "balance": string, "storage"?: [{ "key": string, "value": string }], "code"?: string }. id необязателен и генерируется при отсутствии. balance в wei ("" означает отсутствие переопределения баланса). Ключи и значения storage — 32-байтовый hex. |
accessList | array | Access list EIP-2930: { "address": string, "storageKeys": string[] }. |
Два типа переопределений доступны только в UI и не могут быть заданы в draft’е. Настройте их в приложении после открытия ссылки: переопределения ERC-20 fund-address и пользовательские исходники Solidity (функция «Edit source» в приложении).
Валидация
Гидратация draft’а — всё или ничего. Отсутствующее обязательное поле, неверный v или любое поле с неправильным типом отклоняет всю полезную нагрузку: получатель видит «This draft link is invalid or corrupted» и пустую форму. Частичной гидратации нет, так что сломанный скрипт даёт видимый сбой вместо того, чтобы работать наполовину.
Ограничения и советы
- Держите итоговый URL короче 2000 символов. Приложение отказывается копировать более длинные ссылки, а браузеры и прокси могут их обрезать. Decoded-вызовы с большим количеством входов достигают ~1900.
- Обнаружение формы полезной нагрузки: заполните форму в UI Simulator, нажмите Copy draft link и декодируйте значение
draft из base64url. Результат — заведомо корректная полезная нагрузка для вашего конкретного случая, и это лучший способ правильно получить продвинутые формы (stateOverrides, accessList).
- ABI контракта загружается при открытии ссылки. Если он не может быть разрешён (неверифицированный контракт), decoded-режим draft’а откроется с заполненным адресом, но без выбранной функции. Raw-режим этому не подвержен.
О полях формы, которые заполняет draft, см. в Simulation Parameters. Чтобы сохранить изменённое состояние как постоянное окружение вместо одноразовой симуляции, используйте Virtual Environments.