Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7fff2cd
add the KMS connector module
sparrowDom Mar 23, 2026
987c2ae
Merge remote-tracking branch 'origin/master' into sparrowDom/automate…
sparrowDom Mar 25, 2026
9e0eea5
ai review/plan
apexearth Mar 26, 2026
35c1b9a
supply cron configuration as a json file
sparrowDom Mar 26, 2026
0bca2c0
trigger cron-jobs via http request. Query the job result via HTTP
sparrowDom Mar 26, 2026
9db5c23
move actions
apexearth Mar 27, 2026
d1bb61a
convert to ts
apexearth Mar 27, 2026
9e68ee3
Merge branch 'sparrowDom/automated-actions-lite' of https://github.co…
apexearth Mar 27, 2026
cb262f1
rename/upgrade ts
apexearth Mar 27, 2026
6858bbd
make github build the image instead of docker
sparrowDom Mar 27, 2026
adcb500
fix node issue
sparrowDom Mar 27, 2026
80f660a
rename image
sparrowDom Mar 27, 2026
0371df6
another rename
sparrowDom Mar 27, 2026
9c91e74
fix naming mistakes
sparrowDom Mar 27, 2026
79075e5
Migrate Defender actions to TypeScript hardhat tasks
apexearth Mar 27, 2026
f52fbd3
test loki execution and ensure flush
apexearth Mar 27, 2026
c728f99
prettier
apexearth Mar 27, 2026
f42b623
fix prettier/lint conflict
apexearth Mar 27, 2026
e79b58d
Merge branch 'sparrowDom/automated-actions-lite-chris2' into sparrowD…
apexearth Mar 27, 2026
868b1ec
tweak how we log
apexearth Mar 28, 2026
9ae2bfc
create docker-compose env for local execution
apexearth Mar 28, 2026
55ccb19
add cron readme
apexearth Mar 28, 2026
baeeafb
lint fix
apexearth Mar 28, 2026
ee58e99
path fix
apexearth Mar 28, 2026
5db6014
fix output
apexearth Mar 28, 2026
a3c6ec1
healthcheck fix
apexearth Apr 1, 2026
cd81063
extract actions out of defender
apexearth Apr 1, 2026
243dba7
convert files to ts
apexearth Apr 2, 2026
22d77a4
expose kms signer
sparrowDom Apr 2, 2026
6a25d18
Merge remote-tracking branch 'origin/master' into sparrowDom/automate…
sparrowDom Apr 2, 2026
b6c9fb0
unify names
sparrowDom Apr 2, 2026
eadaae4
add support to base for handling bribes
sparrowDom Apr 2, 2026
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
42 changes: 42 additions & 0 deletions .github/workflows/contracts-automaton-prod-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Contracts Actions Image

on:
push:
branches:
- origin-automaton-production
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Prepare image metadata
id: prep
run: |
IMAGE_NAME="ghcr.io/${{ github.repository_owner }}/contracts-automaton-prod"
IMAGE_NAME="$(echo "${IMAGE_NAME}" | tr '[:upper:]' '[:lower:]')"
echo "image_name=${IMAGE_NAME}" >> "${GITHUB_OUTPUT}"

- name: Build and push image
uses: docker/build-push-action@v5
with:
context: ./contracts
file: ./contracts/dockerfile-actions
push: true
tags: |
${{ steps.prep.outputs.image_name }}:latest
${{ steps.prep.outputs.image_name }}:${{ github.sha }}
21 changes: 21 additions & 0 deletions contracts/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,25 @@ module.exports = {
"no-only-tests/no-only-tests": "error",
"no-unused-vars": [2, { vars: "all", args: "after-used" }],
},
overrides: [
{
files: ["**/*.ts"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./tsconfig.json",
sourceType: "module",
},
plugins: ["@typescript-eslint"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
2,
{ vars: "all", args: "after-used" },
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-require-imports": "off",
},
},
],
};
23 changes: 21 additions & 2 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,9 @@ If enabled, the gas usage will be output in a table after the tests have execute
When using Hardhat tasks, there are a few options for specifying the wallet to send transactions from.

1. Primary key
2. Impersonate
3. Defender Relayer
2. AWS KMS signer
3. Impersonate
4. Defender Relayer

### Primary Key

