Tenderly Web3 Actions Setup
On-chain events require reaction — sometimes immediately. If an asset price fell below liquidation threshold, if a large deposit arrived at the contract, if an oracle returned a stale value — all of this needs to be handled in seconds, not hours. The classic approach: a cron script on a server that polls RPC every minute. This is reliable until the first server failure or RPC timeout.
Tenderly Web3 Actions are event-driven functions that execute directly from on-chain events. Tenderly infrastructure monitors the nodes, you write the logic.
How It Works
Web3 Action is a TypeScript function that executes in a managed Tenderly environment when a trigger fires. Triggers come in three types:
Transaction triggers — reaction to a transaction to a specific contract or from a specific address. You can filter by status (success/failed), function (by selector), or event parameters.
Block triggers — execution on every block (or every N blocks). Used for monitoring state without waiting for specific events: checking health factor in a lending protocol, updating off-chain indices.
Webhook triggers — calling an Action via HTTP from an external system. Useful for testing and integration with off-chain systems.
Alert triggers — reaction to alerts from Tenderly Alerting (gas threshold, large transaction, contract state change).
Setup and Deployment
Workflow via Tenderly CLI:
npm install -g @tenderly/cli
tenderly login
tenderly init
Project structure:
tenderly.yaml
actions/
src/
index.ts # entry points for Actions
package.json
Example Action that reacts to LargeDeposit event and sends a Slack notification:
// actions/src/index.ts
import { ActionFn, Context, Event, TransactionEvent } from "@tenderly/actions";
import { ethers } from "ethers";
const THRESHOLD = ethers.parseEther("100"); // 100 ETH
export const onLargeDeposit: ActionFn = async (context: Context, event: Event) => {
const txEvent = event as TransactionEvent;
// Parse transaction logs
const iface = new ethers.Interface([
"event Deposit(address indexed user, uint256 amount)"
]);
for (const log of txEvent.logs) {
try {
const parsed = iface.parseLog(log);
if (parsed?.name === "Deposit" && parsed.args.amount > THRESHOLD) {
const webhookUrl = await context.secrets.get("SLACK_WEBHOOK_URL");
await fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `Large deposit: ${ethers.formatEther(parsed.args.amount)} ETH from ${parsed.args.user}\nTx: ${txEvent.hash}`
})
});
}
} catch {
// Log doesn't belong to our contract — skip
}
}
};
Configuration in tenderly.yaml:
account_id: "my-account"
project_slug: "my-project"
actions:
my-project/on-large-deposit:
runtime: v2
sources: actions/src
entrypoint: index:onLargeDeposit
trigger:
type: transaction
transaction:
status:
- mined
filters:
- network: 1
eventEmitted:
contract:
address: "0xYourContractAddress"
name: Deposit
Deployment:
tenderly actions deploy
Secrets and Environment Variables
API keys, webhook URLs, private keys — all via Tenderly Secrets, never hardcode in code:
const apiKey = await context.secrets.get("MY_API_KEY");
const rpcUrl = await context.secrets.get("ALCHEMY_RPC_URL");
Secrets are managed via Dashboard or CLI:
tenderly actions secret set MY_API_KEY "value"
Storage Between Invocations
Actions are stateless by default, but Tenderly provides simple key-value storage for persistence between invocations:
// Save the last processed block
await context.storage.putNumber("lastProcessedBlock", blockNumber);
// Read on next invocation
const lastBlock = await context.storage.getNumber("lastProcessedBlock");
Storage is used for: notification deduplication (don't send twice for the same event), accumulating statistics, maintaining state between block trigger invocations.
Practical Scenarios
Automatic Chainlink subscription top-up. Action monitors LINK subscription balance. If balance falls below minimum — calls topUp() function via private key from Secrets.
Health factor monitoring in lending. Block trigger every 10 blocks reads health factor for all open positions. If a position is close to liquidation — notification to user via push or email.
Off-chain database synchronization. Transaction trigger on Transfer/Mint/Burn events — update PostgreSQL via REST API or direct connection.
Automatic rebalancing. If treasury imbalance exceeds threshold — Action collects and sends transaction via Tenderly Simulation first, then live.
Limitations
Maximum Action execution time is 25 seconds. For long tasks (data aggregation, complex calculations) you need a webhook to an external service or a job queue. Runtime is Node.js v20, most npm packages are supported, but heavy native bindings are not supported.
Setting up Tenderly Web3 Actions takes 1-3 business days depending on the number of scenarios and integrations. Cost is calculated individually.







