Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion starter-templates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ They are more comprehensive than **building-blocks**, and can be adapted into yo
2. **Bring Your Own Data (BYOD)** — NAV & PoR — [`./bring-your-own-data`](./bring-your-own-data)
End-to-end examples for publishing **Net Asset Value** and **Proof of Reserve** data on-chain using CRE workflows and demo contracts.

3. **Multi-Chain Token Manager** — [`./multi-chain-token-manager`](./multi-chain-token-manager)
3. **Multi-Chain Token Manager** — [`./multi-chain-token-manager`](./multi-chain-token-manager)
Orchestrate token operations and state across multiple chains, showing RPC configuration, bindings, and cross-chain patterns.

4. **Vault Harvester** — [`./vault-harvester`](./vault-harvester)
Automated DeFi vault harvesting — check if yield is profitable, then harvest and compound. Same cron → read → check → write pattern as the Keeper Bot, with a DeFi-specific profitability check.

> Each subdirectory includes its own README with template-specific steps and example logs.

## License
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
kind: starter-template
id: vault-harvester-ts
projectDir: .
title: "Vault Harvester (TypeScript)"
description: "Automated DeFi vault harvesting — check if yield is profitable, then harvest and compound."
language: typescript
category: workflow
tags:
- vault
- harvester
- yield
- compounder
- defi
- cron
- on-chain-read
- on-chain-write
workflows:
- dir: my-workflow
postInit: |
A demo VaultHarvester contract is pre-deployed on Sepolia. See README.md for details.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.env
153 changes: 153 additions & 0 deletions starter-templates/vault-harvester/vault-harvester-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Vault Harvester — CRE Starter Template (TypeScript)
Copy link
Collaborator

@miro-cl miro-cl Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion, will do for all templates.


Automated DeFi vault harvesting — check if yield is profitable, then harvest and compound.

**⚠️ DISCLAIMER**

This template is an educational example to demonstrate how to interact with Chainlink systems, products, and services. It is provided **"AS IS"** and **"AS AVAILABLE"** without warranties of any kind, has **not** been audited, and may omit checks or error handling for clarity. **Do not use this code in production** without performing your own audits and applying best practices. Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for unintended outputs generated due to errors in code.

---

## Overview

This template demonstrates the **cron -> read -> check profitability -> harvest** pattern using Chainlink CRE (Compute Runtime Environment). Same cron -> read -> check -> write pattern as the Keeper Bot, but with a DeFi-specific vault contract.

### Use Cases

- **Vault harvesting** (primary): Check if `timeSinceLastHarvest > threshold` AND `yield > minThreshold`, then harvest
- **Yield compounding**: Harvest + swap + redeposit accumulated rewards
- **Protocol revenue collection**: Claim and redistribute protocol fees on schedule
- **Target protocols**: Beefy, Yearn, Alchemix, and any vault with a `harvest()` function

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│ CRE DON │
│ │
│ ┌──────────┐ ┌───────────────┐ ┌──────────────────┐ │
│ │ CRON │───>│ Read Vault │───>│ Profitable to │ │
│ │ Trigger │ │ State │ │ harvest? │ │
│ │ (5 min) │ │ (EVMClient) │ │ (time + yield) │ │
│ └──────────┘ └───────────────┘ └────────┬─────────┘ │
│ │ │
│ ┌──────────v──────────┐ │
│ │ Profitable? │ │
│ │ YES -> Write report│ │
│ │ NO -> Log & skip │ │
│ └──────────┬──────────┘ │
│ │ │
└────────────────────────────────────────────────┼────────────┘
┌──────────v──────────────┐
│ KeystoneForwarder │
│ -> Vault._processReport│
│ (harvest + compound) │
└─────────────────────────┘
```

## Components

### CRE Workflow (`my-workflow/`)

The TypeScript workflow runs off-chain inside CRE DON:

1. **Cron trigger** fires every 5 minutes (configurable)
2. **Reads** `shouldHarvest()`, `pendingYield()`, `totalHarvested()`, `harvestCount()` from the on-chain `VaultHarvester` contract
3. **If profitable**: sends a signed report to trigger harvest on-chain
4. **If not profitable**: logs and skips

### Consumer Contract (`contracts/evm/src/VaultHarvester.sol`)

A DeFi vault contract extending `ReceiverTemplate`:

- `shouldHarvest()` — view function that checks if `harvestInterval` has elapsed AND `pendingYield >= minYieldThreshold`
- `_processReport(bytes)` — called by CRE Forwarder, harvests pending yield, resets state, emits event
- `accrueYield(uint256)` — simulates yield accrual (in production, yield comes from the underlying strategy)
- `pendingYield`, `totalHarvested`, `harvestCount`, `lastHarvest` — public state variables

## Getting Started

A demo `VaultHarvester` contract is pre-deployed on Sepolia with simulated yield — this template works out of the box.

### Prerequisites

- [Bun](https://bun.sh/) runtime installed
- [CRE CLI](https://docs.chain.link/cre) installed

### 1. Install Dependencies

```bash
cd my-workflow && bun install && cd ..
cd contracts && bun install && cd ..
```

### 2. Run Tests

```bash
cd my-workflow && bun test
```

### 3. Simulate

```bash
# Dry run (no broadcast)
cre workflow simulate my-workflow --target staging-settings

