A draft link opens the Tenderly Simulator with a pre-filled form. Nothing runs until the recipient clicks Simulate, so the link is safe to share in test reports, pull requests, or alerts.
A draft prefills a single simulation, not a bundle. It populates one transaction form (one row); it does not reconstruct a multi-step bundle.
The link is the Simulator’s new-simulation route with a draft query parameter:
https://dashboard.tenderly.co/<org>/<project>/simulator/new?draft=<value>
<value> is the draft JSON payload, UTF-8 encoded, then base64url: standard base64 with + replaced by -, / replaced by _, and = padding stripped.
For a one-off share you do not need a script. Fill the form in the Simulator and click Copy draft link in the page header (shown while the bundle has a single simulation). The rest of this page is for generating links programmatically, from CI, test reports, or other tooling.
Quick start
A minimal raw-calldata draft is all most CI scripts need. The to address goes in contractAddress, and the calldata goes in rawFunctionInput:
{
"v": 1,
"network": { "id": "1" },
"row": {
"contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"from": "0xab5801a7d398351b8be11c439e05c5b3259aec9b",
"inputDataType": "raw",
"rawFunctionInput": "0xa9059cbb…"
}
}
Encode that payload and append it to the 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, reading the payload from payload.json (GNU coreutils)
DRAFT=$(jq -c . payload.json | basenc --base64url -w0 | tr -d '=')
# macOS fallback:
DRAFT=$(jq -c . payload.json | base64 | tr '+/' '-_' | tr -d '=\n')
Top-level payload
| Field | Type | Required | Description |
|---|
v | number | yes | Schema version. Must be 1. |
network | { "id": string } or null | yes | Chain id as a string ("1" is Mainnet, "56" is BNB Chain, and so on). Must be a network enabled on the recipient’s project, otherwise they get a “network not available” error and an empty form. Always set it. null is accepted but skips the contract lookup and leaves the form mostly unusable. See Supported Networks for chain ids. |
row | object | yes | The form fields. See row fields below. |
row fields
Only contractAddress is required. Omit anything you do not need. Omitted fields keep the form’s defaults, and unknown extra fields are ignored.
Contract and function
| Field | Type | Description |
|---|
contractAddress | string | Required. The “to” address. The contract and its ABI are fetched on the recipient’s side when the link opens. |
inputDataType | "decoded" or "raw" | Selects the function-input mode. Use "raw" with rawFunctionInput, or "decoded" with contractFunction and functionInputs. |
rawFunctionInput | string | Hex calldata (0x…) for raw mode. No ABI needed, which makes it the most robust option for scripts. |
contractFunction | object or null | Decoded-mode function reference: { "name": string, "selector"?: string, "signature"?: string }. name is required inside the object. selector (4-byte hex, e.g. "0xa9059cbb") is optional but preferred: it is matched first and is exact for overloaded functions. Without it, matching falls back to name and may pick the wrong overload. signature is informational only. |
functionInputs | array or object | Decoded-mode argument values. Preferred: a positional array in ABI order, for example ["0xabc…", "1000000", ["0xa…", "0xb…"]]. Values are strings, numbers, or booleans. Array and tuple arguments may be passed natively as nested arrays or objects, with no pre-stringifying needed. The keyed form { "input_0": … } is also accepted (it is what the UI’s Copy button emits). |
contractAbiImport | string | Pre-fills the in-app “Edit ABI” field. Function matching on open uses the fetched ABI only, so this field does not make decoded mode work for unverified contracts. Use raw calldata for those. |
Transaction parameters
| Field | Type | Description |
|---|
from | string | Sender address. |
gas | string or number | Gas limit, e.g. "8000000". |
gasPrice | string or number | Gas price in wei. |
value | string or number | Native-token value in wei. |
Block selection
| Field | Type | Description |
|---|
block | string or number | Block number to simulate at. Omit for the chain head. |
blockIndex | string, number, or null | Transaction position inside the block. null or omitted means the start of the block. |
endOfBlock | boolean | true runs the simulated transaction after every transaction in the block (overrides blockIndex). |
usePendingBlock | boolean | true simulates on the pending block instead of a fixed number. |
See Simulation Parameters for how block selection and transaction index behave in the UI.
L2 parameters
These fields apply to OP-stack and Boba networks only.
| Field | Type | Description |
|---|
depositTx | boolean | Mark as an L2 deposit transaction. |
systemTx | boolean | Mark as an L2 system transaction. |
mint | string | Deposit mint amount. |
l1BlockNumber | string | Originating L1 block number. |
l1Timestamp | string | Originating L1 timestamp. |
l1MessageSender | string | L1 message sender address. |
l1Turing | string | Boba Turing data. |
Overrides
| Field | Type | Description |
|---|
blockHeaderOverrides | object | { "number"?: …, "timestamp"?: … }, each a string, number, or null. Overrides the simulated block header. |
stateOverrides | array | Per-contract state overrides: { "id"?: string, "contractAddress": string, "balance": string, "storage"?: [{ "key": string, "value": string }], "code"?: string }. id is optional and generated when absent. balance is in wei ("" means no balance override). storage keys and values are 32-byte hex. |
accessList | array | EIP-2930 access list: { "address": string, "storageKeys": string[] }. |
Two override types are UI-only and cannot be set in a draft. Configure them in the app after opening the link: fund-address ERC-20 overrides and custom Solidity sources (the in-app “Edit source” feature).
Validation
Draft hydration is all-or-nothing. A missing required field, a wrong v, or any wrong-typed field rejects the whole payload: the recipient sees “This draft link is invalid or corrupted” and an empty form. There is no partial hydration, so a broken script fails visibly instead of half-working.
Limits and tips
- Keep the final URL under 2000 characters. The app refuses to copy longer links, and browsers and proxies may truncate them. Decoded calls with many inputs land around ~1900.
- Discovering payload shapes: fill the form in the Simulator UI, click Copy draft link, and base64url-decode the
draft value. The result is a known-good payload for your exact use case, and it is the best way to get the advanced shapes (stateOverrides, accessList) right.
- The contract ABI is fetched when the link opens. If it cannot be resolved (an unverified contract), decoded-mode drafts open with the address filled but no function selected. Raw mode is immune to this.
For the form fields a draft populates, see Simulation Parameters. To keep a modified state as a persistent environment instead of a one-shot simulation, use Virtual Environments.