13 releases
| new 0.3.8 | Feb 14, 2026 |
|---|---|
| 0.3.7 | Feb 13, 2026 |
| 0.3.5 | Jan 27, 2026 |
| 0.2.2 | Jan 12, 2026 |
| 0.1.1 | Jan 12, 2026 |
#938 in Web programming
28 downloads per month
Used in ethcli
390KB
7.5K
SLoC
tndrly
Unofficial Rust client for the Tenderly API
Features
- Simulation API - Simulate transactions without broadcasting
- Virtual TestNets API - Create isolated blockchain environments
- Alerts API - Monitor on-chain activity with notifications
- Contract API - Manage and verify smart contracts
- Web3 Actions API - Deploy serverless functions
- Wallets API - Track and monitor wallet addresses
Installation
[dependencies]
tndrly = "0.3"
tokio = { version = "1", features = ["full"] }
Quick Start
use tndrly::Client;
use tndrly::simulation::SimulationRequest;
#[tokio::main]
async fn main() -> Result<(), tndrly::Error> {
// Create client from environment variables
let client = Client::from_env()?;
// Simulate a transaction
let request = SimulationRequest::new(
"0x0000000000000000000000000000000000000000",
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"0x70a08231000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"
)
.gas(100000)
.save(true);
let result = client.simulation().simulate(&request).await?;
println!("Gas used: {}", result.simulation.gas_used);
println!("Status: {}", if result.simulation.status { "success" } else { "failed" });
Ok(())
}
Environment Variables
export TENDERLY_ACCESS_KEY="your-access-key"
export TENDERLY_ACCOUNT="your-account-slug"
export TENDERLY_PROJECT="your-project-slug"
API Modules
Simulation
use tndrly::simulation::{SimulationRequest, BundleSimulationRequest};
// Single simulation
let request = SimulationRequest::new(from, to, calldata)
.network_id("1")
.value_wei(1_000_000_000_000_000_000u128)
.gas(100000)
.save(true);
let result = client.simulation().simulate(&request).await?;
// Bundle simulation
let bundle = BundleSimulationRequest::new(vec![tx1, tx2, tx3]);
let results = client.simulation().simulate_bundle(&bundle).await?;
// List saved simulations
let sims = client.simulation().list(0, 10).await?;
// Share a simulation
let url = client.simulation().share("sim-id").await?;
Virtual TestNets
use tndrly::vnets::{CreateVNetRequest, ListVNetsQuery};
// Create a VNet
let request = CreateVNetRequest::new("my-testnet", "My TestNet", 1)
.block_number(18000000)
.sync_state(true);
let vnet = client.vnets().create(&request).await?;
// List VNets
let vnets = client.vnets().list(None).await?;
// Delete VNets (CI cleanup)
client.vnets().delete_many(vec!["id1".into(), "id2".into()]).await?;
// Admin RPC - Balance management
let admin = client.vnets().admin_rpc(&vnet.id).await?;
admin.set_balance("0xAddress", "0xDE0B6B3A7640000").await?; // 1 ETH
admin.set_erc20_balance("0xToken", "0xWallet", "0xAmount").await?;
// Admin RPC - State management
let snapshot_id = admin.snapshot().await?;
admin.revert(&snapshot_id).await?;
// Admin RPC - Simulate transaction (tenderly_simulateTransaction)
use tndrly::vnets::SimulateTransactionParams;
let tx = SimulateTransactionParams::new("0xFrom".into())
.to("0xTo".into())
.data("0xCalldata".into());
let result = admin.simulate_transaction(&tx, "latest", None, None).await?;
// Admin RPC - Simulate bundle (tenderly_simulateBundle)
let txs = vec![tx1, tx2, tx3];
let result = admin.simulate_bundle(&txs, None, None).await?;
Alerts
Note: The Tenderly Alerts API uses an undocumented request format. Alert creation (
create()) may not work as expected. Read operations (list(),get(),history()) work correctly. See src/alerts/types.rs for details.
use tndrly::alerts::AlertHistoryQuery;
// List existing alerts
let alerts = client.alerts().list().await?;
// Get alert history
let history = client.alerts()
.history(Some(AlertHistoryQuery::new().page(1).per_page(50)))
.await?;
// Get a specific alert
let alert = client.alerts().get("alert-id").await?;
Contracts
use tndrly::contracts::{AddContractRequest, VerifyContractRequest};
// Add a contract
let contract = client.contracts()
.add(&AddContractRequest::new("1", "0xAddress")
.display_name("My Contract")
.tag("defi"))
.await?;
// Verify source code
let result = client.contracts()
.verify(&VerifyContractRequest::new(
"1",
"0xAddress",
"MyContract",
source_code,
"v0.8.19+commit.7dd6d404",
).optimization(true, 200))
.await?;
Web3 Actions
use tndrly::actions::{CreateActionRequest, ActionTrigger, TriggerConfig};
// Create an action
let action = client.actions()
.create(&CreateActionRequest::new(
"Notify Slack",
ActionTrigger::Alert,
source_code,
)
.trigger_config(TriggerConfig::alert("alert-id"))
.secret("SLACK_WEBHOOK", webhook_url))
.await?;
// View execution logs
let logs = client.actions().logs(&action.id).await?;
Wallets
use tndrly::wallets::{AddWalletRequest, UpdateWalletRequest};
// Add a wallet to monitor
let wallet = client.wallets()
.add(&AddWalletRequest::new("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
.display_name("vitalik.eth")
.tag("whale"))
.await?;
// List all wallets
let wallets = client.wallets().list().await?;
// Update wallet metadata
client.wallets()
.update("0xd8dA...", &UpdateWalletRequest::new()
.display_name("Vitalik Buterin"))
.await?;
// Remove a wallet
client.wallets().remove("0xd8dA...").await?;
Terms of Service
This is an unofficial client. By using this library, you agree to comply with Tenderly's Terms of Service.
Disclaimer
This crate is not affiliated with or endorsed by Tenderly.
License
MIT
Dependencies
~6–21MB
~225K SLoC