# With actual on-chain transaction
cre workflow simulate my-workflow --target staging-settings --broadcast
```

### 4. Deploy Your Own Contract (Optional)

To use your own contract, deploy `contracts/evm/src/VaultHarvester.sol` to Sepolia using [Remix](https://remix.ethereum.org/) or Foundry. Constructor arguments:

- `forwarder`: CRE KeystoneForwarder address on Sepolia (`0x15fc6ae953e024d975e77382eeec56a9101f9f88`)
- `_harvestInterval`: Seconds between allowed harvests (e.g., `300` for 5 minutes)
- `_minYieldThreshold`: Minimum yield in wei to justify harvest (e.g., `1000000000000000000` for 1 token)

Then update the `contractAddress` in `my-workflow/config.staging.json` with your deployed address.

To simulate yield accrual on your contract:
```bash
cast send <YOUR_CONTRACT> "accrueYield(uint256)" 2000000000000000000 \
--private-key <YOUR_KEY> --rpc-url https://ethereum-sepolia-rpc.publicnode.com
```

## Customization

- **Change the schedule**: Edit `schedule` in `config.staging.json` (cron syntax)
- **Change the profitability check**: Modify `shouldHarvest()` in `VaultHarvester.sol`
- **Change the harvest logic**: Modify `_processReport()` to implement your vault's harvest + compound strategy

## Migration Guides

### Coming from Gelato?

This template replaces Gelato's vault harvesting Web3 Functions with CRE cron triggers. Gelato shut down Web3 Functions in March 2026.

| Gelato | CRE |
|--------|-----|
| `Web3Function.onRun()` | CRE cron callback (`onCronTrigger`) |
| `multiChainProvider` | `EVMClient` |
| `userArgs` | `config.json` |

### Coming from Chainlink Automation?

This replaces the Beefy/Yearn `checkUpkeep + performUpkeep` pattern. Off-chain profitability check means no wasted gas.

| Chainlink Automation | CRE |
|---------------------|-----|
| `checkUpkeep()` on-chain | `shouldHarvest()` read via `EVMClient` (off-chain) |
| `performUpkeep()` | `_processReport()` via KeystoneForwarder |
| LINK funding | No per-call LINK costs |

## Security

- The `VaultHarvester.sol` contract is a **demo** — audit and customize before production use
- The `ReceiverTemplate` base contract validates that only CRE Forwarder can call `onReport()`
- Never commit `.env` files or secrets

## License

MIT
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { parseAbi } from "viem"

export const VaultHarvesterAbi = parseAbi([
"function shouldHarvest() view returns (bool harvestNeeded)",
"function totalHarvested() view returns (uint256)",
"function lastHarvest() view returns (uint256)",
"function harvestCount() view returns (uint256)",
"function pendingYield() view returns (uint256)",
"function harvestInterval() view returns (uint256)",
"function minYieldThreshold() view returns (uint256)",
])
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './VaultHarvester'
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)

pragma solidity >=0.4.16;

/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(
bytes4 interfaceId
) external view returns (bool);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "./IERC165.sol";

/// @title IReceiver - receives keystone reports
/// @notice Implementations must support the IReceiver interface through ERC165.
interface IReceiver is IERC165 {
/// @notice Handles incoming keystone reports.
/// @dev If this function call reverts, it can be retried with a higher gas
/// limit. The receiver is responsible for discarding stale reports.
/// @param metadata Report's metadata.
/// @param report Workflow report.
function onReport(
bytes calldata metadata,
bytes calldata report
) external;
}
Loading
Loading