A self-contained template for deploying and automating IOrionStrategist contracts against the Orion Finance protocol.
Clone this repo, fill in a .env, and you're ready to deploy or run automated rebalancing.
This template deploys the following strategists from protocol-plugins (sourced directly via npm - no local copies):
| Key | Contract | Strategy |
|---|---|---|
tvl |
KBestTvlWeightedAverage |
Top-K assets by TVL, weighted proportionally to TVL |
apy-equal |
KBestApyStrategist (EqualWeighted) |
Top-K assets by APY, equal weights |
apy-weighted |
KBestApyStrategist (ApyWeighted) |
Top-K assets by APY, weights ∝ APY |
- Node.js ≥ 24
- npm
git clone https://github.com/OrionFinanceAI/IOrionStrategist-template.git
cd IOrionStrategist-template
npm install
cp .env.example .env
# Edit .env with your RPC URLs, PRIVATE_KEY, and SEPOLIA_ORION_CONFIG_ADDRESS / MAINNET_ORION_CONFIG_ADDRESS
npm run compile# Deploy all three strategist variants
npx hardhat run scripts/deploy.ts --network sepolia
# Or via npm
npm run deploy:sepolia
npm run deploy:mainnet
# Deploy a single contract
DEPLOY_CONTRACTS=tvl npx hardhat run scripts/deploy.ts --network sepolia
# Deploy a subset
DEPLOY_CONTRACTS=apy-equal,apy-weighted npx hardhat run scripts/deploy.ts --network sepoliaA deployment summary is written to deployments/<network>-<timestamp>.json (gitignored).
| Variable | Required | Default | Description |
|---|---|---|---|
PRIVATE_KEY |
Yes | - | Deployer key; becomes strategist owner |
SEPOLIA_RPC_URL |
For Sepolia | - | Sepolia JSON-RPC endpoint |
MAINNET_RPC_URL |
For mainnet | - | Mainnet JSON-RPC endpoint |
SEPOLIA_ORION_CONFIG_ADDRESS |
Yes if --network is sepolia / hardhat / localhost |
- | OrionConfig contract |
MAINNET_ORION_CONFIG_ADDRESS |
Yes if --network mainnet |
- | OrionConfig contract |
VAULT_ADDRESS |
No | - | If set, vault manager (PRIVATE_KEY) calls updateStrategist() on this vault. Requires DEPLOY_CONTRACTS to name exactly one strategist |
STRATEGIST_K |
No | 10 |
Top-K assets to select (1–65535) |
DEPLOY_CONTRACTS |
No | tvl,apy-equal,apy-weighted |
Comma-separated contracts to deploy |
SKIP_VERIFY |
No | - | Set 1 to skip printing verify commands |
ETHERSCAN_API_KEY |
No | - | Needed to run hardhat verify after deployment |
{
"network": "sepolia",
"timestamp": "2026-05-21T12:00:00.000Z",
"deployer": "0x...",
"orionConfig": "0x...",
"k": 10,
"vault": null,
"tvl": "0x...",
"apy-equal": "0x...",
"apy-weighted": "0x..."
}Each strategist must be linked to exactly one transparent vault. setVault() on the strategist is only callable by the vault itself; the vault manager triggers that by calling updateStrategist(). Linking is one-time and irreversible on the strategist.
Automatic - set VAULT_ADDRESS and deploy exactly one contract (DEPLOY_CONTRACTS=tvl). The deployer (PRIVATE_KEY) must be that vault's manager. The script calls updateStrategist() on the vault.
Manual - call updateStrategist() on the vault as its manager:
const vault = await ethers.getContractAt("OrionTransparentVault", "0x<VAULT_ADDR>");
await vault.updateStrategist("0x<STRATEGIST_ADDR>");After deployment, the script prints the exact commands to verify each contract:
To verify on Etherscan, run:
npx hardhat verify --network sepolia 0x<TVL_ADDR> <owner> <config> <k>
...
Verification publishes the source code to Etherscan so anyone can audit the logic and interact with the contract directly via the Etherscan UI. Requires ETHERSCAN_API_KEY in .env.
Provide one vault address or a comma-separated list. The script reads the strategist() on each vault, verifies it implements IOrionStrategist (ERC-165), and calls submitIntent() as the PRIVATE_KEY signer (the strategist owner).
# Single vault
VAULT_ADDRESS=0xAAA... npx hardhat run scripts/update-intents.ts --network sepolia
# Multiple vaults
VAULT_ADDRESS=0xAAA...,0xBBB...,0xCCC... npx hardhat run scripts/update-intents.ts --network sepolia
# Or via npm
npm run update-intents:sepolia
npm run update-intents:mainnetDry-run (no transactions, all vaults still resolved and checked):
DRY_RUN=1 VAULT_ADDRESS=0xAAA...,0xBBB... npx hardhat run scripts/update-intents.ts --network sepoliaExit code is 1 on any failure - including vault read errors (e.g. strategist() reverts or the strategist fails the ERC-165 check) as well as failed submitIntent() calls - useful for cron/ECS alerting.
| Variable | Required | Default | Description |
|---|---|---|---|
PRIVATE_KEY |
Yes | - | Strategist owner key |
VAULT_ADDRESS |
Yes | - | Vault address or comma-separated list of vault addresses |
ORION_STRATEGIST_INTERFACE_ID |
No | - | Override ERC-165 check to a specific bytes4 |
DRY_RUN |
No | - | Set 1 to log only - no transactions sent |
# Preview the crontab line
bash infra/cron/setup.sh
# Or install it automatically (runs every 4 hours)
NETWORK=mainnet bash infra/cron/setup.sh --installLogs are written to logs/iorion-strategist-template.log in the repo root (see infra/cron/setup.sh).
-
Build and push the image (tag with the commit SHA - CI uses the same strategy)
GIT_SHA=$(git rev-parse --short HEAD) docker build -f infra/ecs/Dockerfile -t <ECR_REPO_URI>:${GIT_SHA} . docker push <ECR_REPO_URI>:${GIT_SHA}
-
Store secrets in AWS Secrets Manager
aws secretsmanager create-secret --name orion/PRIVATE_KEY --secret-string '0x...' aws secretsmanager create-secret --name orion/MAINNET_RPC_URL --secret-string 'https://...' aws secretsmanager create-secret --name orion/VAULT_ADDRESS --secret-string '0xAAA...,0xBBB...' aws secretsmanager create-secret --name orion/MAINNET_ORION_CONFIG_ADDRESS --secret-string '0x...'
-
Register the task - fill in the
<PLACEHOLDERS>ininfra/ecs/task-definition.json, then:aws ecs register-task-definition --cli-input-json file://infra/ecs/task-definition.json
-
Schedule with EventBridge - see the
_commentblock ininfra/ecs/task-definition.jsonfor the fullaws events put-rulecommand. The default schedule is every 4 hours (cron(0 */4 * * ? *)).
Contract source lives in the public OrionFinanceAI/protocol-plugins repo. The package is pinned to a git SHA in package.json. npm update will not move that pin - change the SHA, then reinstall:
# Edit the @orion-finance/plugins commit SHA in package.json, then:
npm install
npm run compileTo deploy your own strategist, implement IOrionStrategist:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.34;
import "@orion-finance/protocol/contracts/interfaces/IOrionStrategist.sol";
import "@orion-finance/protocol/contracts/interfaces/IOrionTransparentVault.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
contract MyStrategist is IOrionStrategist, ERC165 {
address private _vault;
function setVault(address vault_) external {
require(_vault == address(0), "already linked");
require(msg.sender == vault_, "not authorized");
_vault = vault_;
}
function submitIntent() external {
IOrionTransparentVault.IntentPosition[] memory intent = _buildIntent();
IOrionTransparentVault(_vault).submitIntent(intent);
}
function supportsInterface(bytes4 id) public view override(ERC165, IERC165) returns (bool) {
return id == type(IOrionStrategist).interfaceId || super.supportsInterface(id);
}
function _buildIntent() internal view returns (IOrionTransparentVault.IntentPosition[] memory) {
// your allocation logic here
}
}-
Place the contract under
contracts/in this repo. -
Add
contracts/as a source inhardhat.config.ts:paths: { sources: "./contracts" }
-
Add a deploy step in
scripts/deploy.ts.