Expand All @@ -262,6 +263,24 @@ unset DEPLOYER_PK
unset GOVERNOR_PK
```

### AWS KMS Signer

Hardhat tasks can sign transactions with AWS KMS when both `AWS_ACCESS_KEY_ID` and
`AWS_SECRET_ACCESS_KEY` are set.

The default `relayer-id` is `origin-relayer-production-evm`. Some tasks can be mapped
to different defaults in code, and a user-provided task parameter always wins:

```
npx hardhat <task> --network <network> --relayer-id <kms-key-id-or-alias>
```

The relayer resolution precedence is:

1. `--relayer-id`
2. task-name based override map
3. global default (`origin-relayer-production-evm`)

### Impersonate

If using a fork test or node, you can impersonate any externally owned account or contract. Export `IMPERSONATE` with the address of the account you want to impersonate. The account will be funded with some Ether. For example
Expand Down
1 change: 1 addition & 0 deletions contracts/cron/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cronjob
68 changes: 68 additions & 0 deletions contracts/cron/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Cron Actions

Containerized scheduler for running hardhat tasks on a schedule. Replaces OpenZeppelin Defender actions.

## How it works

1. **`cron-jobs.json`** — Defines all scheduled jobs (name, cron schedule, command, enabled flag)
2. **`render-crontab.ts`** — Reads `cron-jobs.json`, filters to enabled jobs, writes a supercronic-compatible crontab file
3. **`cron-supervisor.ts`** — Starts supercronic with the generated crontab, runs an HTTP API for triggering actions on-demand and checking run status
4. **`cron-entrypoint.sh`** — Docker entrypoint that boots the supervisor

Each job runs a hardhat task (e.g. `pnpm hardhat healthcheck`). Signing uses the existing KMS signer from `utils/signers.js` when `AWS_ACCESS_KEY_ID` is set.

## Running locally

```bash
cd contracts
docker compose up actions
```

## API

The supervisor exposes an HTTP API (default port 8080):

```bash
# Health check (no auth)
curl http://localhost:8080/healthz

# List all actions (requires bearer token)
curl -H 'Authorization: Bearer $ACTION_API_BEARER_TOKEN' \
http://localhost:8080/api/v1/actions

# Trigger an action
curl -X POST -H 'Authorization: Bearer $ACTION_API_BEARER_TOKEN' \
http://localhost:8080/api/v1/actions/healthcheck/runs

