From 88afa44a3af825103be1daa7917d08e8cadc90e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 28 Sep 2026 13:23:15 -0300 Subject: [PATCH] feat(data-edge): add REO deployments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- packages/data-edge/README.md | 109 +++++++++++++++++++++++++-- packages/data-edge/addresses.json | 6 +- packages/data-edge/hardhat.config.ts | 1 + packages/data-edge/package.json | 2 +- packages/data-edge/tasks/deploy.ts | 3 +- 5 files changed, 110 insertions(+), 11 deletions(-) diff --git a/packages/data-edge/README.md b/packages/data-edge/README.md index 190052899..50e8b85b8 100644 --- a/packages/data-edge/README.md +++ b/packages/data-edge/README.md @@ -1,17 +1,112 @@ # Data Edge -A DataEdge contract is used to store arbitrary data on-chain on any EVM compatible blockchain. A subgraph can then read all the calldata sent to a particular contract, decode it and update the subgraph state accordingly. +A DataEdge contract stores arbitrary data on-chain on any EVM compatible blockchain. It does not execute anything: it exists so that off-chain consumers — typically a subgraph — can read the calldata sent to the contract, decode it, and update their own state accordingly. -The DataEdge accepts any function call by using a fallback function that will not revert. It is up to the implementer to define the calldata format as well as how to decode it. +Posting a payload is a plain transaction to the contract. The fallback function accepts any calldata and never reverts, so there is no ABI to conform to and no permissioning: **it is up to the implementer to define the calldata format and how to decode it.** -## Additional Considerations +## Contracts -- Fallback is not payable to avoid anyone sending ETH by mistake as the main purpose is to store calldata. +| Contract | Behaviour | +| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| [`DataEdge`](contracts/DataEdge.sol) | Fallback is a no-op. The payload lives only in the transaction calldata, so consumers must read transactions. | +| [`EventfulDataEdge`](contracts/EventfulDataEdge.sol) | Fallback emits `Log(bytes data)` with the full calldata. Costs more gas, but consumers can index events only. | -## Deploying +`EventfulDataEdge` is the one to pick unless you have a specific reason not to: subgraphs (and most indexing tooling) handle events far more comfortably than raw calldata, and some L2 RPCs make calldata retrieval awkward. -Setup a `.env` file with the keys you want to use for deployments. You can use `.env.sample` as a guide. -Deploy a `DataEdge` contract by running `pnpm deploy -- --network ` +### Payload convention + +The contracts impose no format, but the convention used across The Graph is the standard Solidity one, so tooling can treat a payload like a normal function call: + +``` +<4-byte selector of someMethodName(bytes)> || +``` + +Consumers switch on the selector to decide how to decode the rest. See `test/dataedge.test.ts` for a worked example. + +### Considerations + +- **The fallback is `payable`.** Neither contract has a withdrawal path, so any ETH sent to a DataEdge is permanently locked. Send value only if you intend to burn it. +- **Anyone can post.** There is no access control. A consumer must filter by sender (or otherwise authenticate the payload) if it cares about provenance. This is why deployments are per-use-case rather than shared — see the naming below. +- **Nothing is validated on-chain.** Malformed payloads are accepted and cost gas like any other; correctness is entirely a consumer-side concern. + +## Deployments + +Deployed addresses live in [`addresses.json`](addresses.json), keyed by chain ID. That file is the source of truth and is updated by the deploy task — it is not duplicated here. + +Each deployment serves a single use case and is named for it, because the contract accepts posts from anyone: a shared instance would force every consumer to filter other payloads out of its event stream. Keys are ``, e.g. `EBOEventfulDataEdge`. + +| Prefix | Use case | +| ------ | ---------------------------- | +| `EBO` | Epoch Block Oracle | +| `SAO` | Subgraph Availability Oracle | +| `REO` | Rewards Eligibility Oracle | + +Adding a use case means adding a prefix to the `DeployName` enum in [`tasks/deploy.ts`](tasks/deploy.ts); the task rejects anything not listed there. + +## Development + +### Setup + +```bash +pnpm install +cp .env.sample .env # then fill in the values you need +``` + +`.env` keys: `MNEMONIC` (deployer), `INFURA_KEY` (RPC for mainnet and sepolia; the Arbitrum networks use public endpoints), `ETHERSCAN_API_KEY` and `ARBISCAN_API_KEY` (verification). `ACCOUNT_INDEX` optionally selects a derivation index other than 0. + +### Common commands + +```bash +pnpm build # compile contracts and generate TypeChain types into build/ +pnpm test # build, then run the Hardhat test suite +pnpm test:gas # same, with a gas report written to reports/gas-report.log +pnpm test:coverage # solidity-coverage +pnpm lint # solhint + eslint + prettier + markdownlint +pnpm size # contract bytecode sizes +pnpm security # slither (requires slither-analyzer, installed by the script) +``` + +Supported networks are declared in [`hardhat.config.ts`](hardhat.config.ts): `mainnet`, `sepolia`, `arbitrum-one` and `arbitrum-sepolia`, plus the local `hardhat` and `ganache` networks. + +### Deploying + +Deployment is a Hardhat task. `--contract` takes `DataEdge` or `EventfulDataEdge`; `--deploy-name` takes one of the use-case prefixes above and becomes the prefix of the key written back to `addresses.json`. + +```bash +npx hardhat data-edge:deploy \ + --contract EventfulDataEdge \ + --deploy-name EBO \ + --network arbitrum-sepolia +``` + +There is a `deploy` script that wraps the same task, but invoke it as `pnpm run deploy -- --contract ...` — bare `pnpm deploy` is pnpm's own built-in command and will not run the script. + +On success the task appends `` (for example `EBOEventfulDataEdge`) to `addresses.json` under the network's chain ID — commit that change. + +Verify afterwards with: + +```bash +npx hardhat verify --network arbitrum-sepolia
+``` + +### Posting data + +Two tasks help build and submit payloads by hand: + +```bash +# Encode || abi.encode(data) and print the resulting calldata +npx hardhat data:craft \ + --edge \ + --selector setEpochBlocksPayload \ + --data 0xdeadbeef \ + --network arbitrum-sepolia + +# Send raw calldata to the edge contract +npx hardhat data:post \ + --edge \ + --data \ + --network arbitrum-sepolia +``` ## Copyright diff --git a/packages/data-edge/addresses.json b/packages/data-edge/addresses.json index 25bde2b4d..4aec36477 100644 --- a/packages/data-edge/addresses.json +++ b/packages/data-edge/addresses.json @@ -4,11 +4,13 @@ }, "42161": { "EBOEventfulDataEdge": "0x633bb9790d7c4c59991cebd377c0ed6501a35ebe", - "SAOEventfulDataEdge": "0xeD16cEbd4fa74a0016E1149cc03563Db4B223aec" + "SAOEventfulDataEdge": "0xeD16cEbd4fa74a0016E1149cc03563Db4B223aec", + "REOEventfulDataEdge": "0xfECf1535294095d032e072552D03f10fF2bEEa76" }, "421614": { "EBOEventfulDataEdge": "0x9b9402939133F27c6eba81a321dfBFa1feE6714E", - "SAOEventfulDataEdge": "0xB61AF143c79Cbdd68f179B657AaC86665CC2B469" + "SAOEventfulDataEdge": "0xB61AF143c79Cbdd68f179B657AaC86665CC2B469", + "REOEventfulDataEdge": "0x70212C326106e518e0AA12400664935293117D34" }, "11155111": { "EBOEventfulDataEdge": "0xEFC8D47673777b899f2FB597C6FC0E87ecce98Cb" diff --git a/packages/data-edge/hardhat.config.ts b/packages/data-edge/hardhat.config.ts index 6dee140d0..2e9bfab9d 100644 --- a/packages/data-edge/hardhat.config.ts +++ b/packages/data-edge/hardhat.config.ts @@ -7,6 +7,7 @@ import 'hardhat-gas-reporter' import 'hardhat-contract-sizer' import 'solidity-coverage' import 'hardhat-secure-accounts' +import 'dotenv/config' // Tasks import './tasks/craft-calldata' import './tasks/post-calldata' diff --git a/packages/data-edge/package.json b/packages/data-edge/package.json index 15b97d050..2f897fa01 100644 --- a/packages/data-edge/package.json +++ b/packages/data-edge/package.json @@ -25,7 +25,7 @@ "flatten": "scripts/flatten", "verify": "hardhat verify", "size": "hardhat size-contracts", - "deploy": "hardhat run scripts/deploy.ts" + "deploy": "hardhat data-edge:deploy" }, "files": [ "dist/**/*", diff --git a/packages/data-edge/tasks/deploy.ts b/packages/data-edge/tasks/deploy.ts index ca142b1e2..b3a0f7a44 100644 --- a/packages/data-edge/tasks/deploy.ts +++ b/packages/data-edge/tasks/deploy.ts @@ -11,11 +11,12 @@ enum Contract { enum DeployName { EBODataEdge = 'EBO', SAODataEdge = 'SAO', + REODataEdge = 'REO', } task('data-edge:deploy', 'Deploy a DataEdge contract') .addParam('contract', 'Chose DataEdge or EventfulDataEdge') - .addParam('deployName', 'Chose EBO or SAO') + .addParam('deployName', 'Chose EBO, SAO or REO') .setAction(async (taskArgs, hre) => { if (!Object.values(Contract).includes(taskArgs.contract)) { throw new Error(`Contract ${taskArgs.contract} not supported`)