# Check run status
curl -H 'Authorization: Bearer $ACTION_API_BEARER_TOKEN' \
http://localhost:8080/api/v1/runs/<runId>
```

## Adding a new job

Add an entry to `cron-jobs.json`:

```json
{
"name": "my_new_job",
"schedule": "0 */6 * * *",
"enabled": true,
"command": "cd /app && pnpm hardhat myTask --network mainnet"
}
```

Set `enabled: false` to define a job that can only be triggered via the API.

## Environment variables

| Variable | Description |
|----------|-------------|
| `ACTION_API_BEARER_TOKEN` | Required. Auth token for the HTTP API |
| `PROVIDER_URL` | Mainnet RPC endpoint |
| `HARDHAT_NETWORK` | Default network for tasks |
| `LOKI_URL` | Grafana Loki push endpoint (optional) |
| `LOKI_USER` | Loki basic auth user (optional) |
| `LOKI_API_KEY` | Loki basic auth key (optional) |
| `AWS_ACCESS_KEY_ID` | For KMS signer (optional) |
| `AWS_SECRET_ACCESS_KEY` | For KMS signer (optional) |
21 changes: 21 additions & 0 deletions contracts/cron/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# TODO List

## Defender Actions which use compiled code:

https://defender.openzeppelin.com/#/actions/automatic/076c59e4-4150-42c7-9ba0-9962069ac353
https://defender.openzeppelin.com/#/actions/automatic/0b852456-96a0-4f1d-9d6c-39e1c6ae9dfc
https://defender.openzeppelin.com/#/actions/automatic/65b53496-e426-4850-8349-059e63eb2120
https://defender.openzeppelin.com/#/actions/automatic/65f04f74-8da7-4fc5-94b3-96be31bac03b
https://defender.openzeppelin.com/#/actions/automatic/6a633bb0-aff8-4b37-aaae-b4c6f244ed87
https://defender.openzeppelin.com/#/actions/automatic/6e4f764d-4126-45a5-b7d9-1ab90cd3ffd6
https://defender.openzeppelin.com/#/actions/automatic/84988850-6816-4074-8e7b-c11cb2b32e7e
https://defender.openzeppelin.com/#/actions/automatic/a4f8ca5f-7144-469b-b84a-58b30fed72ce
https://defender.openzeppelin.com/#/actions/automatic/aa194c13-0dbf-49d2-8e87-70e61f3d71a8
https://defender.openzeppelin.com/#/actions/automatic/b1d831f1-29d4-4943-bb2e-8e625b76e82c
https://defender.openzeppelin.com/#/actions/automatic/bb43e5da-f936-4185-84da-253394583665
https://defender.openzeppelin.com/#/actions/automatic/e2929f53-db56-49b2-b054-35f7df7fc4fb
https://defender.openzeppelin.com/#/actions/automatic/e571409b-5399-48e4-bfb2-50b7af9903aa
https://defender.openzeppelin.com/#/actions/automatic/f74f24b4-d98b-4181-89cc-6608369b6f91
https://defender.openzeppelin.com/#/actions/automatic/f92ea662-fc34-433b-8beb-b34e9ab74685

- [ ] Check hyper EVM network jobs for correct network usage
4 changes: 4 additions & 0 deletions contracts/cron/cron-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -eu

exec node -r ts-node/register /app/cron/cron-supervisor.ts
124 changes: 124 additions & 0 deletions contracts/cron/cron-jobs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
{
"jobs": [
{
"name": "healthcheck",
"schedule": "*/5 * * * *",
"enabled": true,
"command": "cd /app && pnpm hardhat healthcheck --network ${HARDHAT_NETWORK:-mainnet}"
},
{
"name": "hourly_snap_balances",
"schedule": "0 * * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat snapBalances --network ${HARDHAT_NETWORK:-mainnet}"
},
{
"name": "hourly_verify_balances",
"schedule": "8 * * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat verifyBalances --network ${HARDHAT_NETWORK:-mainnet}"
},
{
"name": "hourly_verify_deposits",
"schedule": "10 * * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat verifyDeposits --network ${HARDHAT_NETWORK:-mainnet}"
},
{
"name": "hourly_auto_validator_deposits",
"schedule": "12 * * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat autoValidatorDeposits --network ${HARDHAT_NETWORK:-mainnet}"
},
{
"name": "hourly_auto_validator_withdrawals",
"schedule": "14 * * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat autoValidatorWithdrawals --network ${HARDHAT_NETWORK:-mainnet}"
},
{
"name": "daily_rebase_mainnet_oeth",
"schedule": "0 0 * * *",
"enabled": true,
"command": "cd /app && pnpm hardhat rebase --network mainnet --symbol OETH"
},
{
"name": "daily_rebase_mainnet_ousd",
"schedule": "10 0 * * *",
"enabled": true,
"command": "cd /app && pnpm hardhat rebase --network mainnet --symbol OUSD"
},
{
"name": "daily_rebase_base_oeth",
"schedule": "20 0 * * *",
"enabled": true,
"command": "cd /app && pnpm hardhat rebase --network base --symbol OETH"
},
{
"name": "otoken_os_collectAndRelease",
"schedule": "50 23 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat otoken-os-collectAndRelease --network sonic"
},
{
"name": "otoken_ousd_autoWithdrawal",
"schedule": "40 11,23 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat otoken-ousd-autoWithdrawal --network mainnet"
},
{
"name": "otoken_oethb_updateWoethPrice",
"schedule": "10 21 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat otoken-oethb-updateWoethPrice --network mainnet"
},
{
"name": "otoken_oethp_addWithdrawalQueueLiquidity",
"schedule": "5 0 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat otoken-oethp-addWithdrawalQueueLiquidity --network mainnet"
},
{
"name": "otoken_oethb_rebase",
"schedule": "25 9,21 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat otoken-oethb-rebase --network base"
},
{
"name": "otoken_os_sonicRestakeRewards",
"schedule": "55 23 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat otoken-os-sonicRestakeRewards --network sonic"
},
{
"name": "crossChainBalanceUpdate-base",
"schedule": "15 7,15,23 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat crossChainBalanceUpdate-base --network base"
},
{
"name": "crossChainBalanceUpdate-hyperevm",
"schedule": "15 7,15,23 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat hyperevm-crossChainBalanceUpdate --network hyperevm"
},
{
"name": "otoken_ousd_oeth_rebase",
"schedule": "50 11,23 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat otoken_ousd_oeth_rebase --network mainnet"
},
{
"name": "ogn_claimAndForwardRewards",
"schedule": "0 0 * * 2",
"enabled": false,
"command": "cd /app && pnpm hardhat ogn-claimAndForwardRewards --network mainnet"
},
{
"name": "otoken_oethb_harvest",
"schedule": "50 11 * * *",
"enabled": false,
"command": "cd /app && pnpm hardhat otoken-oethb-harvest --network base"
}
]
}
Loading
Loading